@king-design/intact 3.6.0-beta.0 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/components/button/demos/basic.md +0 -1
  2. package/components/cascader/index.spec.ts +7 -6
  3. package/components/copy/index.spec.ts +9 -14
  4. package/components/datepicker/basepicker.ts +26 -314
  5. package/components/datepicker/calendar.ts +3 -1
  6. package/components/datepicker/calendar.vdt +5 -2
  7. package/components/datepicker/dayjs.ts +2 -16
  8. package/components/datepicker/demos/multiple.md +5 -0
  9. package/components/datepicker/demos/yearMonth.md +2 -8
  10. package/components/datepicker/helpers.ts +5 -7
  11. package/components/datepicker/index.md +1 -2
  12. package/components/datepicker/index.spec.ts +596 -157
  13. package/components/datepicker/index.ts +16 -33
  14. package/components/datepicker/index.vdt +41 -35
  15. package/components/datepicker/shortcuts.ts +1 -1
  16. package/components/datepicker/styles.ts +27 -18
  17. package/components/datepicker/useConfirm.ts +82 -0
  18. package/components/datepicker/useDisabled.ts +29 -31
  19. package/components/datepicker/useFormats.ts +8 -4
  20. package/components/datepicker/useHighlight.ts +81 -0
  21. package/components/datepicker/useKeyboards.ts +2 -1
  22. package/components/datepicker/useMergeRange.ts +12 -12
  23. package/components/datepicker/useMonths.ts +6 -3
  24. package/components/datepicker/usePanel.ts +19 -19
  25. package/components/datepicker/useShowDate.ts +21 -41
  26. package/components/datepicker/useStatus.ts +34 -15
  27. package/components/datepicker/useValue.ts +43 -72
  28. package/components/datepicker/useValueBase.ts +312 -0
  29. package/components/datepicker/useWeeks.ts +1 -1
  30. package/components/datepicker/useYears.ts +7 -3
  31. package/components/dropdown/dropdown.ts +5 -4
  32. package/components/dropdown/index.md +1 -0
  33. package/components/dropdown/item.ts +1 -1
  34. package/components/dropdown/useKeyboard.ts +0 -1
  35. package/components/form/form.ts +4 -0
  36. package/components/form/index.md +2 -1
  37. package/components/form/index.spec.ts +2 -0
  38. package/components/input/index.spec.ts +42 -0
  39. package/components/input/index.ts +8 -0
  40. package/components/input/index.vdt +3 -4
  41. package/components/input/useAutoWidth.ts +19 -1
  42. package/components/menu/demos/horizontal.md +7 -1
  43. package/components/menu/index.spec.ts +19 -0
  44. package/components/menu/styles.ts +2 -1
  45. package/components/scrollSelect/useMouseEvents.ts +5 -4
  46. package/components/select/base.ts +3 -2
  47. package/components/select/base.vdt +2 -1
  48. package/components/select/demos/creatable.md +2 -2
  49. package/components/select/index.md +1 -1
  50. package/components/select/index.spec.ts +142 -36
  51. package/components/select/menu.ts +1 -1
  52. package/components/select/option.ts +2 -1
  53. package/components/select/select.ts +1 -0
  54. package/components/select/styles.ts +3 -1
  55. package/components/select/useCard.ts +22 -4
  56. package/components/select/useInput.ts +5 -9
  57. package/components/spinner/index.spec.ts +18 -0
  58. package/components/spinner/useValue.ts +2 -1
  59. package/components/table/index.spec.ts +69 -1
  60. package/components/table/useStickyHeader.ts +1 -1
  61. package/components/timepicker/index.spec.ts +145 -27
  62. package/components/timepicker/panelPicker.ts +10 -4
  63. package/components/timepicker/panelPicker.vdt +3 -5
  64. package/components/timepicker/styles.ts +1 -0
  65. package/components/timepicker/useConfirm.ts +33 -0
  66. package/components/timepicker/useDefaultValue.ts +30 -0
  67. package/components/timepicker/useDisabled.ts +17 -4
  68. package/components/timepicker/useFormats.ts +1 -1
  69. package/components/timepicker/useValue.ts +22 -19
  70. package/components/tour/index.spec.ts +1 -1
  71. package/es/components/cascader/index.spec.js +18 -19
  72. package/es/components/copy/index.spec.js +14 -31
  73. package/es/components/datepicker/basepicker.d.ts +6 -27
  74. package/es/components/datepicker/basepicker.js +23 -268
  75. package/es/components/datepicker/calendar.d.ts +4 -2
  76. package/es/components/datepicker/dayjs.d.ts +2 -13
  77. package/es/components/datepicker/helpers.d.ts +3 -2
  78. package/es/components/datepicker/helpers.js +2 -3
  79. package/es/components/datepicker/index.d.ts +21 -29
  80. package/es/components/datepicker/index.js +22 -32
  81. package/es/components/datepicker/index.spec.js +1277 -484
  82. package/es/components/datepicker/index.vdt.js +39 -38
  83. package/es/components/datepicker/shortcuts.d.ts +1 -1
  84. package/es/components/datepicker/styles.d.ts +7 -2
  85. package/es/components/datepicker/styles.js +10 -15
  86. package/es/components/datepicker/useConfirm.d.ts +6 -0
  87. package/es/components/datepicker/useConfirm.js +65 -0
  88. package/es/components/datepicker/useDisabled.d.ts +5 -3
  89. package/es/components/datepicker/useDisabled.js +22 -27
  90. package/es/components/datepicker/useFormats.d.ts +2 -2
  91. package/es/components/datepicker/useFormats.js +6 -2
  92. package/es/components/datepicker/useHighlight.d.ts +14 -0
  93. package/es/components/datepicker/useHighlight.js +60 -0
  94. package/es/components/datepicker/useKeyboards.js +2 -1
  95. package/es/components/datepicker/useMergeRange.d.ts +1 -1
  96. package/es/components/datepicker/useMergeRange.js +11 -16
  97. package/es/components/datepicker/useMonths.js +5 -3
  98. package/es/components/datepicker/usePanel.d.ts +1 -10
  99. package/es/components/datepicker/usePanel.js +19 -32
  100. package/es/components/datepicker/useShowDate.d.ts +1 -1
  101. package/es/components/datepicker/useShowDate.js +15 -40
  102. package/es/components/datepicker/useStatus.js +33 -16
  103. package/es/components/datepicker/useValue.d.ts +11 -6
  104. package/es/components/datepicker/useValue.js +49 -69
  105. package/es/components/datepicker/useValueBase.d.ts +28 -0
  106. package/es/components/datepicker/useValueBase.js +280 -0
  107. package/es/components/datepicker/useYears.js +6 -3
  108. package/es/components/dropdown/dropdown.d.ts +1 -0
  109. package/es/components/dropdown/dropdown.js +7 -4
  110. package/es/components/form/form.d.ts +1 -0
  111. package/es/components/form/form.js +7 -0
  112. package/es/components/form/index.spec.js +10 -8
  113. package/es/components/input/index.d.ts +2 -0
  114. package/es/components/input/index.js +6 -0
  115. package/es/components/input/index.spec.js +45 -0
  116. package/es/components/input/index.vdt.js +4 -3
  117. package/es/components/input/useAutoWidth.d.ts +2 -0
  118. package/es/components/input/useAutoWidth.js +19 -1
  119. package/es/components/menu/index.spec.js +28 -0
  120. package/es/components/menu/styles.js +2 -2
  121. package/es/components/scrollSelect/useMouseEvents.js +5 -4
  122. package/es/components/select/base.d.ts +1 -1
  123. package/es/components/select/base.js +3 -2
  124. package/es/components/select/base.vdt.js +4 -3
  125. package/es/components/select/index.spec.js +346 -218
  126. package/es/components/select/menu.js +1 -1
  127. package/es/components/select/option.js +2 -1
  128. package/es/components/select/select.js +2 -1
  129. package/es/components/select/styles.d.ts +79 -0
  130. package/es/components/select/styles.js +1 -0
  131. package/es/components/select/useCard.d.ts +4 -3
  132. package/es/components/select/useCard.js +15 -4
  133. package/es/components/select/useInput.d.ts +1 -1
  134. package/es/components/select/useInput.js +4 -4
  135. package/es/components/spinner/index.spec.js +82 -44
  136. package/es/components/spinner/useValue.js +2 -1
  137. package/es/components/table/index.spec.js +84 -6
  138. package/es/components/table/useStickyHeader.js +1 -1
  139. package/es/components/timepicker/index.spec.js +298 -128
  140. package/es/components/timepicker/panelPicker.d.ts +21 -16
  141. package/es/components/timepicker/panelPicker.js +7 -4
  142. package/es/components/timepicker/panelPicker.vdt.js +5 -9
  143. package/es/components/timepicker/selectPicker.d.ts +4 -3
  144. package/es/components/timepicker/styles.js +1 -1
  145. package/es/components/timepicker/useConfirm.d.ts +6 -0
  146. package/es/components/timepicker/useConfirm.js +19 -0
  147. package/es/components/timepicker/useDefaultValue.d.ts +4 -0
  148. package/es/components/timepicker/useDefaultValue.js +27 -0
  149. package/es/components/timepicker/useDisabled.d.ts +6 -3
  150. package/es/components/timepicker/useDisabled.js +13 -4
  151. package/es/components/timepicker/useFormats.d.ts +1 -1
  152. package/es/components/timepicker/useValue.d.ts +13 -8
  153. package/es/components/timepicker/useValue.js +14 -15
  154. package/es/components/tour/index.spec.js +1 -1
  155. package/es/index.d.ts +2 -2
  156. package/es/index.js +2 -2
  157. package/es/site/data/components/button/demos/basic/react.js +0 -2
  158. package/es/site/data/components/datepicker/demos/multiple/index.d.ts +1 -0
  159. package/es/site/data/components/datepicker/demos/multiple/index.js +2 -1
  160. package/es/site/data/components/datepicker/demos/multiple/react.d.ts +1 -0
  161. package/es/site/data/components/datepicker/demos/multiple/react.js +13 -2
  162. package/es/site/data/components/datepicker/demos/yearMonth/index.d.ts +0 -2
  163. package/es/site/data/components/datepicker/demos/yearMonth/index.js +1 -3
  164. package/es/site/data/components/datepicker/demos/yearMonth/react.d.ts +0 -2
  165. package/es/site/data/components/datepicker/demos/yearMonth/react.js +1 -21
  166. package/es/site/data/components/menu/demos/horizontal/react.js +5 -1
  167. package/es/site/data/components/select/demos/creatable/react.js +2 -2
  168. package/es/site/data/components/select/demos/searchable/index.js +1 -1
  169. package/es/site/data/components/select/demos/searchable/react.js +1 -1
  170. package/es/site/data/components/tour/demos/customText/index.d.ts +19 -6
  171. package/es/site/data/components/tour/demos/customText/index.js +18 -17
  172. package/es/site/data/components/tour/demos/customText/react.d.ts +20 -6
  173. package/es/site/data/components/tour/demos/customText/react.js +31 -27
  174. package/es/test/demos.js +1 -1
  175. package/index.ts +2 -2
  176. package/package.json +2 -2
  177. package/components/datepicker/demos/nowrap.md +0 -35
  178. package/components/datepicker/usePosition.ts +0 -169
  179. package/es/components/datepicker/usePosition.d.ts +0 -10
  180. package/es/components/datepicker/usePosition.js +0 -166
  181. package/es/site/data/components/datepicker/demos/nowrap/index.d.ts +0 -10
  182. package/es/site/data/components/datepicker/demos/nowrap/index.js +0 -19
  183. package/es/site/data/components/datepicker/demos/nowrap/react.d.ts +0 -10
  184. package/es/site/data/components/datepicker/demos/nowrap/react.js +0 -49
  185. package/es/site/data/components/tour/demos/customButtons/index.d.ts +0 -33
  186. package/es/site/data/components/tour/demos/customButtons/index.js +0 -55
  187. package/es/site/data/components/tour/demos/customButtons/react.d.ts +0 -33
  188. package/es/site/data/components/tour/demos/customButtons/react.js +0 -99
