@antify/ui 1.1.0 → 1.3.0

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 (105) hide show
  1. package/dist/components/AntAccordion.vue +15 -7
  2. package/dist/components/AntAccordionItem.vue +19 -5
  3. package/dist/components/AntAlert.vue +8 -6
  4. package/dist/components/AntDropdown.vue +50 -36
  5. package/dist/components/AntIcon.vue +8 -6
  6. package/dist/components/AntKeycap.vue +10 -10
  7. package/dist/components/AntListGroup.vue +5 -3
  8. package/dist/components/AntModal.vue +17 -7
  9. package/dist/components/AntPopover.vue +118 -42
  10. package/dist/components/AntSkeleton.vue +1 -1
  11. package/dist/components/AntTooltip.vue +127 -80
  12. package/dist/components/__stories/AntAccordion.stories.js +12 -3
  13. package/dist/components/__stories/AntAccordion.stories.mjs +12 -3
  14. package/dist/components/__stories/AntDropdown.stories.js +27 -23
  15. package/dist/components/__stories/AntDropdown.stories.mjs +26 -22
  16. package/dist/components/__stories/AntListGroup.stories.js +1 -1
  17. package/dist/components/__stories/AntListGroup.stories.mjs +1 -1
  18. package/dist/components/__stories/AntModal.stories.js +2 -1
  19. package/dist/components/__stories/AntModal.stories.mjs +2 -1
  20. package/dist/components/__stories/AntPopover.stories.js +22 -21
  21. package/dist/components/__stories/AntPopover.stories.mjs +22 -20
  22. package/dist/components/__stories/AntTooltip.stories.d.ts +0 -10
  23. package/dist/components/__stories/AntTooltip.stories.js +34 -212
  24. package/dist/components/__stories/AntTooltip.stories.mjs +29 -193
  25. package/dist/components/buttons/AntButton.vue +41 -44
  26. package/dist/components/crud/AntCrud.vue +1 -1
  27. package/dist/components/crud/AntCrudDetailActions.vue +1 -0
  28. package/dist/components/crud/AntCrudDetailNav.vue +1 -0
  29. package/dist/components/crud/AntCrudTableFilter.vue +20 -18
  30. package/dist/components/forms/AntField.vue +7 -2
  31. package/dist/components/forms/__stories/AntField.stories.js +0 -16
  32. package/dist/components/forms/__stories/AntField.stories.mjs +2 -16
  33. package/dist/components/index.d.ts +2 -2
  34. package/dist/components/index.js +7 -7
  35. package/dist/components/index.mjs +2 -2
  36. package/dist/components/inputs/AntCheckbox.vue +25 -6
  37. package/dist/components/inputs/AntDateInput.vue +1 -1
  38. package/dist/components/inputs/AntRadio.vue +2 -1
  39. package/dist/components/inputs/AntSelect.vue +25 -22
  40. package/dist/components/inputs/AntSwitch.vue +2 -7
  41. package/dist/components/inputs/AntTagInput.vue +91 -114
  42. package/dist/components/inputs/AntTextarea.vue +16 -4
  43. package/dist/components/inputs/Elements/AntBaseInput.vue +2 -2
  44. package/dist/components/inputs/Elements/{AntDropDown.vue → AntSelectMenu.vue} +84 -40
  45. package/dist/components/inputs/Elements/__stories/AntBaseInput.stories.d.ts +0 -1
  46. package/dist/components/inputs/Elements/__stories/AntBaseInput.stories.js +1 -29
  47. package/dist/components/inputs/Elements/__stories/AntBaseInput.stories.mjs +0 -22
  48. package/dist/components/inputs/Elements/index.d.ts +2 -1
  49. package/dist/components/inputs/Elements/index.js +7 -0
  50. package/dist/components/inputs/Elements/index.mjs +3 -1
  51. package/dist/components/inputs/__stories/AntCheckbox.stories.d.ts +0 -1
  52. package/dist/components/inputs/__stories/AntCheckbox.stories.js +1 -43
  53. package/dist/components/inputs/__stories/AntCheckbox.stories.mjs +0 -35
  54. package/dist/components/inputs/__stories/AntCheckboxGroup.stories.d.ts +0 -1
  55. package/dist/components/inputs/__stories/AntCheckboxGroup.stories.js +1 -31
  56. package/dist/components/inputs/__stories/AntCheckboxGroup.stories.mjs +0 -28
  57. package/dist/components/inputs/__stories/AntDateInput.stories.d.ts +0 -1
  58. package/dist/components/inputs/__stories/AntDateInput.stories.js +1 -32
  59. package/dist/components/inputs/__stories/AntDateInput.stories.mjs +0 -28
  60. package/dist/components/inputs/__stories/AntNumberInput.stories.d.ts +0 -2
  61. package/dist/components/inputs/__stories/AntNumberInput.stories.js +1 -65
  62. package/dist/components/inputs/__stories/AntNumberInput.stories.mjs +1 -54
  63. package/dist/components/inputs/__stories/AntPasswordInput.stories.d.ts +0 -1
  64. package/dist/components/inputs/__stories/AntPasswordInput.stories.js +1 -35
  65. package/dist/components/inputs/__stories/AntPasswordInput.stories.mjs +0 -25
  66. package/dist/components/inputs/__stories/AntRadioGroup.stories.d.ts +0 -1
  67. package/dist/components/inputs/__stories/AntRadioGroup.stories.js +1 -47
  68. package/dist/components/inputs/__stories/AntRadioGroup.stories.mjs +0 -46
  69. package/dist/components/inputs/__stories/AntRangeSlider.stories.d.ts +0 -1
  70. package/dist/components/inputs/__stories/AntRangeSlider.stories.js +1 -33
  71. package/dist/components/inputs/__stories/AntRangeSlider.stories.mjs +1 -28
  72. package/dist/components/inputs/__stories/AntSelect.stories.d.ts +0 -1
  73. package/dist/components/inputs/__stories/AntSelect.stories.js +18 -46
  74. package/dist/components/inputs/__stories/AntSelect.stories.mjs +16 -47
  75. package/dist/components/inputs/__stories/AntSwitch.stories.d.ts +0 -1
  76. package/dist/components/inputs/__stories/AntSwitch.stories.js +1 -42
  77. package/dist/components/inputs/__stories/AntSwitch.stories.mjs +1 -37
  78. package/dist/components/inputs/__stories/AntSwitcher.stories.d.ts +0 -1
  79. package/dist/components/inputs/__stories/AntSwitcher.stories.js +1 -51
  80. package/dist/components/inputs/__stories/AntSwitcher.stories.mjs +1 -51
  81. package/dist/components/inputs/__stories/AntTagInput.stories.d.ts +0 -1
  82. package/dist/components/inputs/__stories/AntTagInput.stories.js +1 -35
  83. package/dist/components/inputs/__stories/AntTagInput.stories.mjs +0 -33
  84. package/dist/components/inputs/__stories/AntTextInput.stories.d.ts +0 -2
  85. package/dist/components/inputs/__stories/AntTextInput.stories.js +2 -107
  86. package/dist/components/inputs/__stories/AntTextInput.stories.mjs +1 -104
  87. package/dist/components/inputs/__stories/AntTextarea.stories.d.ts +0 -2
  88. package/dist/components/inputs/__stories/AntTextarea.stories.js +7 -66
  89. package/dist/components/inputs/__stories/AntTextarea.stories.mjs +6 -55
  90. package/dist/components/inputs/__stories/AntUnitInput.stories.d.ts +0 -2
  91. package/dist/components/inputs/__stories/AntUnitInput.stories.js +1 -61
  92. package/dist/components/inputs/__stories/AntUnitInput.stories.mjs +0 -53
  93. package/dist/components/table/AntTable.vue +17 -15
  94. package/dist/components/table/AntTd.vue +1 -2
  95. package/dist/components/table/__stories/AntTable.stories.js +7 -14
  96. package/dist/components/table/__stories/AntTable.stories.mjs +7 -15
  97. package/dist/components/tabs/AntTabItem.vue +24 -7
  98. package/dist/components/tabs/AntTabs.vue +14 -2
  99. package/dist/components/tabs/__stories/AntTabs.stories.d.ts +1 -0
  100. package/dist/components/tabs/__stories/AntTabs.stories.js +112 -6
  101. package/dist/components/tabs/__stories/AntTabs.stories.mjs +120 -5
  102. package/dist/components/tabs/__types/AntTabItem.types.d.ts +2 -0
  103. package/dist/components/tabs/__types/AntTabItem.types.js +1 -0
  104. package/dist/components/tabs/__types/AntTabItem.types.mjs +1 -0
  105. package/package.json +2 -1
