index.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import baseComponent from '../helpers/baseComponent'
  2. import fieldBehavior from '../helpers/mixins/fieldBehavior'
  3. import classNames from '../helpers/libs/classNames'
  4. import warning from '../helpers/libs/warning'
  5. import { nextTick } from '../helpers/hooks/useNativeAPI'
  6. import { getDefaultContext } from '../helpers/shared/getDefaultContext'
  7. import { useRect } from '../helpers/hooks/useDOM'
  8. import { props as formProps } from '../form/props'
  9. import { props } from './props'
  10. const defaultContext = getDefaultContext(formProps, [
  11. 'layout',
  12. 'validateMessages',
  13. 'requiredMarkStyle',
  14. 'asteriskText',
  15. 'requiredText',
  16. 'optionalText',
  17. 'disabled',
  18. 'readOnly',
  19. ])
  20. const children = [
  21. 'picker',
  22. 'date-picker',
  23. 'popup-select',
  24. 'radio-group',
  25. 'checkbox-group',
  26. 'selectable',
  27. 'selector-group',
  28. 'switch',
  29. 'input',
  30. 'input-number',
  31. 'rater',
  32. 'slider',
  33. 'textarea',
  34. ]
  35. const relations = children.map((name) => `../${name}/index`).reduce((acc, name) => {
  36. return {
  37. ...acc,
  38. [name]: {
  39. type: 'descendant',
  40. observer: function() {
  41. this.callDebounceFn(this.changeValue)
  42. },
  43. },
  44. }
  45. }, {})
  46. baseComponent({
  47. useField: true,
  48. useExport: true,
  49. behaviors: [fieldBehavior],
  50. relations: {
  51. '../form/index': {
  52. type: 'ancestor',
  53. },
  54. ...relations,
  55. },
  56. properties: props,
  57. data: {
  58. index: 0,
  59. isLast: false,
  60. context: defaultContext,
  61. popoverVisible: false,
  62. slotRect: null,
  63. relativeRect: null,
  64. },
  65. observers: {
  66. initialValue(initialValue) {
  67. this.changeValue(initialValue)
  68. },
  69. ['valuePropName, valueNameFromEvent, trigger, validate, validateTrigger, preserve, rules, validateFirst, hidden']() {
  70. this.changeValue()
  71. },
  72. },
  73. computed: {
  74. classes: ['prefixCls, childElementPosition, labelWrap', function(prefixCls, childElementPosition, labelWrap) {
  75. const wrap = classNames(prefixCls)
  76. const child = classNames(`${prefixCls}__child`, {
  77. [`${prefixCls}__child--position-${childElementPosition}`]: childElementPosition,
  78. })
  79. const label = classNames(`${prefixCls}__label`, {
  80. [`${prefixCls}__label--wrap`]: labelWrap,
  81. })
  82. const extra = `${prefixCls}__extra`
  83. const arrow = `${prefixCls}__arrow`
  84. const asterisk = `${prefixCls}__required-asterisk`
  85. const text = `${prefixCls}__required-text`
  86. const feedback = `${prefixCls}__feedback-message`
  87. const labelHelp = `${prefixCls}__label-help`
  88. return {
  89. wrap,
  90. child,
  91. label,
  92. extra,
  93. arrow,
  94. asterisk,
  95. text,
  96. feedback,
  97. labelHelp,
  98. }
  99. }],
  100. },
  101. methods: {
  102. /**
  103. * 获取父节点
  104. */
  105. getFormContext() {
  106. return this.getRelationsByName('../form/index')[0]
  107. },
  108. /**
  109. * 获取子节点
  110. */
  111. getChildNodes() {
  112. return this.getRelationsByType('descendant')
  113. },
  114. changeContext(index = 0, isLast = false, context = defaultContext) {
  115. this.setData({
  116. index,
  117. isLast,
  118. context,
  119. })
  120. },
  121. setPopoverVisible() {
  122. const popoverVisible = !this.data.popoverVisible
  123. const promise = popoverVisible ? this.getPopoverRects() : Promise.resolve([])
  124. promise.then(([slotRect, relativeRect]) => {
  125. if (slotRect && relativeRect) {
  126. this.setData({
  127. slotRect,
  128. relativeRect,
  129. popoverVisible,
  130. })
  131. } else {
  132. this.setData({
  133. popoverVisible,
  134. })
  135. }
  136. })
  137. },
  138. getPopoverRects() {
  139. const { prefixCls } = this.data
  140. const getSlotRect = () => useRect(`.${prefixCls}__label-help`, this)
  141. const getRelativeRect = () => new Promise((resolve) => {
  142. const wuxCell = this.querySelector('#wux-cell')
  143. if (wuxCell) {
  144. wuxCell.getBoundingClientRect((_height, rect) => {
  145. resolve(rect)
  146. })
  147. }
  148. })
  149. return Promise.all([getSlotRect(), getRelativeRect()])
  150. },
  151. /**
  152. * 更新 value 值,同步到子元素
  153. */
  154. changeValue(value = this.data.value) {
  155. // set value
  156. if (this.data.value !== value) {
  157. this.setData({ value })
  158. }
  159. const elements = this.getChildNodes()
  160. const registerInputElem = (valuePropName, value, inputElem) => {
  161. const nextProps = {
  162. hasFieldDecorator: true,
  163. }
  164. if (inputElem.data[valuePropName] !== value) {
  165. nextProps[valuePropName] = value
  166. }
  167. inputElem.setData({
  168. ...nextProps,
  169. })
  170. nextTick(() => {
  171. this.forceUpdate(inputElem)
  172. })
  173. }
  174. if (elements.length > 0) {
  175. elements.forEach((inputElem) => {
  176. Object.defineProperty(inputElem, 'hasFieldDecorator', {
  177. value: true,
  178. enumerable: false,
  179. writable: true,
  180. configurable: true,
  181. })
  182. registerInputElem(this.data.valuePropName, value, inputElem)
  183. })
  184. }
  185. },
  186. /**
  187. * 强制更新元素
  188. */
  189. forceUpdate(inputElem, callback) {
  190. const formContext = this.getFormContext()
  191. if (!formContext) {
  192. warning(
  193. false,
  194. `Field<${this.data.name}> instance is not connected to any Form element.Forgot to use <wux-form />?`
  195. )
  196. return
  197. }
  198. const { getFieldDecorator } = formContext.getInternalHooks('FORM_HOOK_MARK')
  199. const fieldData = this.data
  200. const { name } = fieldData
  201. const inputProps = getFieldDecorator(name, fieldData, this)(inputElem)
  202. const finallyCb = () => {
  203. if (callback) {
  204. callback()
  205. }
  206. // field reRender
  207. this.reRender(this.data)
  208. }
  209. inputElem.setData(inputProps, finallyCb)
  210. },
  211. expose() {
  212. return {
  213. changeContext: this.changeContext.bind(this),
  214. changeValue: this.changeValue.bind(this),
  215. forceUpdate: this.forceUpdate.bind(this),
  216. }
  217. },
  218. },
  219. ready() {
  220. const formContext = this.getFormContext()
  221. if (formContext) {
  222. const { registerField } = formContext.getInternalHooks('FORM_HOOK_MARK')
  223. const { name } = this.data
  224. this.cancelRegister = registerField(name, this)
  225. }
  226. },
  227. detached() {
  228. if (this.cancelRegister) {
  229. this.cancelRegister()
  230. this.cancelRegister = null
  231. }
  232. },
  233. })