@@ -0,0 +1,312 @@
1
+ import {useInstance, Component, TypeDefs} from 'intact';
2
+ import {useState, watchState, State} from '../../hooks/useState';
3
+ import dayjs, {Dayjs, OpUnitType, QUnitType} from './dayjs';
4
+ import {findValueIndex} from './helpers';
5
+ import type {useFormats} from './useFormats';
6
+ import type {useDisabled} from './useDisabled';
7
+ import {isEqualArray, last, bind} from '../utils';
8
+ import {PanelFlags, usePanel} from './usePanel';
9
+ import type {BasePicker, BasePickerProps} from './basepicker';
10
+
11
+ export type Value = string | Date | number | Dayjs;
12
+
13
+ export type StateValueRange = [Dayjs] | [Dayjs, Dayjs];
14
+ export type StateValueItem = Dayjs | StateValueRange;
15
+ export type StateValue = StateValueItem[]
16
+
17
+ export type StringValue = string | string[] | [string, string] | [string, string][]
18
+ export type DayjsValueRange = [Dayjs, Dayjs]
19
+ export type DayjsValueItem = Dayjs | DayjsValueRange
20
+ export type DayjsValue = DayjsValueItem[]
21
+
22
+ export function useValueBase(
23
+ {
24
+ createDateByValueFormat,
25
+ createDateByShowFormat,
26
+ getShowString,
27
+ getValueString,
28
+ }: ReturnType<typeof useFormats>,
29
+ {isDisabled, minDate}: ReturnType<typeof useDisabled>,
30
+ panel: ReturnType<typeof usePanel>,
31
+ shouldUpdateValue: (v: StateValueItem) => boolean,
32
+ updateValueOnInput: (v: DayjsValueItem) => void,
33
+ getEqualType: () => OpUnitType | QUnitType,
34
+ updateStateValue: (v: DayjsValue, value: State<StateValue>) => void,
35
+ ) {
36
+ // Normalize the value to multipe values, no matter it's multipe or not
37
+ const value = useState<StateValue>([]);
38
+ const instance = useInstance() as BasePicker<BasePickerProps<Value>>;
39
+ let dayjsValue: DayjsValue = [];
40
+
41
+ instance.watch('value', (newValue, oldValue) => {
42
+ if (isEqualArray(newValue, oldValue)) return;
43
+ dayjsValue = convertToDayjs(newValue);
44
+ updateStateValue(dayjsValue, value);
45
+ // should update keywords
46
+ instance.resetKeywords();
47
+ });
48
+
49
+ watchState(value, (value) => {
50
+ // silently update the keywords to display the currently selected value
51
+ instance.resetKeywords(true);
52
+ /**
53
+ * the position may changed after the input break line in multipe mode
54
+ * use Macro task instead of nextTick, because it has too many Micro tasks
55
+ */
56
+ setTimeout(() => {
57
+ instance.position();
58
+ });
59
+ });
60
+ instance.on('hide', () => {
61
+ // reset the value after hiding
62
+ value.set(dayjsValue.slice(0));
63
+ });
64
+
65
+ watchState(instance.input.keywords, v => {
66
+ const {range, multiple} = instance.get();
67
+ if (!multiple && v === '') return instance.set('value', null);
68
+
69
+ if (range) {
70
+ const [start, end] = v.split(/\s*~\s*/).map(s => s.trim());
71
+ if (start && end) {
72
+ const startDate = createDateByShowFormat(start);
73
+ if (!isValidDate(startDate)) {
74
+ return;
75
+ }
76
+ const endDate = createDateByShowFormat(end);
77
+ if (!isValidDate(endDate)) {
78
+ return;
79
+ }
80
+ if (endDate.isAfter(startDate)) {
81
+ updateValueOnInput([startDate, endDate]);
82
+ } else {
83
+ updateValueOnInput([endDate, startDate]);
84
+ }
85
+ }
86
+ } else {
87
+ const date = createDateByShowFormat(v);
88
+ if (isValidDate(date)) {
89
+ updateValueOnInput(date);
90
+ }
91
+ }
92
+ });
93
+
94
+ function convertToDayjs(v: BasePickerProps<Value>['value']): DayjsValue {
95
+ if (!v || Array.isArray(v) && !v.length) return [];
96
+ const {multiple} = instance.get();
97
+ if (!multiple) {
98
+ v = [v] as any;
99
+ }
100
+ return (v as (Value | [Value, Value])[]).map(value => {
101
+ if (Array.isArray(value)) {
102
+ // range
103
+ return value.map(createDateByValueFormat);
104
+ }
105
+ return createDateByValueFormat(value);
106
+ }) as DayjsValue;
107
+ }
108
+
109
+ function convertToValueString(v: DayjsValue): StringValue | null {
110
+ const results = v.map(value => {
111
+ if (Array.isArray(value)) {
112
+ return value.map(getValueString);
113
+ }
114
+ return getValueString(value);
115
+ });
116
+
117
+ if (!instance.get('multiple')) {
118
+ return results[0] || null;
119
+ }
120
+ return results as StringValue;
121
+ }
122
+
123
+ function format(): string | string[] {
124
+ const { multiple } = instance.get();
125
+ let labelValue = value.value;
126
+ if (multiple) {
127
+ labelValue = dayjsValue;
128
+ }
129
+ const results = labelValue.map(formatSingleValue);
130
+
131
+ if (!instance.get('multiple')) {
132
+ return results[0];
133
+ }
134
+ return results;
135
+ }
136
+
137
+ function formatSingleValue(value: StateValueItem) {
138
+ if (Array.isArray(value)) {
139
+ if (value.length === 1) {
140
+ return getShowString(value[0]) + ' ~';
141
+ }
142
+ return value.map(getShowString).join(' ~ ');
143
+ }
144
+ return getShowString(value);
145
+ }
146
+
147
+ function setSingleDate(v: StateValueItem, fromInput: boolean) {
148
+ value.set([v]);
149
+ if (fromInput || shouldUpdateValue(v)) {
150
+ updateValue();
151
+ }
152
+ }
153
+
154
+ function addMultipeDate(v: StateValueItem, fromInput: boolean) {
155
+ const {range} = instance.get();
156
+ let _value = value.value as StateValueItem[];
157
+ _value = !_value ? [] : _value.slice();
158
+
159
+ if (range && (v as StateValueRange).length === 2) {
160
+ // pop the last value firstly, if it only has the start date
161
+ const lastValue = last(_value);
162
+ if (lastValue && (lastValue as StateValueRange).length < 2) {
163
+ _value.pop();
164
+ }
165
+ }
166
+
167
+ if (_value.length > dayjsValue.length) {
168
+ // remove the temporary value added by selecting time directly
169
+ _value.pop();
170
+ }
171
+
172
+ let _shouldUpdateValue = true;
173
+ if (fromInput || shouldUpdateValue(v)) {
174
+ // if select the date/year/month, then toggle the value.
175
+ // if from input, treat the value as the final value
176
+ const index = findValueIndex(_value, v, getEqualType());
177
+ if (index > -1) {
178
+ _value.splice(index, 1);
179
+ } else {
180
+ _value.push(v);
181
+ }
182
+ } else {
183
+ // select the datetime, only push the value,
184
+ // and unique the array on click confirm button
185
+ _value.push(v);
186
+ _shouldUpdateValue = false;
187
+ }
188
+
189
+ value.set(_value);
190
+
191
+ if (_shouldUpdateValue) {
192
+ updateValue();
193
+ }
194
+ }
195
+
196
+ function setValue(v: StateValueItem, fromInput: boolean) {
197
+ const multiple = instance.get('multiple');
198
+ if (multiple) {
199
+ addMultipeDate(v, fromInput);
200
+ } else {
201
+ setSingleDate(v, fromInput);
202
+ }
203
+ }
204
+
205
+ function updateValue() {
206
+ const _value = value.value as DayjsValue;
207
+ const { range } = instance.get();
208
+ if (range) {
209
+ // only fix the last value, since the others are already fixed
210
+ const lastValue = last(_value) as DayjsValueRange;
211
+ if (lastValue) {
212
+ lastValue.sort((a, b) => a.isAfter(b) ? 1 : -1);
213
+ }
214
+ }
215
+
216
+ const valueStr = convertToValueString(_value);
217
+ // only trigger change event once
218
+ if (!isEqualArray(valueStr, instance.get('value'))) {
219
+ instance.set('value', valueStr);
220
+ instance.resetKeywords();
221
+ }
222
+ }
223
+
224
+ // TODO
225
+ function setMoment() {
226
+ const now = dayjs();
227
+ setValue(now, true);
228
+ instance.hide();
229
+ }
230
+
231
+ function unique() {
232
+ const _value = value.value;
233
+ const map: Record<string, true> = {};
234
+ const results: StateValue = [];
235
+ _value.forEach(value => {
236
+ let key: string;
237
+ if (Array.isArray(value)) {
238
+ key = (value as DayjsValueRange).map(getValueString).join(' ~ ');
239
+ } else {
240
+ key = getValueString(value);
241
+ }
242
+ if (!map[key]) {
243
+ map[key] = true;
244
+ results.push(value);
245
+ }
246
+ });
247
+
248
+ value.set(results);
249
+ }
250
+
251
+ function isValidDate(date: Dayjs) {
252
+ return date.isValid() && !isDisabled(date, getEqualType());
253
+ }
254
+
255
+ function onChangeTime(date: Dayjs, flag: PanelFlags) {
256
+ const {range, multiple} = instance.get();
257
+ const values = value.value.slice();
258
+ // maybe we select time directly
259
+ let lastIndex = Math.max(values.length - 1, 0);
260
+ if (allValuesUpdatedInMultipleMode()) {
261
+ // need add new value, if all value.value has updated to dayjsValue
262
+ lastIndex = values.length;
263
+ }
264
+ let _value: StateValueItem = date;
265
+
266
+ if (range) {
267
+ _value = (values as DayjsValueRange[])[lastIndex];
268
+ if (_value) {
269
+ _value = _value.slice() as DayjsValueRange;
270
+ } else {
271
+ _value = [] as unknown as DayjsValueRange;
272
+ }
273
+ _value[flag] = date;
274
+ instance.trigger('selecting', _value, false);
275
+ }
276
+
277
+ values[lastIndex] = _value;
278
+ value.set(values);
279
+ }
280
+
281
+ function getTimeValue(flag: PanelFlags): Dayjs | null | undefined {
282
+ const _value = value.value;
283
+ if (!_value.length) return null;
284
+
285
+ const {range, min} = instance.get();
286
+ const lastValue = last(_value);
287
+ return range ? (lastValue as StateValueRange)[flag] : lastValue as Dayjs;
288
+ }
289
+
290
+ function getDayjsValue() {
291
+ return dayjsValue;
292
+ }
293
+
294
+ function allValuesUpdatedInMultipleMode() {
295
+ return instance.get('multiple') && value.value.length === dayjsValue.length;
296
+ }
297
+
298
+ return {
299
+ value,
300
+ format,
301
+ formatSingleValue,
302
+ onChangeTime,
303
+ getTimeValue,
304
+ setValue,
305
+ convertToDayjs,
306
+ getDayjsValue,
307
+ unique,
308
+ setMoment,
309
+ updateValue,
310
+ allValuesUpdatedInMultipleMode,
311
+ };
312
+ }
@@ -39,7 +39,7 @@ export function useWeeks(
39
39
  isDisabled: status.isDisabled(day),
40
40
  isHover: day.isSame(focusDate.value, 'week'),
41
41
  isInRange: !isFirstOrLastDay && status.isActive(day, 'week')
42
- });
42
+ });
43
43
  }
