nuxt-ui-tools 1.6.5 → 1.8.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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +20 -0
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.vue +12 -0
- package/dist/runtime/dashboard/components/dashboard-details.d.vue.ts +38 -0
- package/dist/runtime/dashboard/components/dashboard-details.vue +194 -0
- package/dist/runtime/dashboard/components/dashboard-details.vue.d.ts +38 -0
- package/dist/runtime/dashboard/components/dashboard-page.d.vue.ts +8 -0
- package/dist/runtime/dashboard/components/dashboard-page.vue +20 -3
- package/dist/runtime/dashboard/components/dashboard-page.vue.d.ts +8 -0
- package/dist/runtime/dashboard/components/dashboard-view-tabs.d.vue.ts +2 -5
- package/dist/runtime/dashboard/components/dashboard-view-tabs.vue +5 -1
- package/dist/runtime/dashboard/components/dashboard-view-tabs.vue.d.ts +2 -5
- package/dist/runtime/dashboard/composables/use-dashboard-api.js +4 -1
- package/dist/runtime/dashboard/composables/use-dashboard-views.d.ts +4 -1
- package/dist/runtime/dashboard/composables/use-dashboard-views.js +15 -1
- package/dist/runtime/dashboard/schema/index.d.ts +15 -1
- package/dist/runtime/dashboard/types/blocks.d.ts +27 -1
- package/dist/runtime/dashboard/types/runtime.d.ts +3 -0
- package/dist/runtime/dashboard/types/schema.d.ts +23 -4
- package/dist/runtime/dashboard/types/ui.d.ts +27 -0
- package/dist/runtime/dashboard/utils/schema.js +1 -0
- package/dist/runtime/file-preview/components/provider/file-preview-host.d.vue.ts +7 -0
- package/dist/runtime/file-preview/components/provider/file-preview-host.vue +147 -0
- package/dist/runtime/file-preview/components/provider/file-preview-host.vue.d.ts +7 -0
- package/dist/runtime/file-preview/components/provider/file-preview-provider.d.vue.ts +21 -0
- package/dist/runtime/file-preview/components/provider/file-preview-provider.vue +23 -0
- package/dist/runtime/file-preview/components/provider/file-preview-provider.vue.d.ts +21 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.vue +83 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/csv-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/csv-renderer.vue +116 -0
- package/dist/runtime/file-preview/components/renderers/csv-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/fallback-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/fallback-renderer.vue +41 -0
- package/dist/runtime/file-preview/components/renderers/fallback-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-code.d.vue.ts +9 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-code.vue +83 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-code.vue.d.ts +9 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.d.vue.ts +3 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.vue +14 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.vue.d.ts +3 -0
- package/dist/runtime/file-preview/components/renderers/image-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/image-renderer.vue +182 -0
- package/dist/runtime/file-preview/components/renderers/image-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/markdown-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/markdown-renderer.vue +78 -0
- package/dist/runtime/file-preview/components/renderers/markdown-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/media-controls.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/media-controls.vue +145 -0
- package/dist/runtime/file-preview/components/renderers/media-controls.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/pdf-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/pdf-renderer.vue +58 -0
- package/dist/runtime/file-preview/components/renderers/pdf-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/text-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/text-renderer.vue +41 -0
- package/dist/runtime/file-preview/components/renderers/text-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/video-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/video-renderer.vue +110 -0
- package/dist/runtime/file-preview/components/renderers/video-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/shell/file-preview-button.d.vue.ts +16 -0
- package/dist/runtime/file-preview/components/shell/file-preview-button.vue +40 -0
- package/dist/runtime/file-preview/components/shell/file-preview-button.vue.d.ts +16 -0
- package/dist/runtime/{form/components/renderer/form-field-error.d.vue.ts → file-preview/components/shell/file-preview-details.d.vue.ts} +4 -2
- package/dist/runtime/file-preview/components/shell/file-preview-details.vue +25 -0
- package/dist/runtime/{form/components/renderer/form-field-error.vue.d.ts → file-preview/components/shell/file-preview-details.vue.d.ts} +4 -2
- package/dist/runtime/file-preview/components/shell/file-preview-header.d.vue.ts +44 -0
- package/dist/runtime/file-preview/components/shell/file-preview-header.vue +163 -0
- package/dist/runtime/file-preview/components/shell/file-preview-header.vue.d.ts +44 -0
- package/dist/runtime/file-preview/components/shell/file-preview-message.d.vue.ts +21 -0
- package/dist/runtime/file-preview/components/shell/file-preview-message.vue +49 -0
- package/dist/runtime/file-preview/components/shell/file-preview-message.vue.d.ts +21 -0
- package/dist/runtime/file-preview/components/shell/file-preview-shell.d.vue.ts +17 -0
- package/dist/runtime/file-preview/components/shell/file-preview-shell.vue +280 -0
- package/dist/runtime/file-preview/components/shell/file-preview-shell.vue.d.ts +17 -0
- package/dist/runtime/file-preview/components/shell/file-preview-stage.d.vue.ts +37 -0
- package/dist/runtime/file-preview/components/shell/file-preview-stage.vue +173 -0
- package/dist/runtime/file-preview/components/shell/file-preview-stage.vue.d.ts +37 -0
- package/dist/runtime/file-preview/components/shell/file-preview-thumb.d.vue.ts +14 -0
- package/dist/runtime/file-preview/components/shell/file-preview-thumb.vue +69 -0
- package/dist/runtime/file-preview/components/shell/file-preview-thumb.vue.d.ts +14 -0
- package/dist/runtime/file-preview/components/shell/file-preview-tools.d.vue.ts +13 -0
- package/dist/runtime/file-preview/components/shell/file-preview-tools.vue +10 -0
- package/dist/runtime/file-preview/components/shell/file-preview-tools.vue.d.ts +13 -0
- package/dist/runtime/file-preview/composables/use-file-preview-api.d.ts +21 -0
- package/dist/runtime/file-preview/composables/use-file-preview-api.js +83 -0
- package/dist/runtime/file-preview/composables/use-file-preview-shell.d.ts +13 -0
- package/dist/runtime/file-preview/composables/use-file-preview-shell.js +30 -0
- package/dist/runtime/file-preview/composables/use-file-preview-source.d.ts +17 -0
- package/dist/runtime/file-preview/composables/use-file-preview-source.js +51 -0
- package/dist/runtime/file-preview/composables/use-file-preview-text.d.ts +14 -0
- package/dist/runtime/file-preview/composables/use-file-preview-text.js +79 -0
- package/dist/runtime/file-preview/index.d.ts +5 -0
- package/dist/runtime/file-preview/index.js +14 -0
- package/dist/runtime/file-preview/plugins/file-preview-api.d.ts +11 -0
- package/dist/runtime/file-preview/plugins/file-preview-api.js +10 -0
- package/dist/runtime/file-preview/types/api.d.ts +74 -0
- package/dist/runtime/file-preview/types/api.js +0 -0
- package/dist/runtime/file-preview/types/file.d.ts +77 -0
- package/dist/runtime/file-preview/types/file.js +0 -0
- package/dist/runtime/file-preview/types/index.d.ts +4 -0
- package/dist/runtime/file-preview/types/index.js +0 -0
- package/dist/runtime/file-preview/types/renderer.d.ts +49 -0
- package/dist/runtime/file-preview/types/renderer.js +0 -0
- package/dist/runtime/file-preview/types/shell.d.ts +12 -0
- package/dist/runtime/file-preview/types/shell.js +0 -0
- package/dist/runtime/file-preview/utils/container.d.ts +4 -0
- package/dist/runtime/file-preview/utils/container.js +10 -0
- package/dist/runtime/file-preview/utils/csv.d.ts +17 -0
- package/dist/runtime/file-preview/utils/csv.js +53 -0
- package/dist/runtime/file-preview/utils/download.d.ts +8 -0
- package/dist/runtime/file-preview/utils/download.js +40 -0
- package/dist/runtime/file-preview/utils/files.d.ts +7 -0
- package/dist/runtime/file-preview/utils/files.js +53 -0
- package/dist/runtime/file-preview/utils/format.d.ts +13 -0
- package/dist/runtime/file-preview/utils/format.js +40 -0
- package/dist/runtime/file-preview/utils/instance.d.ts +9 -0
- package/dist/runtime/file-preview/utils/instance.js +57 -0
- package/dist/runtime/file-preview/utils/renderers.d.ts +36 -0
- package/dist/runtime/file-preview/utils/renderers.js +247 -0
- package/dist/runtime/form/components/page/form-page-header.d.vue.ts +3 -1
- package/dist/runtime/form/components/page/form-page-header.vue +4 -4
- package/dist/runtime/form/components/page/form-page-header.vue.d.ts +3 -1
- package/dist/runtime/form/components/page/form-page-navigation.vue +2 -1
- package/dist/runtime/form/components/page/form-page-section.vue +15 -7
- package/dist/runtime/form/components/page/form-page.vue +0 -1
- package/dist/runtime/form/components/utils/form-file-preview.vue +5 -38
- package/dist/runtime/form/composables/use-field-control.d.ts +1 -1
- package/dist/runtime/form/composables/use-form-api.d.ts +8 -0
- package/dist/runtime/form/composables/use-form-api.js +4 -4
- package/dist/runtime/form/composables/use-form-page.js +3 -1
- package/dist/runtime/form/composables/use-form-runtime.js +3 -0
- package/dist/runtime/form/composables/use-form-upload-registry.d.ts +2 -0
- package/dist/runtime/form/composables/use-form-upload-registry.js +11 -6
- package/dist/runtime/form/composables/use-form-validation.js +5 -1
- package/dist/runtime/form/fields/array-list/use-array-items.d.ts +1 -1
- package/dist/runtime/form/fields/array-table/array-table-cell.d.vue.ts +15 -0
- package/dist/runtime/form/fields/array-table/array-table-cell.vue +58 -0
- package/dist/runtime/form/fields/array-table/array-table-cell.vue.d.ts +15 -0
- package/dist/runtime/form/fields/array-table/component.vue +42 -26
- package/dist/runtime/form/fields/choice-card/use-choice-card.js +4 -1
- package/dist/runtime/form/fields/date/types.d.ts +6 -1
- package/dist/runtime/form/fields/date-family/component.vue +7 -1
- package/dist/runtime/form/fields/date-family/utils.d.ts +8 -1
- package/dist/runtime/form/fields/date-family/utils.js +10 -2
- package/dist/runtime/form/fields/hidden/types.d.ts +8 -1
- package/dist/runtime/form/fields/upload/component.vue +55 -176
- package/dist/runtime/form/fields/upload/types.d.ts +68 -3
- package/dist/runtime/form/fields/upload/upload-item.d.vue.ts +24 -0
- package/dist/runtime/form/fields/upload/upload-item.vue +158 -0
- package/dist/runtime/form/fields/upload/upload-item.vue.d.ts +24 -0
- package/dist/runtime/form/fields/upload/use-upload-items.d.ts +44 -0
- package/dist/runtime/form/fields/upload/use-upload-items.js +317 -0
- package/dist/runtime/form/plugins/form-api.d.ts +10 -0
- package/dist/runtime/form/plugins/form-api.js +10 -0
- package/dist/runtime/form/types/callbacks.d.ts +8 -2
- package/dist/runtime/form/types/controller.d.ts +6 -3
- package/dist/runtime/form/types/field-base.d.ts +5 -5
- package/dist/runtime/form/types/field-output-utils.d.ts +12 -1
- package/dist/runtime/form/types/field-output.d.ts +1 -1
- package/dist/runtime/form/types/field.d.ts +3 -2
- package/dist/runtime/form/types/form-api.d.ts +4 -2
- package/dist/runtime/form/types/options-runtime.d.ts +8 -4
- package/dist/runtime/form/types/options.d.ts +11 -11
- package/dist/runtime/form/types/output.d.ts +33 -10
- package/dist/runtime/form/types/page.d.ts +7 -3
- package/dist/runtime/form/types/transform.d.ts +5 -2
- package/dist/runtime/form/types/ui.d.ts +7 -3
- package/dist/runtime/form/types/upload-runtime.d.ts +4 -0
- package/dist/runtime/form/utils/page.js +24 -5
- package/dist/runtime/form/utils/state.js +14 -11
- package/dist/runtime/i18n/locales/en.js +108 -1
- package/dist/runtime/i18n/locales/fr.js +108 -1
- package/dist/runtime/i18n/types.d.ts +127 -0
- package/dist/runtime/query-state/composables.d.ts +2 -2
- package/dist/runtime/shared/styles/tokens.css +1 -1
- package/dist/runtime/shared/types/utils.d.ts +12 -4
- package/dist/runtime/shared/utils/file.d.ts +11 -0
- package/dist/runtime/shared/utils/file.js +50 -0
- package/dist/runtime/table/components/table/table-renderer.vue +23 -3
- package/dist/runtime/table/components/utils/search-query-input.d.vue.ts +5 -1
- package/dist/runtime/table/components/utils/search-query-input.vue +17 -3
- package/dist/runtime/table/components/utils/search-query-input.vue.d.ts +5 -1
- package/package.json +56 -29
- package/dist/runtime/form/components/renderer/form-field-error.vue +0 -16
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FormField } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* One editable cell of an array table. An invalid cell highlights its control through the Nuxt UI
|
|
4
|
+
* form field context and shows its message in a tooltip on hover, kept open while the cell has
|
|
5
|
+
* focus, instead of pushing text under the control. Only invalid cells mount a tooltip, so large
|
|
6
|
+
* tables stay cheap to render. The cell positions the visually hidden message, which would
|
|
7
|
+
* otherwise escape the table's scroll containers and stretch the document.
|
|
8
|
+
*/
|
|
9
|
+
type __VLS_Props = {
|
|
10
|
+
field: FormField;
|
|
11
|
+
itemPath: readonly string[];
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
import UButton from "@nuxt/ui/components/Button.vue";
|
|
3
3
|
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
4
4
|
import { useScrollShadow } from "@nuxt/ui/composables/useScrollShadow";
|
|
5
|
+
import { useElementSize } from "@vueuse/core";
|
|
5
6
|
import { computed, defineAsyncComponent, ref } from "vue";
|
|
6
|
-
import FormFieldError from "../../components/renderer/form-field-error.vue";
|
|
7
|
-
import FormFieldRenderer from "../../components/renderer/form-field-renderer.vue";
|
|
8
7
|
import { useResolvedFieldProps } from "../../composables/use-field-control";
|
|
9
8
|
import { isRecord } from "../../utils/path";
|
|
10
9
|
import { isNumber, isString } from "../../utils/predicate";
|
|
11
10
|
import { resolveFormText } from "../../utils/text";
|
|
12
11
|
import { mergeFormUiClass } from "../../utils/ui";
|
|
13
12
|
import { useFormArrayItems } from "../array-list/use-array-items";
|
|
13
|
+
import ArrayTableCell from "./array-table-cell.vue";
|
|
14
14
|
const DEFAULT_COLUMN_MIN_WIDTH = "140px";
|
|
15
|
+
const ACTION_BUTTON_WIDTH = 24;
|
|
16
|
+
const ACTIONS_CELL_INSET = 14;
|
|
15
17
|
const props = defineProps({
|
|
16
18
|
field: { type: Object, required: true },
|
|
17
19
|
path: { type: Array, required: true }
|
|
@@ -49,6 +51,7 @@ const {
|
|
|
49
51
|
);
|
|
50
52
|
const viewportRef = ref(null);
|
|
51
53
|
const viewportShadow = useScrollShadow(viewportRef, { orientation: "horizontal", size: 20 });
|
|
54
|
+
const { width: viewportWidth } = useElementSize(viewportRef);
|
|
52
55
|
const ui = computed(() => formUi.ui.value.arrayTable?.ui);
|
|
53
56
|
const columns = computed(
|
|
54
57
|
() => props.field.fields.filter((field) => field.type !== "hidden" && field.ignore !== true)
|
|
@@ -65,6 +68,19 @@ const showActionsColumn = computed(
|
|
|
65
68
|
(_item, index) => isDraggable.value || canDelete(index) || hasCustomActions(index)
|
|
66
69
|
)
|
|
67
70
|
);
|
|
71
|
+
const pinnedStyle = computed(
|
|
72
|
+
() => viewportWidth.value > 0 ? { width: `${viewportWidth.value}px` } : void 0
|
|
73
|
+
);
|
|
74
|
+
const actionsStyle = computed(() => {
|
|
75
|
+
const buttons = Math.max(0, ...items.value.map((_item, index) => rowActionCount(index)));
|
|
76
|
+
return buttons ? { width: `${buttons * ACTION_BUTTON_WIDTH + ACTIONS_CELL_INSET}px` } : void 0;
|
|
77
|
+
});
|
|
78
|
+
function rowActionCount(index) {
|
|
79
|
+
const custom = (props.field.actions?.custom ?? []).filter(
|
|
80
|
+
(_action, actionIndex) => customActionVisible(index, actionIndex)
|
|
81
|
+
);
|
|
82
|
+
return Number(isDraggable.value) + Number(canDelete(index)) + custom.length;
|
|
83
|
+
}
|
|
68
84
|
function columnStyle(field) {
|
|
69
85
|
const layout = Object.getOwnPropertyDescriptor(field, "layout")?.value;
|
|
70
86
|
const width = isRecord(layout) ? layout.width : void 0;
|
|
@@ -127,6 +143,7 @@ function fieldLabel(field) {
|
|
|
127
143
|
<th
|
|
128
144
|
v-if="showActionsColumn"
|
|
129
145
|
scope="col"
|
|
146
|
+
:style="actionsStyle"
|
|
130
147
|
:class="
|
|
131
148
|
mergeFormUiClass(
|
|
132
149
|
'sticky right-0 w-px border-b border-l border-default bg-elevated px-1.5 py-2 shadow-[-8px_0_12px_-10px_rgba(0,0,0,0.45)]',
|
|
@@ -159,15 +176,7 @@ function fieldLabel(field) {
|
|
|
159
176
|
:key="column.key"
|
|
160
177
|
:class="mergeFormUiClass('border-r border-default p-1.5 last:border-r-0', ui?.cell)"
|
|
161
178
|
>
|
|
162
|
-
<
|
|
163
|
-
:class="mergeFormUiClass('flex w-full items-center [&>*]:w-full', ui?.control)"
|
|
164
|
-
>
|
|
165
|
-
<FormFieldRenderer :field="column" :parent-path="itemPath(index)" bare />
|
|
166
|
-
</div>
|
|
167
|
-
<FormFieldError
|
|
168
|
-
:path="[...itemPath(index), column.key]"
|
|
169
|
-
:class="mergeFormUiClass('mt-1 px-2.5 text-xs text-error', ui?.error)"
|
|
170
|
-
/>
|
|
179
|
+
<ArrayTableCell :field="column" :item-path="itemPath(index)" />
|
|
171
180
|
</td>
|
|
172
181
|
<td
|
|
173
182
|
v-if="showActionsColumn"
|
|
@@ -218,11 +227,16 @@ function fieldLabel(field) {
|
|
|
218
227
|
</td>
|
|
219
228
|
</tr>
|
|
220
229
|
<tr v-if="items.length === 0">
|
|
221
|
-
<td
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
<td :colspan="columns.length + (showActionsColumn ? 1 : 0)" class="p-0">
|
|
231
|
+
<div
|
|
232
|
+
:class="
|
|
233
|
+
mergeFormUiClass('sticky left-0 px-3 py-10 text-center text-muted', ui?.empty)
|
|
234
|
+
"
|
|
235
|
+
:style="pinnedStyle"
|
|
236
|
+
data-form-array-empty=""
|
|
237
|
+
>
|
|
238
|
+
{{ emptyLabel }}
|
|
239
|
+
</div>
|
|
226
240
|
</td>
|
|
227
241
|
</tr>
|
|
228
242
|
</component>
|
|
@@ -230,22 +244,24 @@ function fieldLabel(field) {
|
|
|
230
244
|
<tr>
|
|
231
245
|
<td
|
|
232
246
|
:colspan="columns.length + (showActionsColumn ? 1 : 0)"
|
|
233
|
-
:class="mergeFormUiClass('border-t border-default p-
|
|
247
|
+
:class="mergeFormUiClass('border-t border-default p-0', ui?.addCell)"
|
|
234
248
|
>
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
249
|
+
<div class="sticky left-0 p-1.5" :style="pinnedStyle">
|
|
250
|
+
<button
|
|
251
|
+
type="button"
|
|
252
|
+
:class="
|
|
238
253
|
mergeFormUiClass(
|
|
239
254
|
'flex w-full items-center justify-center gap-2 rounded-md border border-dashed border-default px-3 py-2 text-sm text-muted transition-colors hover:border-inverted/40 hover:bg-elevated/50 hover:text-default focus-visible:outline-2 focus-visible:outline-primary',
|
|
240
255
|
ui?.add
|
|
241
256
|
)
|
|
242
257
|
"
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
258
|
+
:data-form-array-add="path.join('.')"
|
|
259
|
+
@click="addItem()"
|
|
260
|
+
>
|
|
261
|
+
<UIcon name="i-lucide-plus" class="size-4" aria-hidden="true" />
|
|
262
|
+
<span>{{ addItemLabel }}</span>
|
|
263
|
+
</button>
|
|
264
|
+
</div>
|
|
249
265
|
</td>
|
|
250
266
|
</tr>
|
|
251
267
|
</tfoot>
|
|
@@ -32,6 +32,9 @@ export function useChoiceCard(params) {
|
|
|
32
32
|
corner.value ? "relative" : void 0,
|
|
33
33
|
current?.item
|
|
34
34
|
),
|
|
35
|
+
// The card label renders the option icon itself (inline or as a tile): Nuxt UI's own copy
|
|
36
|
+
// would show it twice.
|
|
37
|
+
icon: mergeFormUiClass("hidden", current?.icon),
|
|
35
38
|
// A tile stacks above the title, and the description below it, with gaps.
|
|
36
39
|
label: mergeFormUiClass(
|
|
37
40
|
tile.value ? "flex flex-col items-start gap-2.5 font-semibold" : void 0,
|
|
@@ -39,7 +42,7 @@ export function useChoiceCard(params) {
|
|
|
39
42
|
),
|
|
40
43
|
// A hidden indicator centers the text: keep it aligned, and clear of an inline corner check.
|
|
41
44
|
wrapper: mergeFormUiClass(
|
|
42
|
-
tile.value ? "flex flex-col gap-1.5" : void 0,
|
|
45
|
+
tile.value ? "flex flex-col items-start gap-1.5" : void 0,
|
|
43
46
|
[
|
|
44
47
|
indicator.value === "hidden" ? "ms-0 text-start" : "",
|
|
45
48
|
corner.value && !tile.value ? "pe-7" : ""
|
|
@@ -13,4 +13,9 @@ export interface FormDateProps {
|
|
|
13
13
|
calendar?: FormDateFamilyCalendarOptions;
|
|
14
14
|
}
|
|
15
15
|
export type FormDateField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> = FormStatefulFieldBase<'date', Date | string | null, TContext, TDeps, FormDateProps>;
|
|
16
|
-
|
|
16
|
+
/** A date field submits its canonical `YYYY-MM-DD` value, or a `Date` with `outputFormat: 'date'`. */
|
|
17
|
+
export type DateFieldOutput<TField> = TField extends {
|
|
18
|
+
props: {
|
|
19
|
+
outputFormat: 'date';
|
|
20
|
+
};
|
|
21
|
+
} ? Date | NullableValue : string | NullableValue;
|
|
@@ -102,7 +102,13 @@ const placeholder = computed(() => {
|
|
|
102
102
|
if (explicitlyConfigured !== void 0) {
|
|
103
103
|
return fieldPlaceholder.value;
|
|
104
104
|
}
|
|
105
|
-
return manualInput.value.placeholder ?? dateManualPlaceholder(manualFormat.value, isRange.value
|
|
105
|
+
return manualInput.value.placeholder ?? dateManualPlaceholder(manualFormat.value, isRange.value, {
|
|
106
|
+
day: t("form.fields.date.tokens.day"),
|
|
107
|
+
hour: t("form.fields.date.tokens.hour"),
|
|
108
|
+
minute: t("form.fields.date.tokens.minute"),
|
|
109
|
+
month: t("form.fields.date.tokens.month"),
|
|
110
|
+
year: t("form.fields.date.tokens.year")
|
|
111
|
+
});
|
|
106
112
|
});
|
|
107
113
|
const previewValue = computed(() => {
|
|
108
114
|
if (!isRange.value) {
|
|
@@ -13,7 +13,14 @@ export declare function isDateFamilyRange(type: FormDateFamilyType): type is "da
|
|
|
13
13
|
export declare function hasDateFamilyTime(type: FormDateFamilyType): type is "datetime" | "datetimerange";
|
|
14
14
|
export declare function dateFamilyCalendarType(type: FormDateFamilyType): 'date' | 'month' | 'year';
|
|
15
15
|
export declare function defaultDateManualFormat(type: FormDateFamilyType, locale: string): string;
|
|
16
|
-
export
|
|
16
|
+
export interface DateManualPlaceholderTokens {
|
|
17
|
+
day: string;
|
|
18
|
+
month: string;
|
|
19
|
+
year: string;
|
|
20
|
+
hour: string;
|
|
21
|
+
minute: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function dateManualPlaceholder(format: string, range: boolean, tokens?: DateManualPlaceholderTokens): string;
|
|
17
24
|
export declare function applyDateManualMask(value: string, format: string, range: boolean): string;
|
|
18
25
|
export declare function parseDateManualValue(value: string, format: string, type: FormDateFamilyType): string | readonly [string, string] | undefined;
|
|
19
26
|
export declare function formatDateManualValue(values: readonly [string, string], format: string, type: FormDateFamilyType): string;
|
|
@@ -25,8 +25,16 @@ export function defaultDateManualFormat(type, locale) {
|
|
|
25
25
|
const dateFormat = locale.toLowerCase().startsWith("fr") ? "dd/MM/yyyy" : "MM/dd/yyyy";
|
|
26
26
|
return hasDateFamilyTime(type) ? `${dateFormat} HH:mm` : dateFormat;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const DATE_PLACEHOLDER_TOKEN = /yyyy|MM|dd|HH|mm/gu;
|
|
29
|
+
export function dateManualPlaceholder(format, range, tokens) {
|
|
30
|
+
const names = {
|
|
31
|
+
HH: tokens?.hour,
|
|
32
|
+
MM: tokens?.month,
|
|
33
|
+
dd: tokens?.day,
|
|
34
|
+
mm: tokens?.minute,
|
|
35
|
+
yyyy: tokens?.year
|
|
36
|
+
};
|
|
37
|
+
const placeholder = format.replaceAll(DATE_PLACEHOLDER_TOKEN, (token) => names[token] ?? token.toLowerCase()).toLowerCase();
|
|
30
38
|
return range ? `${placeholder} \u2013 ${placeholder}` : placeholder;
|
|
31
39
|
}
|
|
32
40
|
export function applyDateManualMask(value, format, range) {
|
|
@@ -2,4 +2,11 @@ import type { FormValue } from '../../types/index.js';
|
|
|
2
2
|
import type { FormStatefulFieldBase } from '../../types/field-base.js';
|
|
3
3
|
import type { FieldDefaultValue, FallbackNever } from '../../types/field-output-utils.js';
|
|
4
4
|
export type FormHiddenField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> = Pick<FormStatefulFieldBase<'hidden', FormValue, TContext, TDeps>, 'key' | 'type' | 'default' | 'dependencies' | 'condition' | 'validation' | 'transform' | 'submit' | 'ignore' | 'watch' | 'watchOptions' | 'onDependencyChange' | 'onRendered' | 'stateEffect'>;
|
|
5
|
-
|
|
5
|
+
type TransformInputResult<TField> = TField extends {
|
|
6
|
+
transform: {
|
|
7
|
+
input: (...params: never[]) => infer TResult;
|
|
8
|
+
};
|
|
9
|
+
} ? TResult : never;
|
|
10
|
+
/** A hidden field holds its default, or what its `transform.input` makes of the input. */
|
|
11
|
+
export type HiddenFieldOutput<TField> = FallbackNever<FieldDefaultValue<TField>, FallbackNever<TransformInputResult<TField>, FormValue>>;
|
|
12
|
+
export {};
|
|
@@ -1,209 +1,88 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import UAlert from "@nuxt/ui/components/Alert.vue";
|
|
3
|
-
import UButton from "@nuxt/ui/components/Button.vue";
|
|
4
2
|
import UFileUpload from "@nuxt/ui/components/FileUpload.vue";
|
|
5
|
-
import
|
|
6
|
-
import { computed, onScopeDispose, ref } from "vue";
|
|
3
|
+
import { computed, onScopeDispose, shallowRef, useTemplateRef, watch } from "vue";
|
|
7
4
|
import { useUiToolsLocale } from "../../../i18n/use-locale";
|
|
8
5
|
import FormFieldShell from "../../components/renderer/form-field-shell.vue";
|
|
9
|
-
import FormFilePreview from "../../components/utils/form-file-preview.vue";
|
|
10
6
|
import { useFieldControl } from "../../composables/use-field-control";
|
|
11
|
-
import { isObject, isString, isUndefined } from "../../utils/predicate";
|
|
12
7
|
import { resolveFormText } from "../../utils/text";
|
|
8
|
+
import UploadItem from "./upload-item.vue";
|
|
9
|
+
import { useUploadItems } from "./use-upload-items";
|
|
13
10
|
const props = defineProps({
|
|
14
11
|
field: { type: Object, required: true },
|
|
15
12
|
path: { type: Array, required: true }
|
|
16
13
|
});
|
|
17
14
|
const { t } = useUiToolsLocale();
|
|
18
|
-
const { fieldProps, form, controlProps, disabled, handleBlur,
|
|
15
|
+
const { api, fieldProps, form, controlProps, disabled, handleBlur, validationPending } = useFieldControl(
|
|
19
16
|
() => props.field,
|
|
20
17
|
() => props.path,
|
|
21
|
-
{ omit: ["dropzoneLabel", "dropzoneDescription", "autoUpload"] }
|
|
18
|
+
{ omit: ["dropzoneLabel", "dropzoneDescription", "autoUpload", "max", "preview"] }
|
|
22
19
|
);
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (Array.isArray(value) && value.every((item) => isString(item))) {
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
if (Array.isArray(value) && value.every(isFormObject)) {
|
|
37
|
-
return value;
|
|
38
|
-
}
|
|
39
|
-
return isFormObject(value) ? value : null;
|
|
40
|
-
});
|
|
41
|
-
const files = computed(() => {
|
|
42
|
-
if (Array.isArray(selectedFiles.value)) {
|
|
43
|
-
return selectedFiles.value;
|
|
44
|
-
}
|
|
45
|
-
return selectedFiles.value ? [selectedFiles.value] : [];
|
|
46
|
-
});
|
|
47
|
-
const openDialog = ref(null);
|
|
48
|
-
const hasSingleFile = computed(() => {
|
|
49
|
-
const { value } = selectedFiles;
|
|
50
|
-
return !fieldProps.value.multiple && value !== null && !Array.isArray(value);
|
|
51
|
-
});
|
|
52
|
-
const dropzoneUi = computed(() => ({
|
|
53
|
-
...controlProps.value.ui,
|
|
54
|
-
base: hasSingleFile.value ? "hidden" : controlProps.value.ui?.base,
|
|
55
|
-
file: hasSingleFile.value ? "relative inset-auto p-0" : controlProps.value.ui?.file,
|
|
56
|
-
files: hasSingleFile.value ? "w-full" : "mb-2 gap-2"
|
|
57
|
-
}));
|
|
58
|
-
function captureOpen(open) {
|
|
59
|
-
openDialog.value = open;
|
|
60
|
-
return "";
|
|
61
|
-
}
|
|
62
|
-
function replaceFile() {
|
|
63
|
-
openDialog.value?.();
|
|
64
|
-
}
|
|
65
|
-
async function uploadFiles() {
|
|
66
|
-
const run = uploadRun.value + 1;
|
|
67
|
-
uploadRun.value = run;
|
|
68
|
-
uploadError.value = null;
|
|
69
|
-
if (!files.value.length) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
uploadPending.value = true;
|
|
73
|
-
try {
|
|
74
|
-
uploadProgress.value = null;
|
|
75
|
-
const value = await props.field.upload.handler({
|
|
76
|
-
...params.value,
|
|
77
|
-
files: files.value,
|
|
78
|
-
onProgress: (percent) => {
|
|
79
|
-
if (uploadRun.value === run) {
|
|
80
|
-
uploadProgress.value = Math.min(100, Math.max(0, percent));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
if (uploadRun.value !== run) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
form.setValue(props.path, value);
|
|
88
|
-
selectedFiles.value = null;
|
|
89
|
-
} catch (error) {
|
|
90
|
-
if (uploadRun.value !== run) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
uploadError.value = error instanceof Error ? error.message : t("form.fields.upload.failed");
|
|
94
|
-
} finally {
|
|
95
|
-
if (uploadRun.value === run) {
|
|
96
|
-
uploadPending.value = false;
|
|
97
|
-
uploadProgress.value = null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
async function removeUpload(value) {
|
|
102
|
-
uploadPending.value = true;
|
|
103
|
-
try {
|
|
104
|
-
await props.field.upload.onDelete?.({
|
|
105
|
-
...params.value,
|
|
106
|
-
value: isUndefined(value) ? uploadedValue.value : value
|
|
107
|
-
});
|
|
108
|
-
form.setValue(props.path, null);
|
|
109
|
-
selectedFiles.value = null;
|
|
110
|
-
} finally {
|
|
111
|
-
uploadPending.value = false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
async function cancelUpload() {
|
|
115
|
-
uploadRun.value += 1;
|
|
116
|
-
uploadPending.value = false;
|
|
117
|
-
}
|
|
118
|
-
async function retryUpload() {
|
|
119
|
-
await uploadFiles();
|
|
120
|
-
}
|
|
121
|
-
const unregisterUpload = form.registerFieldUpload(props.path, {
|
|
122
|
-
cancel: cancelUpload,
|
|
123
|
-
remove: removeUpload,
|
|
124
|
-
retry: retryUpload,
|
|
125
|
-
start: uploadFiles
|
|
126
|
-
});
|
|
20
|
+
const uploads = useUploadItems(
|
|
21
|
+
() => props.field,
|
|
22
|
+
() => props.path
|
|
23
|
+
);
|
|
24
|
+
const picker = useTemplateRef("picker");
|
|
25
|
+
const picked = shallowRef(null);
|
|
26
|
+
const compact = computed(
|
|
27
|
+
() => !uploads.multiple.value && fieldProps.value.variant === "button"
|
|
28
|
+
);
|
|
29
|
+
const unregisterUpload = form.registerFieldUpload(props.path, uploads.runtime);
|
|
127
30
|
onScopeDispose(unregisterUpload);
|
|
128
|
-
|
|
31
|
+
watch(picked, (value) => {
|
|
32
|
+
const files = Array.isArray(value) ? value : value ? [value] : [];
|
|
33
|
+
if (files.length === 0) return;
|
|
34
|
+
picked.value = uploads.multiple.value ? [] : null;
|
|
129
35
|
void handleBlur();
|
|
130
|
-
|
|
131
|
-
|
|
36
|
+
uploads.select(files);
|
|
37
|
+
});
|
|
38
|
+
watch(
|
|
39
|
+
() => uploads.runtime.pending(),
|
|
40
|
+
(pending) => {
|
|
41
|
+
if (!pending) api.value.validation.clearError();
|
|
132
42
|
}
|
|
133
|
-
|
|
134
|
-
function
|
|
135
|
-
|
|
43
|
+
);
|
|
44
|
+
function replace() {
|
|
45
|
+
picker.value?.inputRef?.click();
|
|
136
46
|
}
|
|
137
47
|
</script>
|
|
138
48
|
|
|
139
49
|
<template>
|
|
140
50
|
<FormFieldShell :field="field" :path="path">
|
|
141
|
-
<div class="grid gap-
|
|
51
|
+
<div class="grid gap-2" :data-form-upload-busy="uploads.busy.value || void 0">
|
|
52
|
+
<ul
|
|
53
|
+
v-if="uploads.items.value.length"
|
|
54
|
+
class="m-0 grid list-none gap-2 p-0"
|
|
55
|
+
data-form-upload-list=""
|
|
56
|
+
>
|
|
57
|
+
<UploadItem
|
|
58
|
+
v-for="item in uploads.items.value"
|
|
59
|
+
:key="item.key"
|
|
60
|
+
:item="item"
|
|
61
|
+
:compact="compact"
|
|
62
|
+
:disabled="disabled"
|
|
63
|
+
:replaceable="!uploads.multiple.value"
|
|
64
|
+
@open="uploads.open(item.key)"
|
|
65
|
+
@start="uploads.start(item.key)"
|
|
66
|
+
@cancel="uploads.cancel(item.key)"
|
|
67
|
+
@retry="uploads.retry(item.key)"
|
|
68
|
+
@remove="uploads.remove(item.key)"
|
|
69
|
+
@replace="replace"
|
|
70
|
+
/>
|
|
71
|
+
</ul>
|
|
142
72
|
<UFileUpload
|
|
143
|
-
|
|
73
|
+
ref="picker"
|
|
74
|
+
v-model="picked"
|
|
144
75
|
v-bind="controlProps"
|
|
145
|
-
class="w-full"
|
|
76
|
+
:class="uploads.canSelect.value ? 'w-full' : 'hidden'"
|
|
146
77
|
:accept="fieldProps.accept"
|
|
147
|
-
:multiple="
|
|
148
|
-
:disabled="disabled ||
|
|
78
|
+
:multiple="uploads.multiple.value"
|
|
79
|
+
:disabled="disabled || validationPending"
|
|
149
80
|
:label="resolveFormText(fieldProps.dropzoneLabel) ?? t('form.fields.file.drop')"
|
|
150
81
|
:description="resolveFormText(fieldProps.dropzoneDescription)"
|
|
151
82
|
:icon="fieldProps.icon"
|
|
152
83
|
:variant="fieldProps.variant"
|
|
153
84
|
:layout="fieldProps.layout"
|
|
154
|
-
:preview="
|
|
155
|
-
position="outside"
|
|
156
|
-
:ui="dropzoneUi"
|
|
157
|
-
@change="handleFileChange"
|
|
158
|
-
>
|
|
159
|
-
<template #files-top="{ open }">
|
|
160
|
-
<span hidden>{{ captureOpen(open) }}</span>
|
|
161
|
-
</template>
|
|
162
|
-
<template #file="{ file, index, removeFile }">
|
|
163
|
-
<FormFilePreview
|
|
164
|
-
:file="file"
|
|
165
|
-
:index="index"
|
|
166
|
-
:disabled="disabled"
|
|
167
|
-
:remove-file="removeFile"
|
|
168
|
-
:replace="fieldProps.multiple ? void 0 : replaceFile"
|
|
169
|
-
/>
|
|
170
|
-
</template>
|
|
171
|
-
</UFileUpload>
|
|
172
|
-
<UProgress
|
|
173
|
-
v-if="uploadPending && uploadProgress !== null"
|
|
174
|
-
:model-value="uploadProgress"
|
|
175
|
-
size="sm"
|
|
176
|
-
data-form-upload-progress=""
|
|
177
|
-
/>
|
|
178
|
-
|
|
179
|
-
<div class="flex flex-wrap items-center gap-2">
|
|
180
|
-
<UButton
|
|
181
|
-
icon="i-lucide-upload"
|
|
182
|
-
:loading="uploadPending"
|
|
183
|
-
:disabled="disabled || !files.length"
|
|
184
|
-
@click="uploadFiles"
|
|
185
|
-
>
|
|
186
|
-
{{ t("form.fields.upload.upload") }}
|
|
187
|
-
</UButton>
|
|
188
|
-
<UButton
|
|
189
|
-
v-if="uploadedValue"
|
|
190
|
-
icon="i-lucide-trash-2"
|
|
191
|
-
color="neutral"
|
|
192
|
-
variant="soft"
|
|
193
|
-
:loading="uploadPending"
|
|
194
|
-
:disabled="disabled"
|
|
195
|
-
@click="removeUpload"
|
|
196
|
-
>
|
|
197
|
-
{{ t("form.fields.upload.remove") }}
|
|
198
|
-
</UButton>
|
|
199
|
-
</div>
|
|
200
|
-
|
|
201
|
-
<UAlert
|
|
202
|
-
v-if="uploadError"
|
|
203
|
-
color="error"
|
|
204
|
-
variant="soft"
|
|
205
|
-
icon="i-lucide-circle-alert"
|
|
206
|
-
:description="uploadError"
|
|
85
|
+
:preview="false"
|
|
207
86
|
/>
|
|
208
87
|
</div>
|
|
209
88
|
</FormFieldShell>
|
|
@@ -2,30 +2,95 @@ import type { FormValue } from '../../types/index.js';
|
|
|
2
2
|
import type { FormFieldCallbackParams } from '../../types/callbacks.js';
|
|
3
3
|
import type { FormStatefulFieldBase } from '../../types/field-base.js';
|
|
4
4
|
import type { FieldProps, NullableValue } from '../../types/field-output-utils.js';
|
|
5
|
-
import type { FormObject, FormText } from '../../types/utils.js';
|
|
5
|
+
import type { FormMaybePromise, FormObject, FormText } from '../../types/utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* One stored upload value: the URL string or the object returned by the upload handler.
|
|
8
|
+
*/
|
|
9
|
+
export type FormUploadValue = string | FormObject;
|
|
6
10
|
export interface FormUploadCallbackParams<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> extends FormFieldCallbackParams<TContext, TDeps> {
|
|
11
|
+
/** Files handled by this call. The field calls the handler once per selected file. */
|
|
7
12
|
files: readonly File[];
|
|
13
|
+
/** Reports the progress of this upload, from `0` to `100`. */
|
|
8
14
|
onProgress: (percent: number) => void;
|
|
15
|
+
/** Aborted when the user cancels the upload or removes the file while it uploads. */
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Uploads the selected file and returns the value stored in the form.
|
|
20
|
+
* When an array is returned for a single file, its first item is stored.
|
|
21
|
+
*/
|
|
22
|
+
export type FormUploadHandler<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> = (params: FormUploadCallbackParams<TContext, TDeps>) => Promise<FormUploadValue | readonly FormUploadValue[] | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Display data for a stored upload value, used to render files that are already set.
|
|
25
|
+
*/
|
|
26
|
+
export interface FormUploadResolvedFile {
|
|
27
|
+
/** File name shown in the list. */
|
|
28
|
+
name: string;
|
|
29
|
+
/** Size in bytes, shown next to the file type when provided. */
|
|
30
|
+
size?: number | null;
|
|
31
|
+
/** MIME type, used for the file icon and image previews. */
|
|
32
|
+
type?: string | null;
|
|
33
|
+
/** Link opened by the open action when no `upload.open` hook is configured. */
|
|
34
|
+
url?: string | null;
|
|
35
|
+
/** Image preview source. Defaults to `url` for image types. */
|
|
36
|
+
thumbnail?: string | null;
|
|
37
|
+
/** Replaces the file type and size line under the name, for example with an upload date. */
|
|
38
|
+
description?: string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Shows the open action for this file. Defaults to `true` when an `upload.open` hook or a
|
|
41
|
+
* `url` is available.
|
|
42
|
+
*/
|
|
43
|
+
openable?: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface FormUploadValueParams<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> extends FormFieldCallbackParams<TContext, TDeps> {
|
|
46
|
+
/** The stored value being resolved or opened. */
|
|
47
|
+
value: FormUploadValue;
|
|
48
|
+
}
|
|
49
|
+
export interface FormUploadOpenParams<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> extends FormUploadValueParams<TContext, TDeps> {
|
|
50
|
+
/** Display data resolved for the stored value. */
|
|
51
|
+
file: FormUploadResolvedFile;
|
|
9
52
|
}
|
|
10
|
-
export type FormUploadHandler<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> = (params: FormUploadCallbackParams<TContext, TDeps>) => Promise<string | FormObject | readonly string[] | readonly FormObject[] | null>;
|
|
11
53
|
export interface FormUploadDeleteParams<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> extends FormFieldCallbackParams<TContext, TDeps> {
|
|
12
54
|
value: FormValue;
|
|
13
55
|
}
|
|
14
56
|
export interface FormUploadProps {
|
|
15
57
|
multiple?: boolean;
|
|
58
|
+
/** Maximum number of stored and pending files when `multiple` is enabled. */
|
|
59
|
+
max?: number;
|
|
16
60
|
accept?: string;
|
|
61
|
+
/** Uploads files as soon as they are selected. Defaults to `true`. */
|
|
17
62
|
autoUpload?: boolean;
|
|
18
63
|
dropzoneLabel?: FormText;
|
|
19
64
|
dropzoneDescription?: FormText;
|
|
20
65
|
icon?: string | false;
|
|
66
|
+
/** `button` renders a compact picker, suited to table cells. Ignored when `multiple` is set. */
|
|
21
67
|
variant?: 'area' | 'button';
|
|
22
68
|
layout?: 'list' | 'grid';
|
|
69
|
+
/** Shows image thumbnails for image files. Defaults to `true`. */
|
|
23
70
|
preview?: boolean;
|
|
24
71
|
}
|
|
25
|
-
export interface FormUploadField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> extends FormStatefulFieldBase<'upload',
|
|
72
|
+
export interface FormUploadField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> extends FormStatefulFieldBase<'upload', FormUploadValue | readonly FormUploadValue[] | null, TContext, TDeps, FormUploadProps> {
|
|
26
73
|
output: 'url' | 'object';
|
|
27
74
|
upload: {
|
|
28
75
|
handler: FormUploadHandler<TContext, TDeps>;
|
|
76
|
+
/**
|
|
77
|
+
* Resolves display data for a stored value so files that are already set render with
|
|
78
|
+
* their name, size, and preview. Without it, string values use their last URL segment as
|
|
79
|
+
* the name and the value as the link; object values read their `name`, `size`, `type`,
|
|
80
|
+
* `url`, and `thumbnail` keys.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* resolve: async ({ value }) => {
|
|
84
|
+
* const file = await api.files.show(String(value))
|
|
85
|
+
* return { name: file.name, size: file.size, type: file.mimeType }
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
resolve?: (params: FormUploadValueParams<TContext, TDeps>) => FormMaybePromise<FormUploadResolvedFile | null>;
|
|
89
|
+
/**
|
|
90
|
+
* Opens a stored file, for example through a short-lived download link.
|
|
91
|
+
* Defaults to opening the resolved `url` in a new tab.
|
|
92
|
+
*/
|
|
93
|
+
open?: (params: FormUploadOpenParams<TContext, TDeps>) => FormMaybePromise<void>;
|
|
29
94
|
onDelete?: (params: FormUploadDeleteParams<TContext, TDeps>) => Promise<void> | void;
|
|
30
95
|
};
|
|
31
96
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FormUploadItem } from './use-upload-items.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
item: FormUploadItem;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
compact?: boolean;
|
|
6
|
+
replaceable?: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
replace: () => any;
|
|
10
|
+
retry: () => any;
|
|
11
|
+
start: () => any;
|
|
12
|
+
open: () => any;
|
|
13
|
+
remove: () => any;
|
|
14
|
+
cancel: () => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
onReplace?: (() => any) | undefined;
|
|
17
|
+
onRetry?: (() => any) | undefined;
|
|
18
|
+
onStart?: (() => any) | undefined;
|
|
19
|
+
onOpen?: (() => any) | undefined;
|
|
20
|
+
onRemove?: (() => any) | undefined;
|
|
21
|
+
onCancel?: (() => any) | undefined;
|
|
22
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const _default: typeof __VLS_export;
|
|
24
|
+
export default _default;
|