@sfxcode/nuxt-ui-formkit 1.1.1 → 1.2.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 (36) hide show
  1. package/README.md +11 -1
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/FUDataEdit.d.vue.ts +8 -4
  4. package/dist/runtime/components/FUDataEdit.vue +1 -1
  5. package/dist/runtime/components/FUDataEdit.vue.d.ts +8 -4
  6. package/dist/runtime/components/FUDataView.d.vue.ts +8 -4
  7. package/dist/runtime/components/FUDataView.vue +1 -1
  8. package/dist/runtime/components/FUDataView.vue.d.ts +8 -4
  9. package/dist/runtime/components/inputs/FUCalendar.d.vue.ts +48 -0
  10. package/dist/runtime/components/inputs/FUCalendar.vue +61 -0
  11. package/dist/runtime/components/inputs/FUCalendar.vue.d.ts +48 -0
  12. package/dist/runtime/components/inputs/FUEditor.d.vue.ts +60 -0
  13. package/dist/runtime/components/inputs/FUEditor.vue +121 -0
  14. package/dist/runtime/components/inputs/FUEditor.vue.d.ts +60 -0
  15. package/dist/runtime/components/inputs/FUFileUpload.d.vue.ts +39 -0
  16. package/dist/runtime/components/inputs/FUFileUpload.vue +56 -0
  17. package/dist/runtime/components/inputs/FUFileUpload.vue.d.ts +39 -0
  18. package/dist/runtime/components/inputs/FUInputDate.d.vue.ts +18 -5
  19. package/dist/runtime/components/inputs/FUInputDate.vue +24 -6
  20. package/dist/runtime/components/inputs/FUInputDate.vue.d.ts +18 -5
  21. package/dist/runtime/components/inputs/FUListbox.d.vue.ts +2 -0
  22. package/dist/runtime/components/inputs/FUListbox.vue +226 -6
  23. package/dist/runtime/components/inputs/FUListbox.vue.d.ts +2 -0
  24. package/dist/runtime/formkit/definitions/index.d.ts +27 -24
  25. package/dist/runtime/formkit/definitions/index.js +4 -1
  26. package/dist/runtime/formkit/definitions/index.mjs +4 -1
  27. package/dist/runtime/formkit/definitions/input.d.ts +3 -0
  28. package/dist/runtime/formkit/definitions/input.js +17 -2
  29. package/dist/runtime/formkit/definitions/input.mjs +17 -2
  30. package/dist/runtime/formkit/definitions/repeater.js +8 -1
  31. package/dist/runtime/formkit/definitions/repeater.mjs +8 -1
  32. package/dist/runtime/formkit/index.d.ts +13 -1
  33. package/dist/runtime/utils/dateValueConversion.d.ts +35 -0
  34. package/dist/runtime/utils/dateValueConversion.js +80 -0
  35. package/dist/runtime/utils/useFormKitInput.d.ts +1 -1
  36. package/package.json +9 -9
@@ -0,0 +1,56 @@
1
+ <script setup>
2
+ import { useFormKitInput } from "../../utils/useFormKitInput";
3
+ const props = defineProps({
4
+ context: {
5
+ type: Object,
6
+ required: true
7
+ }
8
+ });
9
+ const { handleInput, handleChange, isInvalid, styleClass, color, modelValue, validSlotNames } = useFormKitInput(props.context);
10
+ </script>
11
+
12
+ <template>
13
+ <UFileUpload
14
+ :id="context.id"
15
+ v-model="modelValue"
16
+ v-bind="{ ...context?.attrs }"
17
+ :class="styleClass"
18
+ :disabled="!!context?.disabled"
19
+ :style="context?.attrs.style"
20
+ :highlight="isInvalid || context.highlight"
21
+ :multiple="context.multiple"
22
+ :accept="context.accept"
23
+ :icon="context.icon"
24
+ :label="context.label"
25
+ :description="context.description"
26
+ :color="color"
27
+ :variant="context.variant ?? 'area'"
28
+ :size="context.size ?? 'md'"
29
+ :layout="context.layout ?? 'list'"
30
+ :position="context.position ?? 'outside'"
31
+ :dropzone="context.dropzone"
32
+ :interactive="context.interactive"
33
+ :required="context.required"
34
+ :reset="context.reset"
35
+ :file-icon="context.fileIcon"
36
+ :file-image="context.fileImage"
37
+ :file-delete="context.fileDelete"
38
+ :file-delete-icon="context.fileDeleteIcon"
39
+ :preview="context.preview"
40
+ :name="context.name"
41
+ :ui="context.ui"
42
+ @change="handleChange"
43
+ @update:model-value="handleInput"
44
+ >
45
+ <template
46
+ v-for="slotName in validSlotNames"
47
+ :key="slotName"
48
+ #[slotName]="slotProps"
49
+ >
50
+ <component
51
+ :is="context?.slots[slotName]"
52
+ v-bind="{ ...context, ...slotProps }"
53
+ />
54
+ </template>
55
+ </UFileUpload>
56
+ </template>
@@ -0,0 +1,39 @@
1
+ import type { FormKitFrameworkContext } from '@formkit/core';
2
+ import type { PropType } from 'vue';
3
+ export interface FormKitFileUploadProps {
4
+ multiple?: boolean;
5
+ accept?: string;
6
+ icon?: string | false;
7
+ label?: string;
8
+ description?: string;
9
+ color?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | 'neutral';
10
+ variant?: 'area' | 'button';
11
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
12
+ layout?: 'list' | 'grid';
13
+ position?: 'inside' | 'outside';
14
+ highlight?: boolean;
15
+ dropzone?: boolean;
16
+ interactive?: boolean;
17
+ required?: boolean;
18
+ reset?: boolean;
19
+ fileIcon?: string;
20
+ fileImage?: boolean;
21
+ fileDelete?: boolean | Record<string, unknown>;
22
+ fileDeleteIcon?: string;
23
+ preview?: boolean;
24
+ name?: string;
25
+ ui?: Record<string, unknown>;
26
+ }
27
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
28
+ context: {
29
+ type: PropType<FormKitFrameworkContext & FormKitFileUploadProps>;
30
+ required: true;
31
+ };
32
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
33
+ context: {
34
+ type: PropType<FormKitFrameworkContext & FormKitFileUploadProps>;
35
+ required: true;
36
+ };
37
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
38
+ declare const _default: typeof __VLS_export;
39
+ export default _default;
@@ -1,11 +1,13 @@
1
1
  import type { FormKitFrameworkContext } from '@formkit/core';
