nuxt-ui-tools 1.7.0 → 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 +18 -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-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/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/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 +104 -1
- package/dist/runtime/i18n/locales/fr.js +104 -1
- package/dist/runtime/i18n/types.d.ts +121 -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,247 @@
|
|
|
1
|
+
import { defineAsyncComponent } from "vue";
|
|
2
|
+
import { resolveFilePreviewText } from "./format.js";
|
|
3
|
+
export function defineFilePreviewRenderer(renderer) {
|
|
4
|
+
return renderer;
|
|
5
|
+
}
|
|
6
|
+
export function matchFilePreview(rule) {
|
|
7
|
+
return (file) => {
|
|
8
|
+
const { mime, extension } = file;
|
|
9
|
+
if (mime && rule.mimes?.some((entry) => entry.endsWith("/") ? mime.startsWith(entry) : mime === entry))
|
|
10
|
+
return true;
|
|
11
|
+
return Boolean(extension && rule.extensions?.includes(extension));
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const MEDIA_MODE = "fullscreen md:modal";
|
|
15
|
+
const DOCUMENT_MODE = "fullscreen md:drawer";
|
|
16
|
+
export const fallbackFilePreviewRenderer = defineFilePreviewRenderer({
|
|
17
|
+
compact: true,
|
|
18
|
+
component: () => import("../components/renderers/fallback-renderer.vue"),
|
|
19
|
+
icon: "i-lucide-file",
|
|
20
|
+
kind: "other",
|
|
21
|
+
mode: MEDIA_MODE
|
|
22
|
+
});
|
|
23
|
+
export const builtInFilePreviewRenderers = [
|
|
24
|
+
defineFilePreviewRenderer({
|
|
25
|
+
component: () => import("../components/renderers/pdf-renderer.vue"),
|
|
26
|
+
icon: "i-lucide-file-text",
|
|
27
|
+
kind: "pdf",
|
|
28
|
+
match: matchFilePreview({ extensions: ["pdf"], mimes: ["application/pdf"] }),
|
|
29
|
+
mode: DOCUMENT_MODE
|
|
30
|
+
}),
|
|
31
|
+
defineFilePreviewRenderer({
|
|
32
|
+
component: () => import("../components/renderers/image-renderer.vue"),
|
|
33
|
+
icon: "i-lucide-file-image",
|
|
34
|
+
kind: "image",
|
|
35
|
+
match: matchFilePreview({
|
|
36
|
+
extensions: [
|
|
37
|
+
"apng",
|
|
38
|
+
"avif",
|
|
39
|
+
"bmp",
|
|
40
|
+
"gif",
|
|
41
|
+
"heic",
|
|
42
|
+
"heif",
|
|
43
|
+
"ico",
|
|
44
|
+
"jpeg",
|
|
45
|
+
"jpg",
|
|
46
|
+
"png",
|
|
47
|
+
"svg",
|
|
48
|
+
"webp"
|
|
49
|
+
],
|
|
50
|
+
mimes: ["image/"]
|
|
51
|
+
}),
|
|
52
|
+
mode: MEDIA_MODE
|
|
53
|
+
}),
|
|
54
|
+
defineFilePreviewRenderer({
|
|
55
|
+
component: () => import("../components/renderers/video-renderer.vue"),
|
|
56
|
+
icon: "i-lucide-file-video",
|
|
57
|
+
kind: "video",
|
|
58
|
+
match: matchFilePreview({
|
|
59
|
+
extensions: ["m4v", "mov", "mp4", "ogv", "webm"],
|
|
60
|
+
mimes: ["video/"]
|
|
61
|
+
}),
|
|
62
|
+
mode: MEDIA_MODE
|
|
63
|
+
}),
|
|
64
|
+
defineFilePreviewRenderer({
|
|
65
|
+
compact: true,
|
|
66
|
+
component: () => import("../components/renderers/audio-renderer.vue"),
|
|
67
|
+
icon: "i-lucide-file-audio",
|
|
68
|
+
kind: "audio",
|
|
69
|
+
match: matchFilePreview({
|
|
70
|
+
extensions: ["aac", "flac", "m4a", "mp3", "oga", "ogg", "opus", "wav", "weba"],
|
|
71
|
+
mimes: ["audio/"]
|
|
72
|
+
}),
|
|
73
|
+
mode: MEDIA_MODE
|
|
74
|
+
}),
|
|
75
|
+
defineFilePreviewRenderer({
|
|
76
|
+
component: () => import("../components/renderers/csv-renderer.vue"),
|
|
77
|
+
icon: "i-lucide-file-spreadsheet",
|
|
78
|
+
kind: "csv",
|
|
79
|
+
match: matchFilePreview({
|
|
80
|
+
extensions: ["csv", "tsv"],
|
|
81
|
+
mimes: ["text/csv", "text/tab-separated-values"]
|
|
82
|
+
}),
|
|
83
|
+
mode: DOCUMENT_MODE
|
|
84
|
+
}),
|
|
85
|
+
defineFilePreviewRenderer({
|
|
86
|
+
component: () => import("../components/renderers/markdown-renderer.vue"),
|
|
87
|
+
icon: "i-lucide-file-text",
|
|
88
|
+
kind: "markdown",
|
|
89
|
+
match: matchFilePreview({
|
|
90
|
+
extensions: ["markdown", "md", "mdx"],
|
|
91
|
+
mimes: ["text/markdown", "text/x-markdown"]
|
|
92
|
+
}),
|
|
93
|
+
mode: DOCUMENT_MODE
|
|
94
|
+
}),
|
|
95
|
+
defineFilePreviewRenderer({
|
|
96
|
+
component: () => import("../components/renderers/text-renderer.vue"),
|
|
97
|
+
icon: "i-lucide-file-code",
|
|
98
|
+
kind: "text",
|
|
99
|
+
match: matchFilePreview({
|
|
100
|
+
extensions: [
|
|
101
|
+
"c",
|
|
102
|
+
"cjs",
|
|
103
|
+
"conf",
|
|
104
|
+
"cpp",
|
|
105
|
+
"cs",
|
|
106
|
+
"css",
|
|
107
|
+
"env",
|
|
108
|
+
"go",
|
|
109
|
+
"graphql",
|
|
110
|
+
"h",
|
|
111
|
+
"htm",
|
|
112
|
+
"html",
|
|
113
|
+
"ini",
|
|
114
|
+
"java",
|
|
115
|
+
"js",
|
|
116
|
+
"json",
|
|
117
|
+
"jsonc",
|
|
118
|
+
"jsx",
|
|
119
|
+
"kt",
|
|
120
|
+
"log",
|
|
121
|
+
"mjs",
|
|
122
|
+
"php",
|
|
123
|
+
"py",
|
|
124
|
+
"rb",
|
|
125
|
+
"rs",
|
|
126
|
+
"scss",
|
|
127
|
+
"sh",
|
|
128
|
+
"sql",
|
|
129
|
+
"srt",
|
|
130
|
+
"swift",
|
|
131
|
+
"toml",
|
|
132
|
+
"ts",
|
|
133
|
+
"tsx",
|
|
134
|
+
"txt",
|
|
135
|
+
"vtt",
|
|
136
|
+
"vue",
|
|
137
|
+
"xml",
|
|
138
|
+
"yaml",
|
|
139
|
+
"yml"
|
|
140
|
+
],
|
|
141
|
+
mimes: [
|
|
142
|
+
"text/",
|
|
143
|
+
"application/json",
|
|
144
|
+
"application/ld+json",
|
|
145
|
+
"application/xml",
|
|
146
|
+
"application/javascript",
|
|
147
|
+
"application/x-yaml",
|
|
148
|
+
"application/yaml",
|
|
149
|
+
"application/sql"
|
|
150
|
+
]
|
|
151
|
+
}),
|
|
152
|
+
mode: DOCUMENT_MODE
|
|
153
|
+
}),
|
|
154
|
+
defineFilePreviewRenderer({
|
|
155
|
+
compact: true,
|
|
156
|
+
component: () => import("../components/renderers/fallback-renderer.vue"),
|
|
157
|
+
icon: "i-lucide-file-type",
|
|
158
|
+
kind: "office",
|
|
159
|
+
match: matchFilePreview({
|
|
160
|
+
extensions: [
|
|
161
|
+
"doc",
|
|
162
|
+
"docx",
|
|
163
|
+
"key",
|
|
164
|
+
"numbers",
|
|
165
|
+
"odp",
|
|
166
|
+
"ods",
|
|
167
|
+
"odt",
|
|
168
|
+
"pages",
|
|
169
|
+
"ppt",
|
|
170
|
+
"pptx",
|
|
171
|
+
"rtf",
|
|
172
|
+
"xls",
|
|
173
|
+
"xlsx"
|
|
174
|
+
],
|
|
175
|
+
mimes: [
|
|
176
|
+
"application/msword",
|
|
177
|
+
"application/rtf",
|
|
178
|
+
"application/vnd.ms-excel",
|
|
179
|
+
"application/vnd.ms-powerpoint",
|
|
180
|
+
"application/vnd.oasis.opendocument.presentation",
|
|
181
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
|
182
|
+
"application/vnd.oasis.opendocument.text",
|
|
183
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
184
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
185
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
186
|
+
]
|
|
187
|
+
}),
|
|
188
|
+
mode: MEDIA_MODE
|
|
189
|
+
}),
|
|
190
|
+
defineFilePreviewRenderer({
|
|
191
|
+
compact: true,
|
|
192
|
+
component: () => import("../components/renderers/fallback-renderer.vue"),
|
|
193
|
+
icon: "i-lucide-file-archive",
|
|
194
|
+
kind: "archive",
|
|
195
|
+
match: matchFilePreview({
|
|
196
|
+
extensions: ["7z", "bz2", "gz", "rar", "tar", "tgz", "xz", "zip"],
|
|
197
|
+
mimes: [
|
|
198
|
+
"application/gzip",
|
|
199
|
+
"application/x-7z-compressed",
|
|
200
|
+
"application/x-bzip2",
|
|
201
|
+
"application/x-rar-compressed",
|
|
202
|
+
"application/x-tar",
|
|
203
|
+
"application/zip"
|
|
204
|
+
]
|
|
205
|
+
}),
|
|
206
|
+
mode: MEDIA_MODE
|
|
207
|
+
}),
|
|
208
|
+
fallbackFilePreviewRenderer
|
|
209
|
+
];
|
|
210
|
+
export function mergeFilePreviewRenderers(custom) {
|
|
211
|
+
const replaced = new Set(custom.map((renderer) => renderer.kind));
|
|
212
|
+
return [
|
|
213
|
+
...custom,
|
|
214
|
+
...builtInFilePreviewRenderers.filter((renderer) => !replaced.has(renderer.kind))
|
|
215
|
+
];
|
|
216
|
+
}
|
|
217
|
+
export function detectFilePreviewKind(file, renderers) {
|
|
218
|
+
if (file.kind) return file.kind;
|
|
219
|
+
return renderers.find((renderer) => renderer.match?.(file))?.kind ?? "other";
|
|
220
|
+
}
|
|
221
|
+
export function findFilePreviewRenderer(kind, renderers) {
|
|
222
|
+
return renderers.find((renderer) => renderer.kind === kind) ?? renderers.find((renderer) => renderer.kind === "other") ?? fallbackFilePreviewRenderer;
|
|
223
|
+
}
|
|
224
|
+
const components = /* @__PURE__ */ new WeakMap();
|
|
225
|
+
export function filePreviewRendererComponent(renderer) {
|
|
226
|
+
const known = components.get(renderer);
|
|
227
|
+
if (known) return known;
|
|
228
|
+
const component = defineAsyncComponent(renderer.component);
|
|
229
|
+
components.set(renderer, component);
|
|
230
|
+
return component;
|
|
231
|
+
}
|
|
232
|
+
const BUILT_IN_KINDS = /* @__PURE__ */ new Set([
|
|
233
|
+
"archive",
|
|
234
|
+
"audio",
|
|
235
|
+
"csv",
|
|
236
|
+
"image",
|
|
237
|
+
"markdown",
|
|
238
|
+
"office",
|
|
239
|
+
"other",
|
|
240
|
+
"pdf",
|
|
241
|
+
"text",
|
|
242
|
+
"video"
|
|
243
|
+
]);
|
|
244
|
+
export function filePreviewKindLabel(renderer, t) {
|
|
245
|
+
if (renderer.label) return resolveFilePreviewText(renderer.label);
|
|
246
|
+
return BUILT_IN_KINDS.has(renderer.kind) ? t(`filePreview.kinds.${renderer.kind}`) : renderer.kind;
|
|
247
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Header of a form page: the schema `header` (eyebrow, title, description), an unsaved-changes
|
|
3
3
|
* badge while a form with `controls.dirtyCheck` is modified, and the page actions. It stays
|
|
4
|
-
* pinned while the page scrolls on wide layouts, and sections scroll to just below it.
|
|
4
|
+
* pinned while the page scrolls on wide layouts, and sections scroll to just below it. The badge
|
|
5
|
+
* keeps its place while the form is unmodified, hidden, and does not grow its line, so the first
|
|
6
|
+
* edit neither shifts the page nor resizes the pinned header.
|
|
5
7
|
*/
|
|
6
8
|
type __VLS_Slots = {
|
|
7
9
|
/** Before the heading, e.g. an avatar of the record. */
|
|
@@ -70,7 +70,7 @@ function headerText(config, key) {
|
|
|
70
70
|
<slot name="title">{{ title }}</slot>
|
|
71
71
|
</h1>
|
|
72
72
|
<div
|
|
73
|
-
v-if="description ||
|
|
73
|
+
v-if="description || page.dirtyCheck.value || $slots.description"
|
|
74
74
|
:class="
|
|
75
75
|
mergeFormUiClass(
|
|
76
76
|
'flex flex-wrap items-center gap-2.5 text-[13.5px] text-muted',
|
|
@@ -82,12 +82,12 @@ function headerText(config, key) {
|
|
|
82
82
|
<span v-if="description">{{ description }}</span>
|
|
83
83
|
</slot>
|
|
84
84
|
<UBadge
|
|
85
|
-
v-if="
|
|
85
|
+
v-if="page.dirtyCheck.value"
|
|
86
86
|
color="warning"
|
|
87
87
|
variant="soft"
|
|
88
88
|
:label="t('form.page.unsavedChanges')"
|
|
89
|
-
:class="pageUi?.unsaved"
|
|
90
|
-
data-form-page-unsaved
|
|
89
|
+
:class="mergeFormUiClass(unsaved ? '-my-1' : '-my-1 invisible', pageUi?.unsaved)"
|
|
90
|
+
:data-form-page-unsaved="unsaved ? '' : void 0"
|
|
91
91
|
/>
|
|
92
92
|
</div>
|
|
93
93
|
</div>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Header of a form page: the schema `header` (eyebrow, title, description), an unsaved-changes
|
|
3
3
|
* badge while a form with `controls.dirtyCheck` is modified, and the page actions. It stays
|
|
4
|
-
* pinned while the page scrolls on wide layouts, and sections scroll to just below it.
|
|
4
|
+
* pinned while the page scrolls on wide layouts, and sections scroll to just below it. The badge
|
|
5
|
+
* keeps its place while the form is unmodified, hidden, and does not grow its line, so the first
|
|
6
|
+
* edit neither shifts the page nor resizes the pinned header.
|
|
5
7
|
*/
|
|
6
8
|
type __VLS_Slots = {
|
|
7
9
|
/** Before the heading, e.g. an avatar of the record. */
|
|
@@ -75,6 +75,7 @@ function select(event, key) {
|
|
|
75
75
|
<nav
|
|
76
76
|
:aria-labelledby="page.navigationTitle.value || $slots.title ? titleId : void 0"
|
|
77
77
|
:aria-label="page.navigationTitle.value || $slots.title ? void 0 : t('form.page.navigation')"
|
|
78
|
+
:style="{ '--nut-form-page-header': `${page.stickyOffset.value}px` }"
|
|
78
79
|
:class="
|
|
79
80
|
mergeFormUiClass(
|
|
80
81
|
'flex min-w-0 flex-col gap-3.5 @3xl/form-page:sticky @3xl/form-page:top-[calc(var(--nut-form-page-header,0px)+var(--nut-form-page-gap,24px))]',
|
|
@@ -126,7 +127,7 @@ function select(event, key) {
|
|
|
126
127
|
:data-dirty="dirtyShown(section) || void 0"
|
|
127
128
|
:class="
|
|
128
129
|
mergeFormUiClass(
|
|
129
|
-
'group flex h-8 min-w-0 items-center gap-2 rounded-md border border-default bg-default px-2.5 text-[12.5px] font-medium text-muted no-underline transition-colors hover:text-highlighted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary data-[active]:border-primary data-[active]:bg-primary/10 data-[active]:text-highlighted @3xl/form-page:h-9 @3xl/form-page:gap-2.5 @3xl/form-page:rounded-[7px] @3xl/form-page:border-transparent @3xl/form-page:bg-transparent @3xl/form-page:text-[13.5px] @3xl/form-page:hover:bg-elevated @3xl/form-page:data-[active]:border-transparent @3xl/form-page:data-[active]:bg-default @3xl/form-page:data-[active]:font-semibold @3xl/form-page:data-[active]:shadow-[inset_2px_0_0_var(--ui-primary)]',
|
|
130
|
+
'group relative flex h-8 min-w-0 items-center gap-2 rounded-md border border-default bg-default px-2.5 text-[12.5px] font-medium text-muted no-underline transition-colors hover:text-highlighted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary data-[active]:border-primary data-[active]:bg-primary/10 data-[active]:text-highlighted @3xl/form-page:h-9 @3xl/form-page:gap-2.5 @3xl/form-page:rounded-[7px] @3xl/form-page:border-transparent @3xl/form-page:bg-transparent @3xl/form-page:text-[13.5px] @3xl/form-page:hover:bg-elevated @3xl/form-page:data-[active]:border-transparent @3xl/form-page:data-[active]:bg-default @3xl/form-page:data-[active]:font-semibold @3xl/form-page:data-[active]:shadow-[inset_2px_0_0_var(--ui-primary)]',
|
|
130
131
|
pageUi?.navigationItem
|
|
131
132
|
)
|
|
132
133
|
"
|
|
@@ -45,9 +45,12 @@ onBeforeUnmount(() => unregister?.());
|
|
|
45
45
|
:data-form-page-section="section.key"
|
|
46
46
|
:data-state="section.status"
|
|
47
47
|
:data-dirty="dirty || void 0"
|
|
48
|
+
:style="{
|
|
49
|
+
scrollMarginTop: `calc(${page.stickyOffset.value}px + var(--nut-form-page-gap, 24px))`
|
|
50
|
+
}"
|
|
48
51
|
:class="
|
|
49
52
|
mergeFormUiClass(
|
|
50
|
-
'flex min-w-0
|
|
53
|
+
'flex min-w-0 flex-col gap-[18px] rounded-xl border border-default bg-default px-5 pt-5 pb-6 transition-[border-color,box-shadow] duration-200 @3xl/form-page:px-6 @3xl/form-page:pt-[22px] data-[dirty]:border-primary data-[dirty]:ring-4 data-[dirty]:ring-primary/20 data-[dirty]:ring-inset',
|
|
51
54
|
pageUi?.section
|
|
52
55
|
)
|
|
53
56
|
"
|
|
@@ -72,7 +75,12 @@ onBeforeUnmount(() => unregister?.());
|
|
|
72
75
|
</h2>
|
|
73
76
|
<p
|
|
74
77
|
v-if="section.description"
|
|
75
|
-
:class="
|
|
78
|
+
:class="
|
|
79
|
+
mergeFormUiClass(
|
|
80
|
+
'order-last m-0 basis-full text-[13px] text-muted @3xl/form-page:order-none @3xl/form-page:basis-auto',
|
|
81
|
+
pageUi?.sectionDescription
|
|
82
|
+
)
|
|
83
|
+
"
|
|
76
84
|
>
|
|
77
85
|
{{ section.description }}
|
|
78
86
|
</p>
|
|
@@ -84,20 +92,20 @@ onBeforeUnmount(() => unregister?.());
|
|
|
84
92
|
:class="mergeFormUiClass('capitalize', pageUi?.sectionOptional)"
|
|
85
93
|
/>
|
|
86
94
|
<div
|
|
87
|
-
v-if="
|
|
88
|
-
:class="mergeFormUiClass('ms-auto flex items-center gap-2', pageUi?.sectionActions)"
|
|
95
|
+
v-if="page.dirtyCheck.value || $slots.actions"
|
|
96
|
+
:class="mergeFormUiClass('ms-auto -my-1.5 flex items-center gap-2', pageUi?.sectionActions)"
|
|
89
97
|
>
|
|
90
98
|
<slot name="actions" :section />
|
|
91
99
|
<UButton
|
|
92
|
-
v-if="
|
|
100
|
+
v-if="page.dirtyCheck.value"
|
|
93
101
|
type="button"
|
|
94
102
|
color="neutral"
|
|
95
103
|
variant="ghost"
|
|
96
104
|
size="sm"
|
|
97
105
|
icon="i-lucide-rotate-ccw"
|
|
98
106
|
:label="t('form.page.resetSection')"
|
|
99
|
-
:class="pageUi?.sectionReset"
|
|
100
|
-
data-form-page-reset
|
|
107
|
+
:class="mergeFormUiClass(dirty ? void 0 : 'invisible', pageUi?.sectionReset)"
|
|
108
|
+
:data-form-page-reset="dirty ? '' : void 0"
|
|
101
109
|
@click="page.resetSection(section.key)"
|
|
102
110
|
/>
|
|
103
111
|
</div>
|
|
@@ -39,7 +39,6 @@ const pageUi = computed(() => root.formUi.ui.value.page?.ui);
|
|
|
39
39
|
:class="
|
|
40
40
|
mergeFormUiClass('@container/form-page h-full min-w-0 overflow-y-auto bg-muted', pageUi?.root)
|
|
41
41
|
"
|
|
42
|
-
:style="{ '--nut-form-page-header': `${page.stickyOffset.value}px` }"
|
|
43
42
|
data-form-page
|
|
44
43
|
@submit.prevent="root.submit"
|
|
45
44
|
>
|
|
@@ -3,6 +3,7 @@ import UButton from "@nuxt/ui/components/Button.vue";
|
|
|
3
3
|
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
4
4
|
import { computed, onScopeDispose, watch } from "vue";
|
|
5
5
|
import { useUiToolsLocale } from "../../../i18n/use-locale";
|
|
6
|
+
import { fileExtensionLabel, fileIconName, formatFileSize } from "../../../shared/utils/file";
|
|
6
7
|
const props = defineProps({
|
|
7
8
|
file: { type: null, required: true },
|
|
8
9
|
index: { type: Number, required: true },
|
|
@@ -23,8 +24,10 @@ const preview = computed(() => {
|
|
|
23
24
|
previewUrl = URL.createObjectURL(props.file);
|
|
24
25
|
return previewUrl;
|
|
25
26
|
});
|
|
26
|
-
const icon = computed(() =>
|
|
27
|
-
const meta = computed(
|
|
27
|
+
const icon = computed(() => fileIconName(props.file.type));
|
|
28
|
+
const meta = computed(
|
|
29
|
+
() => `${fileExtensionLabel(props.file)} \xB7 ${formatFileSize(props.file.size, code.value)}`
|
|
30
|
+
);
|
|
28
31
|
watch(
|
|
29
32
|
() => props.file,
|
|
30
33
|
() => {
|
|
@@ -39,42 +42,6 @@ onScopeDispose(() => {
|
|
|
39
42
|
URL.revokeObjectURL(previewUrl);
|
|
40
43
|
}
|
|
41
44
|
});
|
|
42
|
-
function fileIcon(type) {
|
|
43
|
-
if (type === "application/pdf") {
|
|
44
|
-
return "i-lucide-file-text";
|
|
45
|
-
}
|
|
46
|
-
if (type.includes("spreadsheet") || type.includes("excel") || type === "text/csv") {
|
|
47
|
-
return "i-lucide-file-spreadsheet";
|
|
48
|
-
}
|
|
49
|
-
if (type.includes("word") || type.includes("document")) {
|
|
50
|
-
return "i-lucide-file-type";
|
|
51
|
-
}
|
|
52
|
-
if (type.includes("zip") || type.includes("compressed")) {
|
|
53
|
-
return "i-lucide-file-archive";
|
|
54
|
-
}
|
|
55
|
-
return "i-lucide-file";
|
|
56
|
-
}
|
|
57
|
-
function extension(file) {
|
|
58
|
-
const dot = file.name.lastIndexOf(".");
|
|
59
|
-
return dot === -1 ? file.type || "\u2014" : file.name.slice(dot + 1).toUpperCase();
|
|
60
|
-
}
|
|
61
|
-
function formatSize(bytes) {
|
|
62
|
-
const units = ["o", "Ko", "Mo", "Go"];
|
|
63
|
-
let size = bytes;
|
|
64
|
-
let unit = 0;
|
|
65
|
-
while (size >= 1024 && unit < units.length - 1) {
|
|
66
|
-
size /= 1024;
|
|
67
|
-
unit += 1;
|
|
68
|
-
}
|
|
69
|
-
let digits = 0;
|
|
70
|
-
if (unit > 0 && size < 10) {
|
|
71
|
-
digits = 1;
|
|
72
|
-
}
|
|
73
|
-
const formatted = new Intl.NumberFormat(code.value, { maximumFractionDigits: digits }).format(
|
|
74
|
-
size
|
|
75
|
-
);
|
|
76
|
-
return `${formatted} ${units[unit]}`;
|
|
77
|
-
}
|
|
78
45
|
</script>
|
|
79
46
|
|
|
80
47
|
<template>
|
|
@@ -54,7 +54,7 @@ export declare function useFieldControl<TField extends FormField>(field: () => T
|
|
|
54
54
|
selectedItems: import("vue").ComputedRef<readonly import("../utils/options.js").ResolvedFormOption[]>;
|
|
55
55
|
setSearch: (term: string) => void;
|
|
56
56
|
};
|
|
57
|
-
params: import("vue").ComputedRef<import("../types/index.js").FormFieldCallbackParams<{},
|
|
57
|
+
params: import("vue").ComputedRef<import("../types/index.js").FormFieldCallbackParams<{}, Readonly<import("../../shared/index.js").GenericObject>, unknown, unknown>>;
|
|
58
58
|
placeholder: import("vue").ComputedRef<string>;
|
|
59
59
|
validationPending: import("vue").ComputedRef<boolean>;
|
|
60
60
|
};
|
|
@@ -37,7 +37,9 @@ export function useFormPage(params) {
|
|
|
37
37
|
const dirtyCheck = computed(() => getSchemaDirtyCheck(root.schema.value));
|
|
38
38
|
const navigationTitle = computed(() => getFormPageNavigationTitle(root.schema.value));
|
|
39
39
|
const remaining = computed(
|
|
40
|
-
() => sections.value.filter(
|
|
40
|
+
() => sections.value.filter(
|
|
41
|
+
(section) => section.optional ? section.missing > 0 || section.status === "invalid" : section.status !== "complete"
|
|
42
|
+
).length
|
|
41
43
|
);
|
|
42
44
|
const modified = computed(() => sections.value.filter((section) => section.dirty).length);
|
|
43
45
|
const stickyOffset = ref(0);
|
|
@@ -147,6 +147,9 @@ export function useFormRuntime(params) {
|
|
|
147
147
|
validate
|
|
148
148
|
});
|
|
149
149
|
async function submitHandler(externalSubmitHandler) {
|
|
150
|
+
await uploadRegistry.settle();
|
|
151
|
+
for (const path of uploadRegistry.pendingPaths())
|
|
152
|
+
validation.setError(path, t("form.fields.upload.pending"), { blocking: true });
|
|
150
153
|
const result = await submission.submitHandler(externalSubmitHandler);
|
|
151
154
|
if (result.success) {
|
|
152
155
|
state.rebaseline();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { FormUploadRuntimeState } from '../types/index.js';
|
|
2
2
|
export declare function useFormUploadRegistry(): {
|
|
3
3
|
get: (path: readonly string[]) => FormUploadRuntimeState;
|
|
4
|
+
pendingPaths: () => (readonly string[])[];
|
|
4
5
|
register: (path: readonly string[], state: FormUploadRuntimeState) => () => void;
|
|
6
|
+
settle: () => Promise<void>;
|
|
5
7
|
};
|
|
@@ -3,15 +3,20 @@ export function useFormUploadRegistry() {
|
|
|
3
3
|
const states = shallowReactive({});
|
|
4
4
|
function register(path, state) {
|
|
5
5
|
const key = path.join(".");
|
|
6
|
-
|
|
6
|
+
const entry = { path, state };
|
|
7
|
+
states[key] = entry;
|
|
7
8
|
return () => {
|
|
8
|
-
if (states[key] ===
|
|
9
|
-
delete states[key];
|
|
10
|
-
}
|
|
9
|
+
if (states[key] === entry) delete states[key];
|
|
11
10
|
};
|
|
12
11
|
}
|
|
13
12
|
function get(path) {
|
|
14
|
-
return states[path.join(".")];
|
|
13
|
+
return states[path.join(".")]?.state;
|
|
15
14
|
}
|
|
16
|
-
|
|
15
|
+
async function settle() {
|
|
16
|
+
await Promise.all(Object.values(states).map((entry) => entry.state.settle()));
|
|
17
|
+
}
|
|
18
|
+
function pendingPaths() {
|
|
19
|
+
return Object.values(states).filter((entry) => entry.state.pending()).map((entry) => entry.path);
|
|
20
|
+
}
|
|
21
|
+
return { get, pendingPaths, register, settle };
|
|
17
22
|
}
|
|
@@ -527,7 +527,11 @@ function isAsyncValidator(rule) {
|
|
|
527
527
|
}
|
|
528
528
|
function resolveRegleStatusErrors(status) {
|
|
529
529
|
const errors = readReactiveProperty(status, "$errors");
|
|
530
|
-
|
|
530
|
+
if (Array.isArray(errors)) {
|
|
531
|
+
return errors.map(String);
|
|
532
|
+
}
|
|
533
|
+
const own = isRecord(errors) ? errors.$self : void 0;
|
|
534
|
+
return Array.isArray(own) ? own.map(String) : [];
|
|
531
535
|
}
|
|
532
536
|
function toReglePath(path, state) {
|
|
533
537
|
const reglePath = [];
|
|
@@ -7,7 +7,7 @@ export declare function isArrayActionConfig(action: FormArrayAction | undefined)
|
|
|
7
7
|
export declare function useFormArrayItems(field: () => FormArrayItemsField, path: () => readonly string[]): {
|
|
8
8
|
actionParams: (index: number) => {
|
|
9
9
|
ctx: {};
|
|
10
|
-
deps:
|
|
10
|
+
deps: Readonly<import("../../../shared/index.js").GenericObject>;
|
|
11
11
|
getOptions: (key: string) => readonly unknown[];
|
|
12
12
|
getValue: (key: string) => unknown;
|
|
13
13
|
index: number;
|
|
@@ -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;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UFormField from "@nuxt/ui/components/FormField.vue";
|
|
3
|
+
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
4
|
+
import UTooltip from "@nuxt/ui/components/Tooltip.vue";
|
|
5
|
+
import { useElementHover, useFocusWithin } from "@vueuse/core";
|
|
6
|
+
import { computed, useTemplateRef } from "vue";
|
|
7
|
+
import FormFieldRenderer from "../../components/renderer/form-field-renderer.vue";
|
|
8
|
+
import { useFormRuntimeContext } from "../../composables/use-form-runtime";
|
|
9
|
+
import { useFormUi } from "../../composables/use-form-ui";
|
|
10
|
+
import { mergeFormUiClass } from "../../utils/ui";
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
field: { type: null, required: true },
|
|
13
|
+
itemPath: { type: Array, required: true }
|
|
14
|
+
});
|
|
15
|
+
const form = useFormRuntimeContext();
|
|
16
|
+
const formUi = useFormUi();
|
|
17
|
+
const ui = computed(() => formUi.ui.value.arrayTable?.ui);
|
|
18
|
+
const control = useTemplateRef("control");
|
|
19
|
+
const hovered = useElementHover(control);
|
|
20
|
+
const { focused } = useFocusWithin(control);
|
|
21
|
+
const path = computed(() => [...props.itemPath, props.field.key]);
|
|
22
|
+
const error = computed(() => form.getFieldError(path.value));
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div
|
|
27
|
+
ref="control"
|
|
28
|
+
:class="mergeFormUiClass('relative flex w-full items-center [&>*]:w-full', ui?.control)"
|
|
29
|
+
:data-form-cell-invalid="error ? path.join('.') : void 0"
|
|
30
|
+
>
|
|
31
|
+
<UFormField
|
|
32
|
+
:name="path.join('.')"
|
|
33
|
+
:error="error"
|
|
34
|
+
:ui="{ root: 'w-full', container: 'mt-0', error: 'sr-only' }"
|
|
35
|
+
>
|
|
36
|
+
<FormFieldRenderer :field="field" :parent-path="itemPath" bare />
|
|
37
|
+
</UFormField>
|
|
38
|
+
</div>
|
|
39
|
+
<UTooltip
|
|
40
|
+
v-if="error"
|
|
41
|
+
:reference="control ?? void 0"
|
|
42
|
+
:open="hovered || focused"
|
|
43
|
+
:content="{ align: 'start', side: 'top', sideOffset: 6 }"
|
|
44
|
+
:ui="{
|
|
45
|
+
content: mergeFormUiClass(
|
|
46
|
+
'h-auto max-w-80 items-start gap-1.5 bg-error px-2.5 py-1.5 text-xs text-inverted ring-error',
|
|
47
|
+
ui?.error
|
|
48
|
+
),
|
|
49
|
+
arrow: 'fill-error'
|
|
50
|
+
}"
|
|
51
|
+
arrow
|
|
52
|
+
>
|
|
53
|
+
<template #content>
|
|
54
|
+
<UIcon name="i-lucide-circle-alert" class="mt-px size-3.5 shrink-0" aria-hidden="true" />
|
|
55
|
+
<span data-form-cell-error="">{{ error }}</span>
|
|
56
|
+
</template>
|
|
57
|
+
</UTooltip>
|
|
58
|
+
</template>
|
|
@@ -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;
|