@@ -8,7 +8,7 @@ import {computed, onMounted, type Ref, ref, watch} from 'vue';
8
8
  import AntTag from '../AntTag.vue';
9
9
  import AntIcon from '../AntIcon.vue';
10
10
  import {AntTagSize, IconSize} from '../__types';
11
- import AntDropDown from './Elements/AntDropDown.vue';
11
+ import AntSelectMenu from './Elements/AntSelectMenu.vue';
12
12
  import AntSkeleton from '../AntSkeleton.vue';
13
13
  import {vOnClickOutside} from '@vueuse/components';
14
14
  import {AntTagInputSize} from './__types/AntTagInput.types';
@@ -61,7 +61,6 @@ const dropDownOpen = ref(false);
61
61
  const hasInputState = computed(() => props.skeleton || props.readonly || props.disabled);
62
62
  const focusedDropDownItem: Ref<string | number | null> = ref(null);
63
63
  const tagInput = ref('');
64
- const filteredOptions = ref(props.options);
65
64
  const inputRef: Ref<HTMLElement | null> = ref(null);
66
65
  const inputContainerClasses = computed(() => {
67
66
  const variants: Record<InputState, string> = {
@@ -89,7 +88,6 @@ const inputContainerClasses = computed(() => {
89
88
  'rounded-none': props.grouped === Grouped.center,
90
89
  'rounded-tl-none rounded-bl-none rounded-tr-md rounded-br-md': props.grouped === Grouped.right,
91
90
  'rounded-md': props.grouped === Grouped.none,
92
- 'rounded-bl-none rounded-br-none': dropDownOpen.value && (!props.options || props.options.length > 0) && !props.readonly,
93
91
  'invisible': props.skeleton,
94
92
  };
95
93
  });
@@ -119,13 +117,23 @@ const skeletonGrouped = computed(() => {
119
117
  return Grouped.left;
120
118
  }
121
119
  });
120
+ const filteredOptions = computed(() => {
121
+ return props.options.filter(option => {
122
+ // Remove all elements that are in modelValue from the filtered options
123
+ if (_modelValue.value && !props.allowDuplicates) {
124
+ return !_modelValue.value?.includes(option.value);
125
+ }
126
+
127
+ return option.label.toLowerCase().includes(tagInput.value.toLowerCase())
128
+ });
129
+ })
122
130
 
123
131
  function onClickOutside() {
124
132
  if (!dropDownOpen.value) {
125
133
  return;
126
134
  }
127
-
128
- dropDownOpen.value = false;
135
+ console.log("click outside");
136
+ // dropDownOpen.value = false;
129
137
  }
130
138
 
131
139
  async function checkCreateTag(item: string): Promise<void> {
@@ -153,14 +161,13 @@ function addTagFromOptions(item: string | number) {
153
161
  addTag(item);
154
162
 
155
163
  if (props.autoCloseAfterSelection) {
164
+ console.log("HIER");
156
165
  dropDownOpen.value = false;
157
166
  }
158
167
  }
159
168
  }
160
169
 
161
170
  function addTag(tagValue: string | number): void {
162
- _modelValue.value = _modelValue.value || [];
163
-
164
171
  if (!props.allowDuplicates && _modelValue.value?.includes(tagValue) || !tagValue) {
165
172
  return;
166
173
  }
@@ -172,53 +179,22 @@ function addTag(tagValue: string | number): void {
172
179
  }
173
180
 
174
181
  tagInput.value = '';
175
-
176
- filterDropDown();
177
182
  }
178
183
 
179
184
  function removeLastTag() {
180
185
  if (tagInput.value === '' && Array.isArray(_modelValue.value) && _modelValue.value.length > 0) {
181
186
  _modelValue.value.splice(-1, 1);
182
-
183
- filterDropDown();
184
187
  }
185
188
  }
186
189
 
187
190
  function removeTag(tag: string | number) {
188
191
  if (_modelValue.value && !props.disabled && !props.skeleton && !props.readonly) {
189
192
  _modelValue.value.splice(_modelValue.value.findIndex((_value) => _value === tag), 1);
190
-
191
- filterDropDown();
192
193
  }
193
194
  }
194
195
 
195
196
  function changeFocus() {
196
- if (props.openOnFocus) {
197
- dropDownOpen.value = true;
198
- }
199
- }
200
-
201
- function filterDropDown() {
202
- if (!props.options) {
203
- return;
204
- }
205
-
206
- if (props.allowCreate) {
207
- focusedDropDownItem.value = null;
208
- }
209
-
210
197
  dropDownOpen.value = true;
211
-
212
- filteredOptions.value = props.options.filter(option => option.label.toLowerCase().includes(tagInput.value.toLowerCase()));
213
-
214
- // Remove all elements that are in modelValue from the filtered options
215
- if (_modelValue.value && !props.allowDuplicates) {
216
- filteredOptions.value = filteredOptions.value.filter(option => !_modelValue.value?.includes(option.value));
217
- }
218
-
219
- if (!props.allowCreate && filteredOptions.value.length > 0) {
220
- focusedDropDownItem.value = filteredOptions.value[0]?.value;
221
- }
222
198
  }
223
199
 
224
200
  function onBlur(e: FocusEvent) {
@@ -251,89 +227,90 @@ onMounted(() => {
251
227
  </script>
252
228
 
253
229
  <template>
254
- <AntField
255
- :label="label"
256
- :size="size as unknown as Size"
257
- :skeleton="_skeleton"
258
- :description="description"
259
- :state="state"
260
- :expanded="expanded"
261
- :messages="messages"
262
- >
263
- <div
264
- v-on-click-outside="onClickOutside"
265
- class="relative w-full"
230
+ <div>
231
+ <AntField
232
+ :label="label"
233
+ :size="size as unknown as Size"
234
+ :skeleton="_skeleton"
235
+ :description="description"
236
+ :state="state"
237
+ :expanded="expanded"
238
+ :messages="messages"
266
239
  >
267
- <AntSkeleton
268
- v-if="skeleton"
269
- absolute
270
- rounded
271
- :grouped="skeletonGrouped"
272
- />
273
-
274
240
  <div
275
- :class="inputContainerClasses"
276
- class="w-full flex items-center"
241
+ class="relative w-full"
277
242
  >
243
+ <AntSkeleton
244
+ v-if="skeleton"
245
+ absolute
246
+ rounded
247
+ :grouped="skeletonGrouped"
248
+ />
249
+
278
250
  <div
279
- class="flex gap-2.5 items-center"
251
+ :class="inputContainerClasses"
252
+ class="w-full flex items-center"
253
+ v-on-click-outside="onClickOutside"
280
254
  >
281
- <AntTag
282
- v-for="(tag, index) in _modelValue"
283
- :key="`tag-input-tag-${index}`"
284
- :size="AntTagSize.xs3"
285
- :state="state as unknown as TagState"
286
- :dismiss="!readonly"
287
- @close="removeTag(tag)"
255
+ <div
256
+ class="flex gap-2.5 items-center"
288
257
  >
289
- {{ options.find((option: SelectOption) => option.value === tag)?.label }}
290
- </AntTag>
258
+ <AntTag
259
+ v-for="(tag, index) in _modelValue"
260
+ :key="`tag-input-tag-${index}`"
261
+ :size="AntTagSize.xs3"
262
+ :state="state as unknown as TagState"
263
+ :dismiss="!readonly"
264
+ @close="removeTag(tag)"
265
+ >
266
+ {{ options.find((option: SelectOption) => option.value === tag)?.label }}
267
+ </AntTag>
268
+ </div>
269
+
270
+ <!-- Input -->
271
+ <div class="flex items-center w-32 shrink grow">
272
+ <AntIcon
273
+ :icon="icon"
274
+ :size="size === AntTagInputSize.sm ? IconSize.xs : IconSize.sm"
275
+ />
276
+
277
+ <input
278
+ ref="inputRef"
279
+ v-model="tagInput"
280
+ type="text"
281
+ :placeholder="placeholder"
282
+ :class="inputClasses"
283
+ :disabled="disabled"
284
+ :readonly="readonly"
285
+ @focus="changeFocus"
286
+ @keydown.delete="removeLastTag"
287
+ @keydown.enter.prevent="checkCreateTag(tagInput)"
288
+ @blur="onBlur"
289
+ />
290
+ </div>
291
291
  </div>
292
292
 
293
- <!-- Input -->
294
- <div class="flex items-center w-32 shrink grow">
295
- <AntIcon
296
- :icon="icon"
297
- :size="size === AntTagInputSize.sm ? IconSize.xs : IconSize.sm"
298
- />
299
-
300
- <input
301
- ref="inputRef"
302
- v-model="tagInput"
303
- type="text"
304
- :placeholder="placeholder"
305
- :class="inputClasses"
306
- :disabled="disabled"
307
- :readonly="readonly"
308
- @focus="changeFocus"
309
- @input="filterDropDown"
310
- @keydown.delete="removeLastTag"
311
- @keydown.enter.prevent="checkCreateTag(tagInput)"
312
- @blur="onBlur"
313
- />
314
- </div>
293
+ <AntSelectMenu
294
+ v-if="!disabled && !readonly"
295
+ v-model:focused="focusedDropDownItem"
296
+ v-model:open="dropDownOpen"
297
+ :model-value="null"
298
+ :auto-select-first-on-open="!allowCreate"
299
+ :options="filteredOptions"
300
+ :input-ref="inputRef"
301
+ :size="size as unknown as Size"
302
+ :state="state"
303
+ :focus-on-open="false"
304
+ :close-on-select-item="false"
305
+ @select-element="addTagFromOptions"
306
+ >
307
+ <template #empty>
308
+ <span v-if="allowCreate">
309
+ No tag found, create now
310
+ </span>
311
+ </template>
312
+ </AntSelectMenu>
315
313
  </div>
316
-
317
- <AntDropDown
318
- v-if="filteredOptions && !disabled && !readonly"
319
- ref="dropDownRef"
320
- v-model:focused="focusedDropDownItem"
321
- v-model:open="dropDownOpen"
322
- :model-value="null"
323
- :auto-select-first-on-open="!allowCreate"
324
- :options="filteredOptions"
325
- :input-ref="inputRef"
326
- :size="size as unknown as Size"
327
- :state="state"
328
- :focus-on-open="false"
329
- @select-element="addTagFromOptions"
330
- >
331
- <template #empty>
332
- <span v-if="allowCreate">
333
- No tag found, create now
334
- </span>
335
- </template>
336
- </AntDropDown>
337
- </div>
338
- </AntField>
314
+ </AntField>
315
+ </div>
339
316
  </template>
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import {computed, onMounted, watch} from 'vue';
2
+ import {computed, onMounted, ref, watch} from 'vue';
3
3
  import {Size} from '../../enums/Size.enum';
4
4
  import AntSkeleton from '../AntSkeleton.vue';
5
5
  import AntIcon from '../AntIcon.vue';
@@ -36,6 +36,7 @@ const props = withDefaults(defineProps<{
36
36
  limiter?: boolean;
37
37
  max?: number;
38
38
  messages?: string[];
39
+ resize?: boolean;
39
40
  }>(), {
40
41
  state: InputState.base,
41
42
  disabled: false,
@@ -45,8 +46,10 @@ const props = withDefaults(defineProps<{
45
46
  grouped: Grouped.none,
46
47
  showIcon: true,
47
48
  limiter: false,
49
+ resize: true,
48
50
  messages: () => []
49
51
  });
52
+
50
53
  const _modelValue = useVModel(props, 'modelValue', emit);
51
54
  const hasInputState = computed(() => props.skeleton || props.readonly || props.disabled);
52
55
  const icons = {
@@ -67,7 +70,7 @@ const inputClasses = computed(() => {
67
70
  };
68
71
 
69
72
  return {
70
- 'block transition-colors relative border-none outline w-full focus:z-10': true,
73
+ 'block transition-colors relative border-none outline w-full focus:z-10 h-full text-black': true,
71
74
  'outline-offset-[-1px] outline-1 focus:outline-offset-[-1px] focus:outline-1': true,
72
75
  'disabled:opacity-50 disabled:cursor-not-allowed': props.disabled,
73
76
  [variants[props.state]]: true,
@@ -90,7 +93,8 @@ const inputClasses = computed(() => {
90
93
  'rounded-none': props.grouped === Grouped.center,
91
94
  'rounded-tl-none rounded-bl-none rounded-tr-md rounded-br-md': props.grouped === Grouped.right,
92
95
  'rounded-md': props.grouped === Grouped.none,
93
- 'invisible': props.skeleton
96
+ 'invisible': props.skeleton,
97
+ 'resize-none': !props.resize
94
98
  };
95
99
  });
96
100
  const iconColor = computed(() => {
@@ -108,6 +112,7 @@ const iconColor = computed(() => {
108
112
  });
109
113
  const _wrapperClass = computed(() => classesToObjectSyntax(props.wrapperClass));
110
114
  const icon = computed(() => icons[props.state]);
115
+ const textAreaRef = ref();
111
116
  const getIconSize = computed(() => {
112
117
  if (props.size === Size.xs || props.size === Size.xs2) {
113
118
  return IconSize.xs;
@@ -141,6 +146,11 @@ function onBlur(e: FocusEvent) {
141
146
  emit('validate', props.modelValue);
142
147
  emit('blur', e);
143
148
  }
149
+
150
+ defineExpose({
151
+ getTextAreaRef: () => textAreaRef.value
152
+ });
153
+
144
154
  </script>
145
155
 
146
156
  <template>
@@ -153,9 +163,10 @@ function onBlur(e: FocusEvent) {
153
163
  :limiter-max-value="limiter && max !== undefined ? max : undefined"
154
164
  :limiter-value="limiter ? _modelValue?.length : undefined"
155
165
  :messages="messages"
166
+ :expanded-height="!resize"
156
167
  >
157
168
  <div
158
- class="block relative w-full"
169
+ class="block relative w-full h-full"
159
170
  :class="{...{'-mr-px': grouped !== Grouped.none}, ..._wrapperClass}"
160
171
  >
161
172
  <textarea
@@ -164,6 +175,7 @@ function onBlur(e: FocusEvent) {
164
175
  :placeholder="placeholder !== undefined ? placeholder : label"
165
176
  :disabled="disabled || skeleton"
166
177
  :readonly="readonly"
178
+ ref="textAreaRef"
167
179
  v-bind="$attrs"
168
180
  @blur="onBlur"
169
181
  />
@@ -68,7 +68,7 @@ const inputClasses = computed(() => {
68
68
  };
69
69
 
70
70
  return {
71
- 'block transition-colors relative border-none outline w-full focus:z-10 text-black': true,
71
+ 'block transition-colors relative border-none outline w-full focus:z-10 text-black font-regular': true,
72
72
  'outline-offset-[-1px] outline-1 focus:outline-offset-[-1px] focus:outline-1': true,
73
73
  'disabled:opacity-50 disabled:cursor-not-allowed': props.disabled,
74
74
  'text-right': props.type === BaseInputType.number,
@@ -235,7 +235,7 @@ function onClickClearIcon() {
235
235
  <div
236
236
  v-if="(nullable && _modelValue !== null && _modelValue !== '') || (showIcon && icon) || hasSlotContent(slot['icon-right'])"
237
237
  class="absolute h-full flex items-center justify-center right-0 top-0 transition-all z-20"
238
- :class="{'w-6': size === Size.xs2, 'w-7': size === Size.xs2, 'w-8': size === Size.sm, 'w-9': size === Size.md, 'w-10': size === Size.lg}"
238
+ :class="{'w-6': size === Size.xs2, 'w-7': size === Size.xs, 'w-8': size === Size.sm, 'w-9': size === Size.md, 'w-10': size === Size.lg}"
239
239
  >
240
240
  <slot name="icon-right">
241
241
  <AntIcon
@@ -6,12 +6,13 @@
6
6
  * Fix overflow bug (See Ellipsis Text story)
7
7
  * TODO:: if the dropdown is open and the user types something, the element with a matching value should be focused.
8
8
  */
9
-
10
9
  import {computed, nextTick, onMounted, onUnmounted, ref, watch} from 'vue';
11
10
  import {InputState, Size} from '../../../enums';
12
11
  import type {SelectOption} from '../__types';
13
- import {useVModel} from '@vueuse/core';
12
+ import {useElementSize, useVModel, onClickOutside} from '@vueuse/core';
14
13
  import type {Validator} from '@antify/validate';
14
+ import {autoUpdate, flip, offset, useFloating} from "@floating-ui/vue";
15
+ import {vOnClickOutside} from '@vueuse/components';
15
16
 
16
17
  const emit = defineEmits(['update:open', 'update:modelValue', 'update:focused', 'selectElement']);
17
18
  const props = withDefaults(defineProps<{
@@ -25,17 +26,39 @@ const props = withDefaults(defineProps<{
25
26
  inputRef?: HTMLElement | null;
26
27
  closeOnEnter?: boolean;
27
28
  autoSelectFirstOnOpen?: boolean;
29
+ closeOnSelectItem?: boolean;
28
30
  }>(), {
29
31
  state: InputState.base,
30
32
  focusOnOpen: true,
31
33
  closeOnEnter: false,
32
- autoSelectFirstOnOpen: true
34
+ autoSelectFirstOnOpen: true,
35
+ closeOnSelectItem: true,
36
+ });
37
+ const reference = ref<HTMLElement | null | undefined>(props.inputRef)
38
+ const width = useElementSize(reference);
39
+ const floating = ref<HTMLElement | null>(null)
40
+ const {floatingStyles, middlewareData, placement} = useFloating(reference, floating, {
41
+ placement: 'bottom',
42
+ whileElementsMounted: autoUpdate,
43
+ middleware: [
44
+ offset(8),
45
+ flip({
46
+ fallbackPlacements: ['top'],
47
+ }),
48
+ ]
49
+ });
50
+
51
+ onClickOutside(floating, () => {
52
+ if (!props.open) {
53
+ return;
54
+ }
55
+
56
+ emit('update:open', false);
33
57
  });
34
58
 
35
59
  const _modelValue = useVModel(props, 'modelValue', emit);
36
60
  const isOpen = useVModel(props, 'open', emit);
37
61
  const focusedDropDownItem = useVModel(props, 'focused', emit);
38
-
39
62
  const dropdownClasses = computed(() => {
40
63
  const variants: Record<InputState, string> = {
41
64
  [InputState.base]: 'bg-neutral-300 border-neutral-300',
@@ -46,11 +69,9 @@ const dropdownClasses = computed(() => {
46
69
  };
47
70
 
48
71
  return {
49
- 'absolute w-full border flex flex-col gap-px outline-none -mt-px overflow-hidden shadow-md z-40': true,
50
- 'rounded-bl-md rounded-br-md': true,
72
+ 'w-full border flex flex-col gap-px outline-none -mt-px overflow-hidden shadow-md z-[90]': true,
73
+ 'rounded-md': true,
51
74
  [variants[props.state]]: true,
52
- // Size
53
- 'text-sm': props.size === Size.sm || props.size === Size.md
54
75
  };
55
76
  });
56
77
  const dropDownItemClasses = computed(() => {
@@ -66,8 +87,11 @@ const dropDownItemClasses = computed(() => {
66
87
  'select-none text-ellipsis overflow-hidden whitespace-nowrap': true,
67
88
  [variants[props.state]]: true,
68
89
  // Size
69
- 'p-1.5': props.size === Size.sm,
70
- 'p-2': props.size === Size.md,
90
+ 'p-1 text-xs': props.size === Size.xs2,
91
+ 'p-1.5 text-xs': props.size === Size.xs,
92
+ 'p-1.5 text-sm': props.size === Size.sm,
93
+ 'p-2 text-sm': props.size === Size.md,
94
+ 'p-2.5 text-sm': props.size === Size.lg,
71
95
  };
72
96
  });
73
97
 
@@ -86,12 +110,12 @@ watch(isOpen, () => {
86
110
 
87
111
  onMounted(() => {
88
112
  nextTick(() => {
89
- props.inputRef?.addEventListener('keydown', onKeyDownDropDown);
113
+ reference.value?.addEventListener('keydown', onKeyDownDropDown);
90
114
  });
91
115
  });
92
116
 
93
117
  onUnmounted(() => {
94
- props.inputRef?.removeEventListener('keydown', onKeyDownDropDown);
118
+ reference.value?.removeEventListener('keydown', onKeyDownDropDown);
95
119
  });
96
120
 
97
121
  function onKeyDownDropDown(e: KeyboardEvent) {
@@ -100,6 +124,10 @@ function onKeyDownDropDown(e: KeyboardEvent) {
100
124
  isOpen.value = false;
101
125
  }
102
126
 
127
+ if (!isOpen.value) {
128
+ isOpen.value = true
129
+ }
130
+
103
131
  emit('selectElement', focusedDropDownItem.value);
104
132
  }
105
133
 
@@ -108,6 +136,7 @@ function onKeyDownDropDown(e: KeyboardEvent) {
108
136
  }
109
137
 
110
138
  if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
139
+ e.preventDefault()
111
140
  isOpen.value = true;
112
141
 
113
142
  const index = props.options.findIndex(option => option.value === focusedDropDownItem.value);
@@ -121,6 +150,7 @@ function onKeyDownDropDown(e: KeyboardEvent) {
121
150
  }
122
151
 
123
152
  if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
153
+ e.preventDefault()
124
154
  isOpen.value = true;
125
155
 
126
156
  const index = props.options.findIndex(option => option.value === focusedDropDownItem.value);
@@ -138,21 +168,24 @@ function onKeyDownDropDown(e: KeyboardEvent) {
138
168
 
139
169
  function getActiveDropDownItemClasses(option: SelectOption) {
140
170
  const variants: Record<InputState, string> = {
141
- [InputState.base]: 'bg-neutral-50/25',
142
- [InputState.success]: 'bg-success-100/25',
143
- [InputState.info]: 'bg-info-100/25',
144
- [InputState.warning]: 'bg-warning-100/25',
145
- [InputState.danger]: 'bg-danger-100/25',
171
+ [InputState.base]: '!bg-neutral-100',
172
+ [InputState.success]: 'bg-success-200',
173
+ [InputState.info]: 'bg-info-200',
174
+ [InputState.warning]: 'bg-warning-200',
175
+ [InputState.danger]: 'bg-danger-200',
146
176
  };
147
177
 
148
- return option.value === focusedDropDownItem.value ? {[variants[props.state]]: true} : {};
178
+ return option.value === focusedDropDownItem.value ? {'bg-white': false, [variants[props.state]]: true} : {};
149
179
  }
150
180
 
151
181
  function onClickDropDownItem(e: MouseEvent, value: string | number | null) {
152
182
  e.preventDefault();
153
- props.inputRef?.focus();
183
+ reference.value?.focus();
184
+
185
+ if (props.closeOnSelectItem) {
186
+ isOpen.value = false;
187
+ }
154
188
 
155
- isOpen.value = false;
156
189
  emit('selectElement', value);
157
190
  _modelValue.value = value;
158
191
  }
@@ -164,26 +197,37 @@ watch(_modelValue, (val) => {
164
197
 
165
198
  <template>
166
199
  <div
167
- v-if="isOpen"
168
- :class="dropdownClasses"
200
+ ref="reference"
201
+ class="relative"
169
202
  >
170
- <div
171
- v-for="(option, index) in options"
172
- :key="`option-${index}`"
173
- :class="{...dropDownItemClasses, ...getActiveDropDownItemClasses(option)}"
174
- @mousedown="(e) => onClickDropDownItem(e, option.value)"
175
- @mouseover="() => focusedDropDownItem = option.value"
176
- >
177
- {{ option.label }}
178
- </div>
179
-
180
- <div
181
- v-if="options.length === 0"
182
- :class="{...dropDownItemClasses}"
183
- >
184
- <slot name="empty">
185
- No options available
186
- </slot>
187
- </div>
203
+ <slot/>
204
+
205
+ <teleport to="body">
206
+ <div
207
+ v-if="isOpen"
208
+ :class="dropdownClasses"
209
+ ref="floating"
210
+ :style="{width: `${width.width.value}px!important`, ...floatingStyles}"
211
+ >
212
+ <div
213
+ v-for="(option, index) in options"
214
+ :key="`option-${index}`"
215
+ :class="{...dropDownItemClasses, ...getActiveDropDownItemClasses(option)}"
216
+ @mousedown="(e) => onClickDropDownItem(e, option.value)"
217
+ @mouseover="() => focusedDropDownItem = option.value"
218
+ >
219
+ {{ option.label }}
220
+ </div>
221
+
222
+ <div
223
+ v-if="options.length === 0"
224
+ :class="{...dropDownItemClasses}"
225
+ >
226
+ <slot name="empty">
227
+ No options available
228
+ </slot>
229
+ </div>
230
+ </div>
231
+ </teleport>
188
232
  </div>
189
233
  </template>
@@ -4,7 +4,6 @@ declare const meta: Meta<typeof AntBaseInput>;
4
4
  export default meta;
5
5
  type Story = StoryObj<typeof AntBaseInput>;
6
6
  export declare const Docs: Story;
7
- export declare const withValidator: Story;
8
7
  export declare const Nullable: Story;
9
8
  export declare const IconLeft: Story;
10
9
  export declare const IconRight: Story;
@@ -3,15 +3,13 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.withValidator = exports.default = exports.Summary = exports.Nullable = exports.IconRight = exports.IconLeft = exports.Docs = void 0;
6
+ module.exports = exports.Summary = exports.Nullable = exports.IconRight = exports.IconLeft = exports.Docs = void 0;
7
7
  var _Size = require("../../../../enums/Size.enum");
8
8
  var _AntBaseInput = require("../__types/AntBaseInput.types");
9
9
  var _AntBaseInput2 = _interopRequireDefault(require("../AntBaseInput.vue"));
10
10
  var _AntButton = _interopRequireDefault(require("../../../buttons/AntButton.vue"));
11
11
  var _AntIcon = _interopRequireDefault(require("../../../AntIcon.vue"));
12
12
  var _Grouped2 = require("../../../../enums/Grouped.enum");
13
- var _validate = require("@antify/validate");
14
- var _vue = require("vue");
15
13
  var _freeSolidSvgIcons = require("@fortawesome/free-solid-svg-icons");
16
14
  var _enums = require("../../../../enums");
17
15
  var _AntFormGroup = _interopRequireDefault(require("../../../forms/AntFormGroup.vue"));
@@ -136,32 +134,6 @@ const Docs = exports.Docs = {
136
134
  placeholder: "Placeholder"
137
135
  }
138
136
  };
139
- const withValidator = exports.withValidator = {
140
- render: args => ({
141
- components: {
142
- AntBaseInput: _AntBaseInput2.default
143
- },
144
- setup: () => {
145
- const validator = (0, _vue.reactive)((0, _validate.useFieldValidator)([_validate.isRequiredRule, _validate.notBlankRule]));
146
- return {
147
- args,
148
- validator,
149
- InputState: _enums.InputState
150
- };
151
- },
152
- template: `
153
- <AntBaseInput
154
- v-model="args.modelValue"
155
- v-bind="args"
156
- :state="args.state ? args.state : (validator.hasErrors() ? InputState.danger : undefined)"
157
- @validate="(val) => validator.validate(val)"
158
- />`
159
- }),
160
- args: {
161
- ...Docs.args,
162
- modelValue: ""
163
- }
164
- };
165
137
  const Nullable = exports.Nullable = {
166
138
  render: Docs.render,
167
139
  args: {