44
44
  // 每周的数据,包括周数和7天的信息
45
45
  weeks.push({week: weekNumber, days});
@@ -55,10 +55,14 @@ export function useYears(
55
55
 
56
56
  function onClick(date: Dayjs) {
57
57
  showDate.set(date);
58
- if (instance.datepicker.get('type') !== 'year') {
59
- instance.type.set('month');
60
- } else {
58
+ const datepickerType = instance.datepicker.get('type');
59
+
60
+ if (datepickerType === 'year') {
61
61
  instance.triggerChange(date);
62
+ } else if (datepickerType === 'quarter') {
63
+ instance.type.set('quarter');
64
+ } else {
65
+ instance.type.set('month');
62
66
  }
63
67
  }
64
68
 
@@ -15,7 +15,6 @@ import {bind, isTextChildren, getRestProps} from '../utils';
15
15
  import {EMPTY_OBJ, isFunction, noop} from 'intact-shared';
16
16
  import {Options, position, Feedback} from '../position';
17
17
  import {cx} from '@emotion/css';
18
- import {DropdownMenu} from './menu';
19
18
  import {useDocumentClick, containsOrEqual} from '../../hooks/useDocumentClick';
20
19
  import {Portal, PortalProps} from '../portal';
21
20
  import {useShowHideEvents} from '../../hooks/useShowHideEvents';
@@ -40,6 +39,7 @@ export interface DropdownProps {
40
39
  collison?: Position['collision']
41
40
  of?: 'self' | 'parent' | Event
42
41
  container?: PortalProps['container']
42
+ alwaysShowOnClick?: boolean
43
43
  }
44
44
 
45
45
  export interface DropdownEvents {
@@ -64,6 +64,7 @@ const typeDefs: Required<TypeDefs<DropdownProps>> = {
64
64
  of: ['self', 'parent', typeof Event === 'undefined' ? undefined : Event],
65
65
  container: [String, Function],
66
66
  collison: ['none', 'fit', 'flip', 'flipfit', Array],
67
+ alwaysShowOnClick: Boolean,
67
68
  };
68
69
 
69
70
  const defaults = (): Partial<DropdownProps> => ({
@@ -266,15 +267,15 @@ function useDocumentClickForDropdown(dropdown: Dropdown) {
266
267
  // case 2: if right click on a trigger and its trigger type is contextmenu, ignore it
267
268
  // case 3: if click on a trigger and its trigger type is focus, do nothing
268
269
  // case 3: if click on sub-dropdown, we should also show the parent dropdown, so ignore it
269
- const trigger = dropdown.get('trigger');
270
+ const { trigger, alwaysShowOnClick }= dropdown.get();
270
271
  if (trigger === 'focus') return;
271
272
 
272
273
  const isHover = trigger === 'hover';
273
- if (isHover || trigger === 'contextmenu') {
274
+ if (isHover || trigger === 'contextmenu' || alwaysShowOnClick) {
274
275
  const triggerDom = findDomFromVNode(dropdown.$lastInput!, true) as Element;
275
276
  const target = e.target as Element;
276
277
  if (containsOrEqual(triggerDom, target)) {
277
- if (isHover) return;
278
+ if (isHover || alwaysShowOnClick) return;
278
279
  if (!isHover && e.type === 'contextmenu') return;
279
280
  }
280
281
  }
@@ -17,6 +17,7 @@ sidebar: doc
17
17
  | position | 菜单弹出的位置,默认与触发器左侧对齐向下偏移`8px`的地方 | `Position` &#124; `"left"` &#124; `"bottom"` &#124; `"right"` &#124; `"top"` | `{my: 'left top+8', 'left bottom'}` |
18
18
  | of | 弹出菜单的位置是相对当前触发元素,还是触发元素所在的菜单元素,这在嵌套菜单中可能会用到,可以使所有子菜单保持统一的对齐方式,例如:级联选择(Cascader)组件 | `"self"` &#124; `"parent"` &#124; `Event` | `"self"` |
19
19
  | container | 指定弹出菜单追加的位置,默认:`Dialog`类型的组件会追加到`Dialog`中,其他会追加到`body`中。你可以传入函数返回一个DOM用来作为插入的容器,或者传入字符串用来给`querySelector`进行查询 | `Container` | `undefined` |
20
+ | alwaysShowOnClick | 不管什么触发方式,重复点击触发器,都不隐藏弹层 | `boolean` | `false` |
20
21
 
21
22
  ```ts
22
23
  type Position = {
@@ -3,7 +3,7 @@ import template from './item.vdt';
3
3
  import {bind} from '../utils';
4
4
  import {useItemKeyboard, MenuKeyboardMethods} from './useKeyboard';
5
5
  import {Dropdown, DROPDOWN} from './dropdown';
6
- import {DropdownMenu, DROPDOWN_MENU} from './menu';
6
+ import {type DropdownMenu, DROPDOWN_MENU} from './menu';
7
7
  import {IgnoreClickEvent} from '../../hooks/useDocumentClick';
8
8
  import { Dropdown as ExportDropdown, DropdownMenu as ExportDropdownMenu } from '.';
9
9
  import { useConfigContext } from '../config';
@@ -20,7 +20,6 @@ import {useKeyboard} from '../../hooks/useKeyboard';
20
20
  import {useState} from '../../hooks/useState';
21
21
  import {eachChildren, isComponentVNode} from '../utils';
22
22
  import {DropdownItem} from './item';
23
- import {isStringOrNumber} from 'intact-shared';
24
23
  // can not import DropdownMenu from index.ts, otherwise it will cause circle reference
25
24
  // import {DropdownMenu} from './';
26
25
  import {DropdownMenu} from './menu';
@@ -71,6 +71,10 @@ export class Form extends Component<FormProps, FormEvents> {
71
71
  });
72
72
  }
73
73
 
74
+ public getAllInvalidFormItems() {
75
+ return this.items.filter(item => !item.isValid());
76
+ }
77
+
74
78
  public submit(e: Event) {
75
79
  this.validate().then(valid => {
76
80
  if (valid) {
@@ -63,7 +63,8 @@ export declare type ClassName = string | ((value: any, param: any) => string)
63
63
  | --- | --- | --- | --- |
64
64
  | validate | 验证表单所有规则 | - | `Promise<boolean>`: `.then(valid => {})`,`valid`为`true`验证成功,否则失败 |
65
65
  | reset | 重置表单验证状态 | - | `undefined` |
66
- | getFirstInvalidFormItem | 获取第一条出错的`FormItem` | - | `FormItem` |
66
+ | getFirstInvalidFormItem | 获取第一条校验失败的`FormItem` | - | `FormItem` |
67
+ | getAllInvalidFormItems | 获取所有校验失败的`FormItem` | - | `FormItem[]` |
67
68
 
68
69
  # 静态方法
69
70
 
@@ -33,6 +33,8 @@ describe('Form', () => {
33
33
  expect(element.innerHTML).to.matchSnapshot();
34
34
  const item = form.getFirstInvalidFormItem()!;
35
35
  expect(item.get('label')).to.eql('Input');
36
+ const items = form.getAllInvalidFormItems();
37
+ expect(items.length).to.eql(10);
36
38
 
37
39
  instance.reset();
38
40
  await wait();
@@ -177,4 +177,46 @@ describe('Input', () => {
177
177
  await wait(50);
178
178
  expect(input.offsetWidth).to.gt(16);
179
179
  });
180
+
181
+ it('should handle textarea resize correctly', async () => {
182
+ class ResizeDemo extends Component {
183
+ static template = `
184
+ const {Input} = this;
185
+ <div>
186
+ <Input type="textarea" resize="none" placeholder="resize none" />
187
+ <Input type="textarea" resize="vertical" placeholder="resize vertical" />
188
+ <Input type="textarea" resize="none" rows="auto" placeholder="resize none with auto rows" />
189
+ </div>
190
+ `;
191
+ private Input = Input;
192
+ }
193
+
194
+ const [instance, element] = mount(ResizeDemo as any);
195
+
196
+ const inputElements = element.querySelectorAll('.k-input');
197
+
198
+ // should have k-resize-none class
199
+ const resizeNoneElement = inputElements[0];
200
+ expect(resizeNoneElement.classList.contains('k-resize-none')).to.be.true;
201
+ expect(resizeNoneElement.classList.contains('k-resize-vertical')).to.be.false;
202
+
203
+ const resizeVerticalElement = inputElements[1];
204
+ expect(resizeVerticalElement.classList.contains('k-resize-vertical')).to.be.true;
205
+ expect(resizeVerticalElement.classList.contains('k-resize-none')).to.be.false;
206
+
207
+ // resize="none" with rows="auto" (should have k-resize-none class)
208
+ const resizeNoneAutoElement = inputElements[2];
209
+ expect(resizeNoneAutoElement.classList.contains('k-resize-none')).to.be.true;
210
+
211
+ // Test CSS computed styles to ensure resize is applied correctly
212
+ const textareas = element.querySelectorAll('.k-textarea');
213
+ const textarea1 = textareas[0] as HTMLTextAreaElement;
214
+ const textarea2 = textareas[1] as HTMLTextAreaElement;
215
+
216
+ const computedStyle1 = getComputedStyle(textarea1);
217
+ const computedStyle2 = getComputedStyle(textarea2);
218
+
219
+ expect(computedStyle1.resize).to.eql('none');
220
+ expect(computedStyle2.resize).to.eql('vertical');
221
+ });
180
222
  });
@@ -157,6 +157,14 @@ export class Input<V extends Value = Value> extends Component<InputProps<V>, Inp
157
157
  selectValue(this.inputRef.value!);
158
158
  }
159
159
 
160
+ getStringWidth(str: string) {
161
+ return this.autoWidth.getStringWidth(str);
162
+ }
163
+
164
+ getSelectionStart() {
165
+ return this.inputRef.value!.selectionStart;
166
+ }
167
+
160
168
  @bind
161
169
  private clear(e: MouseEvent) {
162
170
  this.set<string>('value', '');
@@ -41,8 +41,7 @@ const classNameObj = {
41
41
  [`${k}-flat`]: flat,
42
42
  [`${k}-type-textarea`]: type === 'textarea',
43
43
  [`${k}-focus`]: isFocus.value,
44
- [`${k}-resize-${resize}`]: type === 'textarea' && isNotAutoRows,
45
- [`${k}-resize-none`]: type === 'textarea' && !isNotAutoRows,
44
+ [`${k}-resize-${isNotAutoRows ? resize : 'none'}`]: type === 'textarea',
46
45
  [className]: className,
47
46
  [makeStyles(k)]: true,
48
47
  }
@@ -54,7 +53,7 @@ const {
54
53
  showIcon: showPasswordIcon,
55
54
  } = this.showPassword;
56
55
 
57
- const {fakeRef, width: {value: fakeWidth}} = this.autoWidth;
56
+ const {fakeRef, width: {value: fakeWidth}, forceShowFake} = this.autoWidth;
58
57
  const height = this.autoRows;
59
58
 
60
59
  const inputValue = frozenOnInput && inputing ? originalValue : value;
@@ -142,7 +141,7 @@ if (hasInputValue) {
142
141
  </span>
143
142
  <b:suffix />
144
143
  </div>
145
- <div class={`${k}-input-fake`} v-if={autoWidth}>
144
+ <div class={`${k}-input-fake`} v-if={autoWidth || forceShowFake.value}>
146
145
  <pre ref={fakeRef}>{!hasValue ? (hasInputValue || !defaultValue ? placeholder : defaultValue) : inputValue}</pre>
147
146
  </div>
148
147
  </div>
@@ -6,6 +6,7 @@ export function useAutoWidth() {
6
6
  const instance = useInstance() as Input;
7
7
  const fakeRef = createRef<HTMLDivElement>();
8
8
  const width = useState<number>(0);
9
+ const forceShowFake = useState(false);
9
10
 
10
11
  instance.watch('value', adjustWidth, {inited: true, presented: true});
11
12
  instance.watch('placeholder', adjustWidth, {inited: true, presented: true});
@@ -23,7 +24,24 @@ export function useAutoWidth() {
23
24
  }
24
25
  }
25
26
 
26
- return {fakeRef, width};
27
+ function getStringWidth(str: string) {
28
+ forceShowFake.set(true);
29
+ return new Promise<number>((resolve) => {
30
+ nextTick(() => {
31
+ const fakeElem = fakeRef.value!;
32
+ const textNode = fakeElem.firstChild!;
33
+ const oldStr = textNode.nodeValue;
34
+ textNode.nodeValue = str;
35
+
36
+ resolve(fakeElem.offsetWidth);
37
+
38
+ textNode.nodeValue = oldStr;
39
+ forceShowFake.set(false);
40
+ });
41
+ });
42
+ }
43
+
44
+ return {fakeRef, width, forceShowFake, getStringWidth};
27
45
  }
28
46
 
29
47
  function isVisible(elem: HTMLDivElement | null) {
@@ -27,7 +27,13 @@ import {Menu, MenuItem, Switch, Icon} from 'kpc';
27
27
  </MenuItem>
28
28
  </Menu>
29
29
  </MenuItem>
30
- <MenuItem key="4"><Icon class="ion-gear-b" />menu 4</MenuItem>
30
+ <MenuItem key="4">
31
+ <Icon class="ion-gear-b" />menu 4
32
+ <Menu>
33
+ <MenuItem key="4-1">sub menu 1</MenuItem>
34
+ <MenuItem key="4-2">sub menu 2</MenuItem>
35
+ </Menu>
36
+ </MenuItem>
31
37
  </Menu>
32
38
  <br /><br />
33
39
  <Switch
@@ -3,6 +3,7 @@ import BasicDemo from '~/components/menu/demos/basic';
3
3
  import CollapseDemo from '~/components/menu/demos/collapse';
4
4
  import AccordionDemo from '~/components/menu/demos/accordion';
5
5
  import CollapseArrowDemo from '~/components/menu/demos/showCollapseArrow';
6
+ import HorizontalDemo from '~/components/menu/demos/horizontal';
6
7
  import {mount, unmount, dispatchEvent, getElement, wait} from '../../test/utils';
7
8
  import {Menu, MenuItem} from './';
8
9
 
@@ -190,4 +191,22 @@ describe('Menu', () => {
190
191
  expect(element.outerHTML).to.matchSnapshot();
191
192
  expect(getElement('.k-menu-arrow-box')).to.be.undefined;
192
193
  });
194
+
195
+ it('detecting pop-up position during rapid mouse hover over menus', async () => {
196
+ const [instance, element] = mount(HorizontalDemo);
197
+
198
+ const [, , menu3, menu4] = element.querySelectorAll<HTMLElement>('.k-menu-item-title');
199
+ dispatchEvent(menu3, 'mouseenter');
200
+ await wait(500);
201
+ const dropdownmenu1 = getElement('.k-dropdown-menu')!;
202
+ const top1 = dropdownmenu1.getBoundingClientRect().top;
203
+
204
+ dispatchEvent(menu3, 'mouseleave');
205
+ dispatchEvent(menu4, 'mouseenter');
206
+ await wait(1000);
207
+ const dropdownmenu2 = getElement('.k-dropdown-menu')!;
208
+ const top2 = dropdownmenu2.getBoundingClientRect().top;
209
+
210
+ expect(top1).to.eql(top2);
211
+ });
193
212
  });
@@ -18,7 +18,7 @@ const defaults = {
18
18
  item: {
19
19
  height: '40px',
20
20
  padding: '0 12px',
21
- bodyPadding: '0 4px',
21
+ bodyPadding: '4px',
22
22
  color: '#aeaeb9',
23
23
  hoverColor: '#fff',
24
24
  disabledColor: '#53535a',
@@ -292,6 +292,7 @@ export const makeMenuStyles = cache(function makeMenuStyles(k: string) {
292
292
  &.${k}-dropdown-menu {
293
293
  width: fit-content;
294
294
  min-width: ${menu.dropdown.minWidth};
295
+ position: absolute;
295
296
  .${k}-menu-item-arrow {
296
297
  transform: rotate(-90deg)
297
298
  }
@@ -18,6 +18,7 @@ export function useMouseEvents(
18
18
  let y: number;
19
19
  let itemHeight: number;
20
20
  let deltaY: number;
21
+ let wheelDeltaY: number = 0;
21
22
  const {start, dragging} = useDraggable({
22
23
  onStart(e: MouseEvent) {
23
24
  dragged = false;
@@ -67,15 +68,15 @@ export function useMouseEvents(
67
68
  itemHeight = getItemHeight();
68
69
 
69
70
  const threshold = itemHeight * 0.6;
70
- deltaY = (deltaY || 0) + e.deltaY;
71
+ wheelDeltaY += e.deltaY;
71
72
 
72
- if (Math.abs(deltaY) >= threshold) {
73
- if (deltaY > 0) {
73
+ if (Math.abs(wheelDeltaY) >= threshold) {
74
+ if (wheelDeltaY > 0) {
74
75
  setByRelativeIndex(1, null, true);
75
76
  } else {
76
77
  setByRelativeIndex(-1, null, true);
77
78
  }
78
- deltaY = 0;
79
+ wheelDeltaY = 0;
79
80
  }
80
81
  }
81
82
 
@@ -142,8 +142,9 @@ export abstract class BaseSelect<
142
142
  this.set('show', false);
143
143
  }
144
144
 
145
- public resetKeywords(keywords: State<string>) {
146
- keywords.set('');
145
+ @bind
146
+ public resetKeywords() {
147
+ this.input.keywords.set('');
147
148
  }
148
149
 
149
150
  protected hasValue() {