2
2
  import type { CalendarDate, CalendarDateTime, DateValue, ZonedDateTime } from '@internationalized/date';
3
3
  import type { PropType } from 'vue';
4
+ import type { DateValueType } from '../../utils/dateValueConversion.js';
4
5
  import type { AvatarProps } from '#ui/components/Avatar.vue';
5
6
  export interface DateRange {
6
7
  start: CalendarDate | CalendarDateTime | ZonedDateTime;
7
8
  end: CalendarDate | CalendarDateTime | ZonedDateTime;
8
9
  }
10
+ type DateLike = CalendarDate | CalendarDateTime | ZonedDateTime | Date | string;
9
11
  export interface FormKitInputDateProps {
10
12
  color?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | 'neutral';
11
13
  variant?: 'outline' | 'soft' | 'subtle' | 'ghost' | 'none';
@@ -24,15 +26,26 @@ export interface FormKitInputDateProps {
24
26
  trailingIcon?: string;
25
27
  loading?: boolean;
26
28
  loadingIcon?: string;
27
- defaultValue: CalendarDate | CalendarDateTime | ZonedDateTime | DateRange;
28
- placeholder?: CalendarDate | CalendarDateTime | ZonedDateTime;
29
- defaultPlaceholder?: CalendarDate | CalendarDateTime | ZonedDateTime;
29
+ /**
30
+ * Shape of the value read from / written to the FormKit form data.
31
+ * `'calendar'` (default) keeps the native `@internationalized/date` `DateValue`.
32
+ * `'date'` converts to/from a JS `Date`. `'iso'` converts to/from an ISO 8601 string.
33
+ */
34
+ valueType?: DateValueType;
35
+ /** Timezone used when converting to/from `'date'`/`'iso'`. Defaults to the local timezone. */
36
+ timeZone?: string;
37
+ defaultValue: DateLike | {
38
+ start: DateLike;
39
+ end: DateLike;
40
+ };
41
+ placeholder?: DateLike;
42
+ defaultPlaceholder?: DateLike;
30
43
  hourCycle?: 12 | 24;
31
44
  step?: Record<string, number>;
32
45
  granularity?: 'day' | 'hour' | 'minute' | 'second';
33
46
  hideTimeZone?: boolean;
34
- maxValue?: CalendarDate | CalendarDateTime | ZonedDateTime;
35
- minValue?: CalendarDate | CalendarDateTime | ZonedDateTime;
47
+ maxValue?: DateLike;
48
+ minValue?: DateLike;
36
49
  isDateUnavailable?: (date: DateValue) => boolean;
37
50
  ui?: Record<string, unknown>;
38
51
  }
@@ -1,12 +1,30 @@
1
1
  <script setup>
2
+ import { computed } from "vue";
2
3
  import { useFormKitInput } from "../../utils/useFormKitInput";
4
+ import { fromDateValueOrRange, toDateValue, toDateValueOrRange } from "../../utils/dateValueConversion";
3
5
  const props = defineProps({
4
6
  context: {
5
7
  type: Object,
6
8
  required: true
7
9
  }
8
10
  });
9
- const { handleInput, handleChange, isInvalid, styleClass, color, modelValue, validSlotNames } = useFormKitInput(props.context);
11
+ const { handleInput, handleChange, isInvalid, styleClass, color, validSlotNames } = useFormKitInput(props.context);
12
+ const conversionOptions = computed(() => ({
13
+ granularity: props.context.granularity,
14
+ hideTimeZone: props.context.hideTimeZone,
15
+ timeZone: props.context.timeZone
16
+ }));
17
+ const modelValue = computed({
18
+ get: () => toDateValueOrRange(props.context._value, conversionOptions.value),
19
+ set: (value) => {
20
+ props.context.node.input(fromDateValueOrRange(value, props.context.valueType ?? "calendar", props.context.timeZone));
21
+ }
22
+ });
23
+ const defaultValue = computed(() => toDateValueOrRange(props.context.defaultValue, conversionOptions.value));
24
+ const placeholder = computed(() => toDateValue(props.context.placeholder, conversionOptions.value));
25
+ const defaultPlaceholder = computed(() => toDateValue(props.context.defaultPlaceholder, conversionOptions.value));
26
+ const maxValue = computed(() => toDateValue(props.context.maxValue, conversionOptions.value));
27
+ const minValue = computed(() => toDateValue(props.context.minValue, conversionOptions.value));
10
28
  </script>
11
29
 
12
30
  <template>
@@ -14,7 +32,7 @@ const { handleInput, handleChange, isInvalid, styleClass, color, modelValue, val
14
32
  :id="context.id"
15
33
  v-model="modelValue"
16
34
  v-bind="{ ...context?.attrs }"
17
- :default-value="context.defaultValue"
35
+ :default-value="defaultValue"
18
36
  :class="styleClass"
19
37
  :disabled="!!context?.disabled"
20
38
  :readonly="context?.attrs.readonly ?? false"
@@ -23,8 +41,8 @@ const { handleInput, handleChange, isInvalid, styleClass, color, modelValue, val
23
41
  :highlight="isInvalid || context.highlight"
24
42
  :size="context.size ?? 'md'"
25
43
  :variant="context.variant ?? 'outline'"
26
- :placeholder="context.placeholder"
27
- :default-placeholder="context.defaultPlaceholder"
44
+ :placeholder="placeholder"
45
+ :default-placeholder="defaultPlaceholder"
28
46
  :autofocus="context.autofocus"
29
47
  :autofocus-delay="context.autofocusDelay"
30
48
  :fixed="context.fixed"
@@ -42,8 +60,8 @@ const { handleInput, handleChange, isInvalid, styleClass, color, modelValue, val
42
60
  :step="context.step"
43
61
  :granularity="context.granularity"
44
62
  :hide-time-zone="context.hideTimeZone"
45
- :max-value="context.maxValue"
46
- :min-value="context.minValue"
63
+ :max-value="maxValue"
64
+ :min-value="minValue"
47
65
  :is-date-unavailable="context.isDateUnavailable"
48
66
  :ui="context.ui"
49
67
  @change="handleChange"
@@ -1,11 +1,13 @@
1
1
  import type { FormKitFrameworkContext } from '@formkit/core';
2
2
  import type { CalendarDate, CalendarDateTime, DateValue, ZonedDateTime } from '@internationalized/date';
3
3
  import type { PropType } from 'vue';
4
+ import type { DateValueType } from '../../utils/dateValueConversion.js';
4
5
  import type { AvatarProps } from '#ui/components/Avatar.vue';
5
6
  export interface DateRange {
6
7
  start: CalendarDate | CalendarDateTime | ZonedDateTime;
7
8
  end: CalendarDate | CalendarDateTime | ZonedDateTime;
8
9
  }
10
+ type DateLike = CalendarDate | CalendarDateTime | ZonedDateTime | Date | string;
9
11
  export interface FormKitInputDateProps {
10
12
  color?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | 'neutral';
11
13
  variant?: 'outline' | 'soft' | 'subtle' | 'ghost' | 'none';
@@ -24,15 +26,26 @@ export interface FormKitInputDateProps {
24
26
  trailingIcon?: string;
25
27
  loading?: boolean;
26
28
  loadingIcon?: string;
27
- defaultValue: CalendarDate | CalendarDateTime | ZonedDateTime | DateRange;
28
- placeholder?: CalendarDate | CalendarDateTime | ZonedDateTime;
29
- defaultPlaceholder?: CalendarDate | CalendarDateTime | ZonedDateTime;
29
+ /**
30
+ * Shape of the value read from / written to the FormKit form data.
31
+ * `'calendar'` (default) keeps the native `@internationalized/date` `DateValue`.
32
+ * `'date'` converts to/from a JS `Date`. `'iso'` converts to/from an ISO 8601 string.
33
+ */
34
+ valueType?: DateValueType;
35
+ /** Timezone used when converting to/from `'date'`/`'iso'`. Defaults to the local timezone. */
36
+ timeZone?: string;
37
+ defaultValue: DateLike | {
38
+ start: DateLike;
39
+ end: DateLike;
40
+ };
41
+ placeholder?: DateLike;
42
+ defaultPlaceholder?: DateLike;
30
43
  hourCycle?: 12 | 24;
31
44
  step?: Record<string, number>;
32
45
  granularity?: 'day' | 'hour' | 'minute' | 'second';
33
46
  hideTimeZone?: boolean;
34
- maxValue?: CalendarDate | CalendarDateTime | ZonedDateTime;
35
- minValue?: CalendarDate | CalendarDateTime | ZonedDateTime;
47
+ maxValue?: DateLike;
48
+ minValue?: DateLike;
36
49
  isDateUnavailable?: (date: DateValue) => boolean;
37
50
  ui?: Record<string, unknown>;
38
51
  }
@@ -24,6 +24,8 @@ export interface FormKitListboxProps {
24
24
  transferRightHeaderText?: string;
25
25
  transferHeaderClass?: string;
26
26
  transferAll?: boolean;
27
+ transferSortIcons?: boolean;
28
+ transferItemDraggable?: boolean;
27
29
  orientation?: 'horizontal' | 'vertical';
28
30
  selectionBehavior?: 'toggle' | 'replace';
29
31
  disabled?: boolean;
@@ -1,5 +1,5 @@
1
1
  <script setup>
2
- import { computed, ref, watch } from "vue";
2
+ import { computed, nextTick, ref, watch } from "vue";
3
3
  import { useFormKitInput } from "../../utils/useFormKitInput";
4
4
  const props = defineProps({
5
5
  context: {
@@ -16,9 +16,172 @@ const transferHeaderClass = computed(() => {
16
16
  return props.context.transferHeaderClass ? props.context.transferHeaderClass : baseClass;
17
17
  });
18
18
  const sourceItems = computed(() => {
19
+ if (props.context.displayMode !== "transfer" || !Array.isArray(targetItems.value))
20
+ return [];
19
21
  const valueKey = props.context?.valueKey || "value";
20
22
  return items.value.filter((item) => !targetItems.value.some((t) => t[valueKey] === item[valueKey]));
21
23
  });
24
+ const transferSlotNames = computed(() => validSlotNames.value.filter((name) => name !== "item-trailing"));
25
+ const sourceContainerRef = ref();
26
+ const targetContainerRef = ref();
27
+ const sourceDragActive = ref(false);
28
+ const dragOverTargetIndex = ref(null);
29
+ const dragOverPosition = ref(null);
30
+ const targetDisplayItems = computed(() => {
31
+ if (props.context.displayMode !== "transfer" || !Array.isArray(targetItems.value))
32
+ return [];
33
+ return targetItems.value.map((item, index) => {
34
+ if (index !== dragOverTargetIndex.value || !dragOverPosition.value)
35
+ return item;
36
+ const insertionClass = dragOverPosition.value === "before" ? "border-t-2 border-primary" : "border-b-2 border-primary";
37
+ return { ...item, class: [item.class, insertionClass] };
38
+ });
39
+ });
40
+ function applyItemDraggable(container, enabled) {
41
+ if (!container)
42
+ return;
43
+ container.querySelectorAll('[data-slot="item"]').forEach((el) => {
44
+ el.draggable = enabled;
45
+ });
46
+ }
47
+ watch(
48
+ [sourceItems, sourceContainerRef, () => props.context.transferItemDraggable],
49
+ () => nextTick(() => applyItemDraggable(sourceContainerRef.value, !!props.context.transferItemDraggable)),
50
+ { immediate: true }
51
+ );
52
+ watch(
53
+ [targetDisplayItems, targetContainerRef, () => props.context.transferItemDraggable],
54
+ () => nextTick(() => applyItemDraggable(targetContainerRef.value, !!props.context.transferItemDraggable)),
55
+ { immediate: true }
56
+ );
57
+ function findItemIndex(container, target) {
58
+ const itemEl = target?.closest('[data-slot="item"]');
59
+ if (!itemEl || !container)
60
+ return -1;
61
+ return Array.from(container.querySelectorAll('[data-slot="item"]')).indexOf(itemEl);
62
+ }
63
+ function findDropPosition(target, clientY) {
64
+ const itemEl = target?.closest('[data-slot="item"]');
65
+ if (!itemEl)
66
+ return "after";
67
+ const rect = itemEl.getBoundingClientRect();
68
+ return clientY < rect.top + rect.height / 2 ? "before" : "after";
69
+ }
70
+ function moveElement(array, fromIndex, toIndex) {
71
+ const result = [...array];
72
+ const [element] = result.splice(fromIndex, 1);
73
+ if (element !== void 0)
74
+ result.splice(toIndex, 0, element);
75
+ return result;
76
+ }
77
+ function onSourceDragStart(event) {
78
+ const index = findItemIndex(sourceContainerRef.value, event.target);
79
+ if (index === -1)
80
+ return;
81
+ event.dataTransfer?.setData("text/plain", JSON.stringify({ origin: "source", index }));
82
+ if (event.dataTransfer)
83
+ event.dataTransfer.effectAllowed = "copyMove";
84
+ }
85
+ function onSourceDragEnter(event) {
86
+ event.preventDefault();
87
+ }
88
+ function onSourceDragOver(event) {
89
+ event.preventDefault();
90
+ if (event.target?.tagName === "INPUT") {
91
+ if (event.dataTransfer)
92
+ event.dataTransfer.dropEffect = "none";
93
+ sourceDragActive.value = false;
94
+ return;
95
+ }
96
+ sourceDragActive.value = true;
97
+ if (event.dataTransfer)
98
+ event.dataTransfer.dropEffect = "move";
99
+ }
100
+ function onSourceDrop(event) {
101
+ event.preventDefault();
102
+ sourceDragActive.value = false;
103
+ if (event.target?.tagName === "INPUT")
104
+ return;
105
+ let payload;
106
+ try {
107
+ payload = JSON.parse(event.dataTransfer?.getData("text/plain") || "");
108
+ } catch {
109
+ return;
110
+ }
111
+ if (!payload || payload.origin !== "target" || typeof payload.index !== "number")
112
+ return;
113
+ const draggedItem = targetItems.value[payload.index];
114
+ if (!draggedItem)
115
+ return;
116
+ const valueKey = props.context?.valueKey || "value";
117
+ targetItems.value = targetItems.value.filter((item) => item[valueKey] !== draggedItem[valueKey]);
118
+ }
119
+ function onTargetDragStart(event) {
120
+ const index = findItemIndex(targetContainerRef.value, event.target);
121
+ if (index === -1)
122
+ return;
123
+ event.dataTransfer?.setData("text/plain", JSON.stringify({ origin: "target", index }));
124
+ if (event.dataTransfer)
125
+ event.dataTransfer.effectAllowed = "copyMove";
126
+ }
127
+ function onTargetDragEnter(event) {
128
+ event.preventDefault();
129
+ }
130
+ function onTargetDragOver(event) {
131
+ event.preventDefault();
132
+ const index = findItemIndex(targetContainerRef.value, event.target);
133
+ if (index === -1) {
134
+ dragOverTargetIndex.value = null;
135
+ dragOverPosition.value = null;
136
+ } else {
137
+ dragOverTargetIndex.value = index;
138
+ dragOverPosition.value = findDropPosition(event.target, event.clientY);
139
+ }
140
+ }
141
+ function onTargetDrop(event) {
142
+ event.preventDefault();
143
+ const hoveredIndex = dragOverTargetIndex.value;
144
+ const position = dragOverPosition.value;
145
+ dragOverTargetIndex.value = null;
146
+ dragOverPosition.value = null;
147
+ let payload;
148
+ try {
149
+ payload = JSON.parse(event.dataTransfer?.getData("text/plain") || "");
150
+ } catch {
151
+ return;
152
+ }
153
+ if (!payload || typeof payload.index !== "number")
154
+ return;
155
+ const dropIndex = hoveredIndex === null ? targetItems.value.length : position === "after" ? hoveredIndex + 1 : hoveredIndex;
156
+ if (payload.origin === "source") {
157
+ const draggedItem = sourceItems.value[payload.index];
158
+ if (!draggedItem)
159
+ return;
160
+ const newItems = [...targetItems.value];
161
+ newItems.splice(dropIndex, 0, draggedItem);
162
+ targetItems.value = newItems;
163
+ } else {
164
+ if (payload.index === dropIndex)
165
+ return;
166
+ const adjustedIndex = dropIndex > payload.index ? dropIndex - 1 : dropIndex;
167
+ targetItems.value = moveElement(targetItems.value, payload.index, adjustedIndex);
168
+ }
169
+ }
170
+ function onDragEnd() {
171
+ dragOverTargetIndex.value = null;
172
+ dragOverPosition.value = null;
173
+ sourceDragActive.value = false;
174
+ }
175
+ function moveTargetItemUp(index) {
176
+ if (index <= 0)
177
+ return;
178
+ targetItems.value = moveElement(targetItems.value, index, index - 1);
179
+ }
180
+ function moveTargetItemDown(index) {
181
+ if (index >= targetItems.value.length - 1)
182
+ return;
183
+ targetItems.value = moveElement(targetItems.value, index, index + 1);
184
+ }
22
185
  function transferSelected() {
23
186
  targetItems.value = [...targetItems.value, ...sourceSelection.value];
24
187
  sourceSelection.value = [];
@@ -93,7 +256,16 @@ watch(targetItems, (newVal) => {
93
256
  v-if="context.displayMode === 'transfer'"
94
257
  class="flex items-stretch gap-4 w-full"
95
258
  >
96
- <div class="flex flex-col flex-1 gap-1">
259
+ <div
260
+ ref="sourceContainerRef"
261
+ class="flex flex-col flex-1 gap-1 rounded-lg transition-colors"
262
+ :class="{ 'ring-2 ring-inset ring-primary': sourceDragActive }"
263
+ @dragstart="onSourceDragStart"
264
+ @dragenter.capture="onSourceDragEnter"
265
+ @dragover.capture="onSourceDragOver"
266
+ @drop.capture="onSourceDrop"
267
+ @dragend="onDragEnd"
268
+ >
97
269
  <span
98
270
  v-if="context.transferLeftHeaderText"
99
271
  :class="transferHeaderClass"
@@ -133,7 +305,7 @@ watch(targetItems, (newVal) => {
133
305
  @update:model-value="handleInput"
134
306
  >
135
307
  <template
136
- v-for="slotName in validSlotNames"
308
+ v-for="slotName in transferSlotNames"
137
309
  :key="slotName"
138
310
  #[slotName]="slotProps"
139
311
  >
@@ -142,6 +314,17 @@ watch(targetItems, (newVal) => {
142
314
  v-bind="{ ...context, ...slotProps }"
143
315
  />
144
316
  </template>
317
+ <template
318
+ v-if="!context.disabled"
319
+ #item-trailing
320
+ >
321
+ <UIcon
322
+ name="i-lucide-grip-vertical"
323
+ draggable="true"
324
+ class="cursor-grab text-muted"
325
+ @click.stop
326
+ />
327
+ </template>
145
328
  </UListbox>
146
329
  </div>
147
330
  <div class="flex flex-col items-center justify-center gap-1">
@@ -176,7 +359,15 @@ watch(targetItems, (newVal) => {
176
359
  @click="removeAll"
177
360
  />
178
361
  </div>
179
- <div class="flex flex-col flex-1 gap-1">
362
+ <div
363
+ ref="targetContainerRef"
364
+ class="flex flex-col flex-1 gap-1"
365
+ @dragstart="onTargetDragStart"
366
+ @dragenter.capture.prevent="onTargetDragEnter"
367
+ @dragover.capture.prevent="onTargetDragOver"
368
+ @drop.capture="onTargetDrop"
369
+ @dragend="onDragEnd"
370
+ >
180
371
  <span
181
372
  v-if="context.transferRightHeaderText"
182
373
  :class="transferHeaderClass"
@@ -189,7 +380,7 @@ watch(targetItems, (newVal) => {
189
380
  :style="context?.attrs.style"
190
381
  :as="context.as"
191
382
  :size="context.size ?? 'md'"
192
- :items="targetItems"
383
+ :items="targetDisplayItems"
193
384
  :by="context.by"
194
385
  :orientation="context.orientation"
195
386
  :selection-behavior="context.selectionBehavior"
@@ -216,7 +407,7 @@ watch(targetItems, (newVal) => {
216
407
  @update:model-value="handleInput"
217
408
  >
218
409
  <template
219
- v-for="slotName in validSlotNames"
410
+ v-for="slotName in transferSlotNames"
220
411
  :key="slotName"
221
412
  #[slotName]="slotProps"
222
413
  >
@@ -225,6 +416,35 @@ watch(targetItems, (newVal) => {
225
416
  v-bind="{ ...context, ...slotProps }"
226
417
  />
227
418
  </template>
419
+ <template
420
+ v-if="!context.disabled"
421
+ #item-trailing="{ index }"
422
+ >
423
+ <UButton
424
+ v-if="context.transferSortIcons"
425
+ icon="i-lucide-chevron-up"
426
+ color="neutral"
427
+ variant="ghost"
428
+ size="xs"
429
+ :disabled="index === 0"
430
+ @click.stop="moveTargetItemUp(index)"
431
+ />
432
+ <UButton
433
+ v-if="context.transferSortIcons"
434
+ icon="i-lucide-chevron-down"
435
+ color="neutral"
436
+ variant="ghost"
437
+ size="xs"
438
+ :disabled="index === targetItems.length - 1"
439
+ @click.stop="moveTargetItemDown(index)"
440
+ />
441
+ <UIcon
442
+ name="i-lucide-grip-vertical"
443
+ draggable="true"
444
+ class="cursor-grab text-muted"
445
+ @click.stop
446
+ />
447
+ </template>
228
448
  </UListbox>
229
449
  </div>
230
450
  </div>
@@ -24,6 +24,8 @@ export interface FormKitListboxProps {
24
24
  transferRightHeaderText?: string;
25
25
  transferHeaderClass?: string;
26
26
  transferAll?: boolean;
27
+ transferSortIcons?: boolean;
28
+ transferItemDraggable?: boolean;
27
29
  orientation?: 'horizontal' | 'vertical';
28
30
  selectionBehavior?: 'toggle' | 'replace';
29
31
  disabled?: boolean;
@@ -1,30 +1,33 @@
1
1
  export * from './input.js';
2
2
  export * from './output.js';
3
3
  export declare const nuxtUIInputs: {
4
- nuxtUICheckbox: FormKitTypeDefinition;
5
- nuxtUICheckboxGroup: FormKitTypeDefinition;
6
- nuxtUIColorPicker: FormKitTypeDefinition;
7
- nuxtUIInput: FormKitTypeDefinition;
8
- nuxtUIInputDate: FormKitTypeDefinition;
9
- nuxtUIInputMenu: FormKitTypeDefinition;
10
- nuxtUIInputNumber: FormKitTypeDefinition;
11
- nuxtUIInputTags: FormKitTypeDefinition;
12
- nuxtUIInputTime: FormKitTypeDefinition;
13
- nuxtUIListbox: FormKitTypeDefinition;
14
- nuxtUIPinInput: FormKitTypeDefinition;
15
- nuxtUIRadioGroup: FormKitTypeDefinition;
16
- nuxtUISelect: FormKitTypeDefinition;
17
- nuxtUISelectMenu: FormKitTypeDefinition;
18
- nuxtUISlider: FormKitTypeDefinition;
19
- nuxtUISwitch: FormKitTypeDefinition;
20
- nuxtUITextarea: FormKitTypeDefinition;
21
- nuxtUIRepeater: FormKitTypeDefinition;
4
+ nuxtUICalendar: import("@formkit/core").FormKitTypeDefinition;
5
+ nuxtUICheckbox: import("@formkit/core").FormKitTypeDefinition;
6
+ nuxtUICheckboxGroup: import("@formkit/core").FormKitTypeDefinition;
7
+ nuxtUIColorPicker: import("@formkit/core").FormKitTypeDefinition;
8
+ nuxtUIEditor: import("@formkit/core").FormKitTypeDefinition;
9
+ nuxtUIFileUpload: import("@formkit/core").FormKitTypeDefinition;
10
+ nuxtUIInput: import("@formkit/core").FormKitTypeDefinition;
11
+ nuxtUIInputDate: import("@formkit/core").FormKitTypeDefinition;
12
+ nuxtUIInputMenu: import("@formkit/core").FormKitTypeDefinition;
13
+ nuxtUIInputNumber: import("@formkit/core").FormKitTypeDefinition;
14
+ nuxtUIInputTags: import("@formkit/core").FormKitTypeDefinition;
15
+ nuxtUIInputTime: import("@formkit/core").FormKitTypeDefinition;
16
+ nuxtUIListbox: import("@formkit/core").FormKitTypeDefinition;
17
+ nuxtUIPinInput: import("@formkit/core").FormKitTypeDefinition;
18
+ nuxtUIRadioGroup: import("@formkit/core").FormKitTypeDefinition;
19
+ nuxtUISelect: import("@formkit/core").FormKitTypeDefinition;
20
+ nuxtUISelectMenu: import("@formkit/core").FormKitTypeDefinition;
21
+ nuxtUISlider: import("@formkit/core").FormKitTypeDefinition;
22
+ nuxtUISwitch: import("@formkit/core").FormKitTypeDefinition;
23
+ nuxtUITextarea: import("@formkit/core").FormKitTypeDefinition;
24
+ nuxtUIRepeater: import("@formkit/core").FormKitTypeDefinition;
22
25
  };
23
26
  export declare const nuxtUIOutputs: {
24
- nuxtUIOutputBoolean: FormKitTypeDefinition;
25
- nuxtUIOutputDate: FormKitTypeDefinition;
26
- nuxtUIOutputLink: FormKitTypeDefinition;
27
- nuxtUIOutputList: FormKitTypeDefinition;
28
- nuxtUIOutputNumber: FormKitTypeDefinition;
29
- nuxtUIOutputText: FormKitTypeDefinition;
27
+ nuxtUIOutputBoolean: import("@formkit/core").FormKitTypeDefinition;
28
+ nuxtUIOutputDate: import("@formkit/core").FormKitTypeDefinition;
29
+ nuxtUIOutputLink: import("@formkit/core").FormKitTypeDefinition;
30
+ nuxtUIOutputList: import("@formkit/core").FormKitTypeDefinition;
31
+ nuxtUIOutputNumber: import("@formkit/core").FormKitTypeDefinition;
32
+ nuxtUIOutputText: import("@formkit/core").FormKitTypeDefinition;
30
33
  };
@@ -1,12 +1,15 @@
1
- import { nuxtUICheckboxDefinition, nuxtUICheckboxGroupDefinition, nuxtUIColorPickerDefinition, nuxtUIInputDateDefinition, nuxtUIInputDefinition, nuxtUIInputMenuDefinition, nuxtUIInputNumberDefinition, nuxtUIInputTagsDefinition, nuxtUIInputTimeDefinition, nuxtUIListboxDefinition, nuxtUIPinInputDefinition, nuxtUIRadioGroupDefinition, nuxtUISelectDefinition, nuxtUISelectMenuDefinition, nuxtUISliderDefinition, nuxtUISwitchDefinition, nuxtUITextareaDefinition } from "./input.js";
1
+ import { nuxtUICalendarDefinition, nuxtUICheckboxDefinition, nuxtUICheckboxGroupDefinition, nuxtUIColorPickerDefinition, nuxtUIEditorDefinition, nuxtUIFileUploadDefinition, nuxtUIInputDateDefinition, nuxtUIInputDefinition, nuxtUIInputMenuDefinition, nuxtUIInputNumberDefinition, nuxtUIInputTagsDefinition, nuxtUIInputTimeDefinition, nuxtUIListboxDefinition, nuxtUIPinInputDefinition, nuxtUIRadioGroupDefinition, nuxtUISelectDefinition, nuxtUISelectMenuDefinition, nuxtUISliderDefinition, nuxtUISwitchDefinition, nuxtUITextareaDefinition } from "./input.js";
2
2
  import { nuxtUIOutputBooleanDefinition, nuxtUIOutputDateDefinition, nuxtUIOutputLinkDefinition, nuxtUIOutputListDefinition, nuxtUIOutputNumberDefinition, nuxtUIOutputTextDefinition } from "./output.js";
3
3
  import { nuxtUIRepeaterDefinition } from "./repeater.js";
4
4
  export * from "./input.js";
5
5
  export * from "./output.js";
6
6
  export const nuxtUIInputs = {
7
+ nuxtUICalendar: nuxtUICalendarDefinition,
7
8
  nuxtUICheckbox: nuxtUICheckboxDefinition,
8
9
  nuxtUICheckboxGroup: nuxtUICheckboxGroupDefinition,
9
10
  nuxtUIColorPicker: nuxtUIColorPickerDefinition,
11
+ nuxtUIEditor: nuxtUIEditorDefinition,
12
+ nuxtUIFileUpload: nuxtUIFileUploadDefinition,
10
13
  nuxtUIInput: nuxtUIInputDefinition,
11
14
  nuxtUIInputDate: nuxtUIInputDateDefinition,
12
15
  nuxtUIInputMenu: nuxtUIInputMenuDefinition,
@@ -1,12 +1,15 @@
1
- import { nuxtUICheckboxDefinition, nuxtUICheckboxGroupDefinition, nuxtUIColorPickerDefinition, nuxtUIInputDateDefinition, nuxtUIInputDefinition, nuxtUIInputMenuDefinition, nuxtUIInputNumberDefinition, nuxtUIInputTagsDefinition, nuxtUIInputTimeDefinition, nuxtUIListboxDefinition, nuxtUIPinInputDefinition, nuxtUIRadioGroupDefinition, nuxtUISelectDefinition, nuxtUISelectMenuDefinition, nuxtUISliderDefinition, nuxtUISwitchDefinition, nuxtUITextareaDefinition } from "./input.mjs";
1
+ import { nuxtUICalendarDefinition, nuxtUICheckboxDefinition, nuxtUICheckboxGroupDefinition, nuxtUIColorPickerDefinition, nuxtUIEditorDefinition, nuxtUIFileUploadDefinition, nuxtUIInputDateDefinition, nuxtUIInputDefinition, nuxtUIInputMenuDefinition, nuxtUIInputNumberDefinition, nuxtUIInputTagsDefinition, nuxtUIInputTimeDefinition, nuxtUIListboxDefinition, nuxtUIPinInputDefinition, nuxtUIRadioGroupDefinition, nuxtUISelectDefinition, nuxtUISelectMenuDefinition, nuxtUISliderDefinition, nuxtUISwitchDefinition, nuxtUITextareaDefinition } from "./input.mjs";
2
2
  import { nuxtUIOutputBooleanDefinition, nuxtUIOutputDateDefinition, nuxtUIOutputLinkDefinition, nuxtUIOutputListDefinition, nuxtUIOutputNumberDefinition, nuxtUIOutputTextDefinition } from "./output.mjs";
3
3
  import { nuxtUIRepeaterDefinition } from "./repeater.mjs";
4
4
  export * from "./input.mjs";
5
5
  export * from "./output.mjs";
6
6
  export const nuxtUIInputs = {
7
+ nuxtUICalendar: nuxtUICalendarDefinition,
7
8
  nuxtUICheckbox: nuxtUICheckboxDefinition,
8
9
  nuxtUICheckboxGroup: nuxtUICheckboxGroupDefinition,
9
10
  nuxtUIColorPicker: nuxtUIColorPickerDefinition,
11
+ nuxtUIEditor: nuxtUIEditorDefinition,
12
+ nuxtUIFileUpload: nuxtUIFileUploadDefinition,
10
13
  nuxtUIInput: nuxtUIInputDefinition,
11
14
  nuxtUIInputDate: nuxtUIInputDateDefinition,
12
15
  nuxtUIInputMenu: nuxtUIInputMenuDefinition,