@xleddyl/443-components-vue 0.3.0 → 0.5.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/README.md +52 -25
- package/dist/components/core/FieldBoundary.vue +14 -0
- package/dist/components/core/FieldBoundary.vue.d.ts +15 -0
- package/dist/components/core/Form.vue +91 -12
- package/dist/components/core/Form.vue.d.ts +27 -2
- package/dist/components/core/FormAlerts.vue +30 -0
- package/dist/{internal/forms → components/core}/FormAlerts.vue.d.ts +4 -4
- package/dist/components/core/FormField.vue +18 -3
- package/dist/components/core/index.d.ts +2 -0
- package/dist/components/core/index.mjs +2 -0
- package/dist/components/data/FilterPills.vue +82 -37
- package/dist/components/data/FilterPills.vue.d.ts +46 -427
- package/dist/components/data/SelectionBar.vue +7 -4
- package/dist/components/data/Tree.vue +9 -4
- package/dist/components/data/Tree.vue.d.ts +4 -1
- package/dist/components/data/types.d.ts +7 -2
- package/dist/components/forms/CheckboxGroup.vue +2 -1
- package/dist/components/forms/FileInput.vue +98 -26
- package/dist/components/forms/FileInput.vue.d.ts +28 -10
- package/dist/components/forms/RadioGroup.vue +2 -1
- package/dist/components/forms/SegmentedControl.vue +2 -1
- package/dist/components/forms/types.d.ts +3 -0
- package/dist/components/layout/Accordion.vue.d.ts +4 -4
- package/dist/components/layout/AppShell.vue +1 -1
- package/dist/components/layout/AppShell.vue.d.ts +2 -2
- package/dist/components/layout/Tabs.vue +10 -1
- package/dist/components/layout/Tabs.vue.d.ts +4 -0
- package/dist/components/layout/types.d.ts +1 -0
- package/dist/components/overlays/CommandPalette.vue +10 -4
- package/dist/components/overlays/CommandPalette.vue.d.ts +273 -511
- package/dist/components/overlays/HoverCard.vue +1 -1
- package/dist/components/overlays/HoverCard.vue.d.ts +2 -2
- package/dist/components/overlays/Popover.vue +1 -1
- package/dist/components/overlays/Popover.vue.d.ts +2 -2
- package/dist/components/overlays/Toaster.vue +35 -4
- package/dist/components/overlays/Toaster.vue.d.ts +3 -0
- package/dist/components/overlays/types.d.ts +8 -4
- package/dist/components/templates/DashboardLayout.vue +21 -18
- package/dist/components/templates/DashboardLayout.vue.d.ts +332 -565
- package/dist/components/templates/ListPage.vue +1 -1
- package/dist/components/templates/ListPage.vue.d.ts +3 -3
- package/dist/components/templates/types.d.ts +4 -4
- package/dist/composables/useField.d.ts +6 -1
- package/dist/composables/useField.mjs +40 -12
- package/dist/composables/useFieldBoundary.d.ts +1 -0
- package/dist/composables/useFieldBoundary.mjs +5 -1
- package/dist/composables/useFileDrop.d.ts +14 -1
- package/dist/composables/useFileDrop.mjs +21 -20
- package/dist/composables/useForm.d.ts +4 -0
- package/dist/composables/useForm.mjs +62 -22
- package/dist/editor/RichTextEditor.vue +2 -1
- package/dist/internal/core/ButtonElement.d.ts +2 -0
- package/dist/internal/core/ButtonElement.mjs +8 -0
- package/dist/internal/forms/files.d.ts +7 -0
- package/dist/internal/forms/files.mjs +24 -0
- package/dist/internal/keys.d.ts +2 -1
- package/dist/internal/keys.mjs +1 -0
- package/dist/internal/layout/route.mjs +1 -1
- package/dist/locale/en.mjs +4 -1
- package/dist/locale/it.mjs +4 -1
- package/dist/locale/types.d.ts +3 -0
- package/dist/nuxt/module.mjs +1 -1
- package/dist/styles/core/button.css +1 -13
- package/dist/styles/core/form-field.css +2 -2
- package/dist/styles/data/avatar.css +1 -1
- package/dist/styles/data/description-list.css +5 -3
- package/dist/styles/data/filter-pills.css +0 -1
- package/dist/styles/data/pagination.css +0 -1
- package/dist/styles/data/selection-bar.css +4 -0
- package/dist/styles/data/stat.css +1 -6
- package/dist/styles/data/table.css +1 -2
- package/dist/styles/data/tag.css +1 -4
- package/dist/styles/data/timeline.css +1 -0
- package/dist/styles/data/tree.css +8 -2
- package/dist/styles/feedback/alert.css +1 -1
- package/dist/styles/feedback/empty-state.css +6 -5
- package/dist/styles/forms/color-input.css +1 -1
- package/dist/styles/forms/combobox.css +0 -1
- package/dist/styles/forms/date-picker.css +1 -1
- package/dist/styles/forms/file-input.css +9 -1
- package/dist/styles/forms/number-input.css +1 -1
- package/dist/styles/forms/password-input.css +1 -5
- package/dist/styles/forms/switch.css +0 -1
- package/dist/styles/layout/accordion.css +7 -2
- package/dist/styles/layout/card.css +13 -7
- package/dist/styles/layout/copy-button.css +2 -2
- package/dist/styles/layout/nav-link.css +1 -2
- package/dist/styles/layout/page-header.css +2 -1
- package/dist/styles/layout/sidebar-nav.css +1 -1
- package/dist/styles/layout/sidebar.css +1 -1
- package/dist/styles/layout/tabs.css +22 -2
- package/dist/styles/layout/theme-switch.css +1 -3
- package/dist/styles/overlays/command.css +1 -1
- package/dist/styles/overlays/confirm.css +0 -1
- package/dist/styles/overlays/drawer.css +23 -2
- package/dist/styles/overlays/menu.css +2 -4
- package/dist/styles/overlays/modal.css +4 -3
- package/dist/styles/overlays/toast.css +0 -1
- package/dist/styles/templates/auth-layout.css +4 -1
- package/dist/styles/templates/empty-page.css +7 -3
- package/dist/styles/templates/error-page.css +4 -0
- package/dist/styles/templates/form-page.css +1 -1
- package/dist/styles/templates/page.css +1 -1
- package/dist/styles/templates/settings-modal.css +4 -2
- package/dist/styles/tokens.css +1 -1
- package/package.json +1 -1
- package/dist/internal/forms/FormAlerts.vue +0 -25
- package/dist/internal/overlays/FieldBoundary.d.ts +0 -4
- package/dist/internal/overlays/FieldBoundary.mjs +0 -9
|
@@ -230,7 +230,7 @@ defineSlots();
|
|
|
230
230
|
const page = defineModel("page", { type: Number, ...{ default: 1 } });
|
|
231
231
|
const pageSize = defineModel("pageSize", { type: Number, ...{ default: 20 } });
|
|
232
232
|
const search = defineModel("search", { type: String, ...{ default: "" } });
|
|
233
|
-
const filter = defineModel("filter", { type:
|
|
233
|
+
const filter = defineModel("filter", { type: null, ...{ default: null } });
|
|
234
234
|
const sort = defineModel("sort", { type: [Object, null], ...{ default: null } });
|
|
235
235
|
const selected = defineModel("selected", { type: Array, ...{ default: () => [] } });
|
|
236
236
|
const slots = useSlots();
|
|
@@ -45,7 +45,7 @@ declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<
|
|
|
45
45
|
page?: number;
|
|
46
46
|
pageSize?: number;
|
|
47
47
|
search?: string;
|
|
48
|
-
filter?: (
|
|
48
|
+
filter?: import("../data/types").FilterPillValue | import("../data/types").FilterPillValue[] | null;
|
|
49
49
|
sort?: TableSort | null;
|
|
50
50
|
selected?: RowKey[];
|
|
51
51
|
}) & {
|
|
@@ -57,7 +57,7 @@ declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<
|
|
|
57
57
|
"onUpdate:sort"?: ((value: TableSort | null) => any) | undefined;
|
|
58
58
|
"onUpdate:selected"?: ((value: RowKey[]) => any) | undefined;
|
|
59
59
|
"onUpdate:search"?: ((value: string) => any) | undefined;
|
|
60
|
-
"onUpdate:filter"?: ((value:
|
|
60
|
+
"onUpdate:filter"?: ((value: import("../data/types").FilterPillValue | import("../data/types").FilterPillValue[] | null) => any) | undefined;
|
|
61
61
|
} & Partial<import("vue").HTMLAttributes & import("vue").ReservedProps>> & (typeof globalThis extends {
|
|
62
62
|
__VLS_PROPS_FALLBACK: infer P;
|
|
63
63
|
} ? P : {});
|
|
@@ -100,7 +100,7 @@ declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<
|
|
|
100
100
|
retry: () => void;
|
|
101
101
|
}) => unknown;
|
|
102
102
|
};
|
|
103
|
-
emit: (((evt: "clear") => void) & ((evt: "retry") => void) & ((evt: "rowClick", row: T, index: number, event: MouseEvent) => void)) & (((event: "update:page", value: number) => void) & ((event: "update:pageSize", value: number) => void) & ((event: "update:sort", value: TableSort | null) => void) & ((event: "update:selected", value: RowKey[]) => void) & ((event: "update:search", value: string) => void) & ((event: "update:filter", value:
|
|
103
|
+
emit: (((evt: "clear") => void) & ((evt: "retry") => void) & ((evt: "rowClick", row: T, index: number, event: MouseEvent) => void)) & (((event: "update:page", value: number) => void) & ((event: "update:pageSize", value: number) => void) & ((event: "update:sort", value: TableSort | null) => void) & ((event: "update:selected", value: RowKey[]) => void) & ((event: "update:search", value: string) => void) & ((event: "update:filter", value: import("../data/types").FilterPillValue | import("../data/types").FilterPillValue[] | null) => void));
|
|
104
104
|
}>) => import("vue").VNode & {
|
|
105
105
|
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
106
106
|
};
|
|
@@ -2,7 +2,7 @@ import type { Component } from 'vue';
|
|
|
2
2
|
import type { LinkTarget, Tone } from '../core/types';
|
|
3
3
|
import type { DescriptionItem, StatTrend } from '../data/types';
|
|
4
4
|
import type { BreadcrumbItem, NavItem, NavSection } from '../layout/types';
|
|
5
|
-
import type { CommandGroup, MenuItems } from '../overlays/types';
|
|
5
|
+
import type { CommandEmptyText, CommandGroup, MenuItems } from '../overlays/types';
|
|
6
6
|
import type { ThemePreset } from '../../internal/core/preset-data';
|
|
7
7
|
export type DashboardVariant = 'inset' | 'sidebar' | 'floating';
|
|
8
8
|
export type ThemeSwitchPlacement = 'topbar' | 'sidebar' | false;
|
|
@@ -43,8 +43,8 @@ export interface DashboardBrand {
|
|
|
43
43
|
logo?: string;
|
|
44
44
|
to?: LinkTarget;
|
|
45
45
|
}
|
|
46
|
-
export type DashboardSearchFunction = (query: string, signal: AbortSignal) => CommandGroup[] | Promise<CommandGroup[]>;
|
|
47
|
-
export type DashboardSearch = boolean | CommandGroup[] | DashboardSearchFunction
|
|
46
|
+
export type DashboardSearchFunction<T = unknown> = (query: string, signal: AbortSignal) => CommandGroup<T>[] | Promise<CommandGroup<T>[]>;
|
|
47
|
+
export type DashboardSearch<T = unknown> = boolean | CommandGroup<T>[] | DashboardSearchFunction<T>;
|
|
48
48
|
export interface DashboardSearchProps {
|
|
49
49
|
label?: string;
|
|
50
50
|
placeholder?: string;
|
|
@@ -55,7 +55,7 @@ export interface DashboardSearchProps {
|
|
|
55
55
|
recentKey?: string;
|
|
56
56
|
recentLimit?: number;
|
|
57
57
|
recentLabel?: string;
|
|
58
|
-
emptyText?:
|
|
58
|
+
emptyText?: CommandEmptyText;
|
|
59
59
|
minQueryText?: string;
|
|
60
60
|
errorText?: string;
|
|
61
61
|
loadingText?: string;
|
|
@@ -8,7 +8,9 @@ export interface FieldContext {
|
|
|
8
8
|
required: ComputedRef<boolean>;
|
|
9
9
|
disabled: ComputedRef<boolean>;
|
|
10
10
|
setControlError: (message: string | undefined, source?: object) => void;
|
|
11
|
+
setLabelable?: (labelable: boolean | undefined, source: object) => void;
|
|
11
12
|
}
|
|
13
|
+
export type FieldControlRole = 'primary' | 'auxiliary';
|
|
12
14
|
export declare function useField(): FieldContext | null;
|
|
13
15
|
export interface FieldControlOptions {
|
|
14
16
|
id?: MaybeRefOrGetter<string | undefined>;
|
|
@@ -16,9 +18,11 @@ export interface FieldControlOptions {
|
|
|
16
18
|
required?: MaybeRefOrGetter<boolean | undefined>;
|
|
17
19
|
disabled?: MaybeRefOrGetter<boolean | undefined>;
|
|
18
20
|
describedBy?: MaybeRefOrGetter<string | undefined>;
|
|
21
|
+
role?: MaybeRefOrGetter<FieldControlRole | undefined>;
|
|
22
|
+
labelable?: MaybeRefOrGetter<boolean | undefined>;
|
|
19
23
|
}
|
|
20
24
|
export declare function useFieldControl(options?: FieldControlOptions): {
|
|
21
|
-
field: FieldContext | null;
|
|
25
|
+
readonly field: FieldContext | null;
|
|
22
26
|
id: ComputedRef<string | undefined>;
|
|
23
27
|
invalid: ComputedRef<boolean>;
|
|
24
28
|
required: ComputedRef<boolean>;
|
|
@@ -32,5 +36,6 @@ export declare function useFieldControl(options?: FieldControlOptions): {
|
|
|
32
36
|
'aria-required': "true" | undefined;
|
|
33
37
|
disabled: true | undefined;
|
|
34
38
|
}>;
|
|
39
|
+
auxiliary: ComputedRef<boolean>;
|
|
35
40
|
setControlError: (message: string | undefined) => void | undefined;
|
|
36
41
|
};
|
|
@@ -1,28 +1,56 @@
|
|
|
1
|
-
import { computed, getCurrentScope, inject, onScopeDispose, toValue } from "vue";
|
|
2
|
-
import { FIELD_KEY } from "../internal/keys.mjs";
|
|
1
|
+
import { computed, getCurrentScope, inject, onScopeDispose, toValue, watch } from "vue";
|
|
2
|
+
import { FIELD_KEY, FIELD_ROLE_KEY } from "../internal/keys.mjs";
|
|
3
3
|
export function useField() {
|
|
4
4
|
return inject(FIELD_KEY, null);
|
|
5
5
|
}
|
|
6
6
|
export function useFieldControl(options = {}) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
7
|
+
const parent = useField();
|
|
8
|
+
const inheritedRole = inject(FIELD_ROLE_KEY, "primary");
|
|
9
|
+
const auxiliary = computed(() => (toValue(options.role) ?? inheritedRole) === "auxiliary");
|
|
10
|
+
const field = computed(() => auxiliary.value ? null : parent);
|
|
11
|
+
const id = computed(() => toValue(options.id) ?? field.value?.id);
|
|
12
|
+
const invalid = computed(() => Boolean(toValue(options.invalid) ?? field.value?.invalid.value));
|
|
13
|
+
const required = computed(
|
|
14
|
+
() => Boolean(toValue(options.required) || field.value?.required.value)
|
|
15
|
+
);
|
|
16
|
+
const disabled = computed(() => Boolean(toValue(options.disabled) || parent?.disabled.value));
|
|
12
17
|
const describedBy = computed(() => {
|
|
13
|
-
const ids = [toValue(options.describedBy), field?.describedBy.value].filter(Boolean);
|
|
18
|
+
const ids = [toValue(options.describedBy), field.value?.describedBy.value].filter(Boolean);
|
|
14
19
|
return ids.length ? ids.join(" ") : void 0;
|
|
15
20
|
});
|
|
16
21
|
const attrs = computed(() => ({
|
|
17
22
|
id: id.value,
|
|
18
|
-
name: field?.name.value,
|
|
23
|
+
name: field.value?.name.value,
|
|
19
24
|
"aria-invalid": invalid.value ? "true" : void 0,
|
|
20
25
|
"aria-describedby": describedBy.value,
|
|
21
26
|
"aria-required": required.value ? "true" : void 0,
|
|
22
27
|
disabled: disabled.value || void 0
|
|
23
28
|
}));
|
|
24
29
|
const source = {};
|
|
25
|
-
const setControlError = (message) =>
|
|
26
|
-
if (
|
|
27
|
-
|
|
30
|
+
const setControlError = (message) => parent?.setControlError(message || void 0, source);
|
|
31
|
+
if (parent && getCurrentScope()) {
|
|
32
|
+
if (parent.setLabelable && options.labelable !== void 0)
|
|
33
|
+
watch(
|
|
34
|
+
() => auxiliary.value ? void 0 : toValue(options.labelable),
|
|
35
|
+
(labelable) => parent.setLabelable?.(labelable, source),
|
|
36
|
+
{ immediate: true }
|
|
37
|
+
);
|
|
38
|
+
onScopeDispose(() => {
|
|
39
|
+
parent.setControlError(void 0, source);
|
|
40
|
+
parent.setLabelable?.(void 0, source);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
get field() {
|
|
45
|
+
return field.value;
|
|
46
|
+
},
|
|
47
|
+
id,
|
|
48
|
+
invalid,
|
|
49
|
+
required,
|
|
50
|
+
disabled,
|
|
51
|
+
describedBy,
|
|
52
|
+
attrs,
|
|
53
|
+
auxiliary,
|
|
54
|
+
setControlError
|
|
55
|
+
};
|
|
28
56
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { provide } from "vue";
|
|
2
|
-
import { FIELD_KEY } from "../internal/keys.mjs";
|
|
2
|
+
import { FIELD_KEY, FIELD_ROLE_KEY } from "../internal/keys.mjs";
|
|
3
3
|
export function provideFieldBoundary() {
|
|
4
4
|
provide(FIELD_KEY, null);
|
|
5
|
+
provide(FIELD_ROLE_KEY, "primary");
|
|
6
|
+
}
|
|
7
|
+
export function provideAuxiliaryControls() {
|
|
8
|
+
provide(FIELD_ROLE_KEY, "auxiliary");
|
|
5
9
|
}
|
|
@@ -8,7 +8,20 @@ export interface DroppedFile {
|
|
|
8
8
|
file: File;
|
|
9
9
|
path: string;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export interface DroppedTree {
|
|
12
|
+
files: DroppedFile[];
|
|
13
|
+
directories: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ReadDroppedFilesOptions {
|
|
16
|
+
directories?: boolean;
|
|
17
|
+
skipHidden?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function readDroppedFiles(transfer: DataTransfer | null | undefined, options: ReadDroppedFilesOptions & {
|
|
20
|
+
directories: true;
|
|
21
|
+
}): Promise<DroppedTree>;
|
|
22
|
+
export declare function readDroppedFiles(transfer: DataTransfer | null | undefined, options?: ReadDroppedFilesOptions & {
|
|
23
|
+
directories?: false;
|
|
24
|
+
}): Promise<DroppedFile[]>;
|
|
12
25
|
export declare function useFileDrop(target: MaybeRefOrGetter<HTMLElement | null | undefined>, options: UseFileDropOptions): {
|
|
13
26
|
over: import("vue").Ref<boolean, boolean>;
|
|
14
27
|
reset: () => void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { onScopeDispose, ref, toValue, watch } from "vue";
|
|
2
|
+
import { filesTree, isHiddenName } from "../internal/forms/files.mjs";
|
|
2
3
|
const claimedEvents = /* @__PURE__ */ new WeakSet();
|
|
3
4
|
function carriesFiles(event) {
|
|
4
5
|
return Array.from(event.dataTransfer?.types ?? []).includes("Files");
|
|
@@ -9,17 +10,20 @@ function readFileEntry(entry) {
|
|
|
9
10
|
function readDirectoryBatch(reader) {
|
|
10
11
|
return new Promise((resolve, reject) => reader.readEntries(resolve, reject));
|
|
11
12
|
}
|
|
12
|
-
async function walkEntry(entry, prefix,
|
|
13
|
+
async function walkEntry(entry, prefix, tree, skipHidden) {
|
|
14
|
+
if (skipHidden && isHiddenName(entry.name)) return;
|
|
15
|
+
const path = `${prefix}${entry.name}`;
|
|
13
16
|
if (entry.isFile) {
|
|
14
17
|
try {
|
|
15
18
|
const file = await readFileEntry(entry);
|
|
16
|
-
|
|
19
|
+
tree.files.push({ file, path });
|
|
17
20
|
} catch {
|
|
18
21
|
return;
|
|
19
22
|
}
|
|
20
23
|
return;
|
|
21
24
|
}
|
|
22
25
|
if (!entry.isDirectory) return;
|
|
26
|
+
tree.directories.push(path);
|
|
23
27
|
const reader = entry.createReader();
|
|
24
28
|
const children = [];
|
|
25
29
|
for (; ; ) {
|
|
@@ -32,10 +36,10 @@ async function walkEntry(entry, prefix, result) {
|
|
|
32
36
|
if (!batch.length) break;
|
|
33
37
|
children.push(...batch);
|
|
34
38
|
}
|
|
35
|
-
for (const child of children) await walkEntry(child, `${
|
|
39
|
+
for (const child of children) await walkEntry(child, `${path}/`, tree, skipHidden);
|
|
36
40
|
}
|
|
37
|
-
|
|
38
|
-
if (!transfer) return
|
|
41
|
+
async function readDroppedTree(transfer, skipHidden) {
|
|
42
|
+
if (!transfer) return { files: [], directories: [] };
|
|
39
43
|
const pending = [];
|
|
40
44
|
for (const item of Array.from(transfer.items ?? [])) {
|
|
41
45
|
if (item.kind !== "file") continue;
|
|
@@ -44,21 +48,18 @@ export function readDroppedFiles(transfer) {
|
|
|
44
48
|
if (entry) pending.push(entry);
|
|
45
49
|
else if (fallback) pending.push(fallback);
|
|
46
50
|
}
|
|
47
|
-
if (!pending.length)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
})();
|
|
51
|
+
if (!pending.length) return filesTree(Array.from(transfer.files ?? []), { skipHidden });
|
|
52
|
+
const tree = { files: [], directories: [] };
|
|
53
|
+
for (const entry of pending) {
|
|
54
|
+
if (!(entry instanceof File)) await walkEntry(entry, "", tree, skipHidden);
|
|
55
|
+
else if (!skipHidden || !isHiddenName(entry.name))
|
|
56
|
+
tree.files.push({ file: entry, path: entry.name });
|
|
57
|
+
}
|
|
58
|
+
return tree;
|
|
59
|
+
}
|
|
60
|
+
export async function readDroppedFiles(transfer, options = {}) {
|
|
61
|
+
const tree = await readDroppedTree(transfer, Boolean(options.skipHidden));
|
|
62
|
+
return options.directories ? tree : tree.files;
|
|
62
63
|
}
|
|
63
64
|
export function useFileDrop(target, options) {
|
|
64
65
|
const over = ref(false);
|
|
@@ -58,6 +58,8 @@ export type FormErrors<S extends FormSchema = FormSchema> = Partial<Record<keyof
|
|
|
58
58
|
export interface FormContext {
|
|
59
59
|
errors: Readonly<FormMessageMap>;
|
|
60
60
|
warnings?: Readonly<FormMessageMap>;
|
|
61
|
+
rootErrors?: Readonly<Ref<readonly string[]>>;
|
|
62
|
+
rootWarnings?: Readonly<Ref<readonly string[]>>;
|
|
61
63
|
submitCount: Readonly<Ref<number>>;
|
|
62
64
|
submitting: Readonly<Ref<boolean>>;
|
|
63
65
|
touch: (name: string) => void;
|
|
@@ -80,6 +82,7 @@ export interface UseFormReturn<S extends FormSchema = FormSchema, V extends obje
|
|
|
80
82
|
submitting: Ref<boolean>;
|
|
81
83
|
isDirty: (name: keyof V & string) => boolean;
|
|
82
84
|
validate: () => Promise<boolean>;
|
|
85
|
+
validateSync: () => boolean;
|
|
83
86
|
validateField: (name: keyof V & string) => Promise<boolean>;
|
|
84
87
|
setError: (name: FormPath<V>, message: string | undefined) => void;
|
|
85
88
|
setErrors: (errors: FormErrors<S>) => void;
|
|
@@ -91,3 +94,4 @@ export interface UseFormReturn<S extends FormSchema = FormSchema, V extends obje
|
|
|
91
94
|
export declare function isEmptyValue(value: unknown): boolean;
|
|
92
95
|
export declare function useForm<S extends FormSchema = FormSchema, V extends object = FormValues<S>>(options: UseFormOptions<S, V>): UseFormReturn<S, V>;
|
|
93
96
|
export declare function useFormContext(): FormContext | null;
|
|
97
|
+
export declare function provideForm(form: FormContext): FormContext;
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
computed,
|
|
3
3
|
inject,
|
|
4
4
|
isRef,
|
|
5
|
+
provide,
|
|
5
6
|
reactive,
|
|
6
7
|
readonly,
|
|
7
8
|
ref,
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
groupIssues,
|
|
16
17
|
isStandardSchema,
|
|
17
18
|
keyPath,
|
|
18
|
-
runStandardSchema,
|
|
19
19
|
schemaIssues
|
|
20
20
|
} from "../internal/forms/standard-schema.mjs";
|
|
21
21
|
import { FORM_KEY } from "../internal/keys.mjs";
|
|
@@ -79,6 +79,9 @@ function settle(value, apply) {
|
|
|
79
79
|
if (isThenable(value)) void value.then(apply);
|
|
80
80
|
else apply(value);
|
|
81
81
|
}
|
|
82
|
+
function chain(value, next) {
|
|
83
|
+
return isThenable(value) ? value.then(next) : next(value);
|
|
84
|
+
}
|
|
82
85
|
function isWithin(key, name) {
|
|
83
86
|
return key === name || key.startsWith(`${name}.`);
|
|
84
87
|
}
|
|
@@ -145,13 +148,18 @@ export function useForm(options) {
|
|
|
145
148
|
replaceEntries(errors, composeErrors(), inScope);
|
|
146
149
|
if (!inScope) rootErrors.value = uniqueMessages(manualRoot, issueRoot);
|
|
147
150
|
}
|
|
148
|
-
|
|
151
|
+
function evaluateValidator() {
|
|
149
152
|
const validator = toValue(options.validator);
|
|
150
153
|
if (!validator) return groupIssues([]);
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
return chain(
|
|
155
|
+
validator["~standard"].validate(cloneValue(target())),
|
|
156
|
+
(result) => groupIssues(schemaIssues(result.issues ?? []), issueKey)
|
|
157
|
+
);
|
|
153
158
|
}
|
|
154
|
-
async function
|
|
159
|
+
async function runValidator() {
|
|
160
|
+
return evaluateValidator();
|
|
161
|
+
}
|
|
162
|
+
function evaluateRules(name) {
|
|
155
163
|
const definition = schema[name];
|
|
156
164
|
if (!definition) return void 0;
|
|
157
165
|
const value = readValue(name);
|
|
@@ -185,16 +193,22 @@ export function useForm(options) {
|
|
|
185
193
|
if (!definition.pattern.test(value)) return fieldMessages.pattern(context);
|
|
186
194
|
}
|
|
187
195
|
}
|
|
188
|
-
|
|
189
|
-
|
|
196
|
+
const runSchema = () => {
|
|
197
|
+
if (!definition.schema) return void 0;
|
|
198
|
+
return chain(definition.schema["~standard"].validate(value), (result) => {
|
|
199
|
+
const issues = result.issues ?? [];
|
|
200
|
+
return issues.length ? issues[0].message || fieldMessages.invalid(context) : void 0;
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
if (!definition.validate) return runSchema();
|
|
204
|
+
return chain(definition.validate(value, target()), (result) => {
|
|
190
205
|
if (typeof result === "string" && result) return result;
|
|
191
206
|
if (result === false) return fieldMessages.invalid(context);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return void 0;
|
|
207
|
+
return runSchema();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
async function runRules(name) {
|
|
211
|
+
return evaluateRules(name);
|
|
198
212
|
}
|
|
199
213
|
function hasErrorWithin(name) {
|
|
200
214
|
return Object.entries(errors).some(([key, message]) => message && isWithin(key, name));
|
|
@@ -215,6 +229,18 @@ export function useForm(options) {
|
|
|
215
229
|
syncErrors(submitCount.value > 0 ? void 0 : (key) => isWithin(key, name));
|
|
216
230
|
return !hasErrorWithin(name);
|
|
217
231
|
}
|
|
232
|
+
function applyValidation(fields, issues, results) {
|
|
233
|
+
issueErrors = issues.fields;
|
|
234
|
+
issueRoot = issues.root;
|
|
235
|
+
for (const key of Object.keys(ruleErrors)) delete ruleErrors[key];
|
|
236
|
+
fields.forEach((name, index) => ruleErrors[name] = results[index]);
|
|
237
|
+
for (const key of Object.keys(manualErrors)) delete manualErrors[key];
|
|
238
|
+
manualRoot = [];
|
|
239
|
+
syncErrors();
|
|
240
|
+
}
|
|
241
|
+
function isValidResult(issues, results) {
|
|
242
|
+
return results.every((message) => !message) && Object.keys(issues.fields).length === 0 && issues.root.length === 0;
|
|
243
|
+
}
|
|
218
244
|
async function validate() {
|
|
219
245
|
const run = ++validationRun;
|
|
220
246
|
const fields = names.value;
|
|
@@ -222,16 +248,25 @@ export function useForm(options) {
|
|
|
222
248
|
runValidator(),
|
|
223
249
|
Promise.all(fields.map((name) => runRules(name)))
|
|
224
250
|
]);
|
|
225
|
-
if (run === validationRun)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
251
|
+
if (run === validationRun) applyValidation(fields, issues, results);
|
|
252
|
+
return isValidResult(issues, results);
|
|
253
|
+
}
|
|
254
|
+
function validateSync() {
|
|
255
|
+
const fields = names.value;
|
|
256
|
+
const issues = evaluateValidator();
|
|
257
|
+
const results = fields.map((name) => evaluateRules(name));
|
|
258
|
+
if (isThenable(issues) || results.some((result) => isThenable(result))) {
|
|
259
|
+
for (const pending of [issues, ...results]) {
|
|
260
|
+
if (isThenable(pending)) void pending.catch(() => void 0);
|
|
261
|
+
}
|
|
262
|
+
throw new Error(
|
|
263
|
+
"[k] useForm: validateSync() found an async rule or validator. Use validate() instead."
|
|
264
|
+
);
|
|
233
265
|
}
|
|
234
|
-
|
|
266
|
+
const messages2 = results;
|
|
267
|
+
++validationRun;
|
|
268
|
+
applyValidation(fields, issues, messages2);
|
|
269
|
+
return isValidResult(issues, messages2);
|
|
235
270
|
}
|
|
236
271
|
function setError(name, message) {
|
|
237
272
|
if (name === "") {
|
|
@@ -374,6 +409,7 @@ export function useForm(options) {
|
|
|
374
409
|
submitting,
|
|
375
410
|
isDirty: (name) => dirtyFields.value.includes(name),
|
|
376
411
|
validate,
|
|
412
|
+
validateSync,
|
|
377
413
|
validateField,
|
|
378
414
|
setError,
|
|
379
415
|
setErrors,
|
|
@@ -387,3 +423,7 @@ export function useForm(options) {
|
|
|
387
423
|
export function useFormContext() {
|
|
388
424
|
return inject(FORM_KEY, null);
|
|
389
425
|
}
|
|
426
|
+
export function provideForm(form) {
|
|
427
|
+
provide(FORM_KEY, form);
|
|
428
|
+
return form;
|
|
429
|
+
}
|
|
@@ -126,7 +126,8 @@ const model = defineModel({ type: [String, Object, null], ...{ default: null } }
|
|
|
126
126
|
const control = useFieldControl({
|
|
127
127
|
invalid: () => props.invalid,
|
|
128
128
|
disabled: () => props.disabled,
|
|
129
|
-
required: () => props.required
|
|
129
|
+
required: () => props.required,
|
|
130
|
+
labelable: false
|
|
130
131
|
});
|
|
131
132
|
const { locale } = useLocale();
|
|
132
133
|
const text = computed(() => ({ ...locale.value.messages.editor, ...props.messages }));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DroppedTree } from '../../composables/useFileDrop';
|
|
1
2
|
export declare function parseAccept(accept: string | string[] | undefined): string[];
|
|
2
3
|
export declare function matchesAccept(file: File, accept: string[]): boolean;
|
|
3
4
|
export declare function formatBytes(bytes: number, locale?: string): string;
|
|
@@ -5,3 +6,9 @@ export declare function isImageFile(file: File): boolean;
|
|
|
5
6
|
export declare function setFilePath(file: File, path: string): void;
|
|
6
7
|
export declare function filePath(file: File): string;
|
|
7
8
|
export declare function fileKey(file: File): string;
|
|
9
|
+
export declare function isHiddenName(name: string): boolean;
|
|
10
|
+
export declare function isHiddenPath(path: string): boolean;
|
|
11
|
+
export declare function directoriesOf(paths: Iterable<string>): string[];
|
|
12
|
+
export declare function filesTree(files: Iterable<File>, options?: {
|
|
13
|
+
skipHidden?: boolean;
|
|
14
|
+
}): DroppedTree;
|
|
@@ -37,3 +37,27 @@ export function filePath(file) {
|
|
|
37
37
|
export function fileKey(file) {
|
|
38
38
|
return `${filePath(file)}|${file.size}|${file.lastModified}`;
|
|
39
39
|
}
|
|
40
|
+
export function isHiddenName(name) {
|
|
41
|
+
return name.startsWith(".");
|
|
42
|
+
}
|
|
43
|
+
export function isHiddenPath(path) {
|
|
44
|
+
return path.split("/").some(isHiddenName);
|
|
45
|
+
}
|
|
46
|
+
export function directoriesOf(paths) {
|
|
47
|
+
const directories = /* @__PURE__ */ new Set();
|
|
48
|
+
for (const path of paths) {
|
|
49
|
+
const segments = path.split("/").filter(Boolean);
|
|
50
|
+
for (let depth = 1; depth < segments.length; depth++)
|
|
51
|
+
directories.add(segments.slice(0, depth).join("/"));
|
|
52
|
+
}
|
|
53
|
+
return [...directories];
|
|
54
|
+
}
|
|
55
|
+
export function filesTree(files, options = {}) {
|
|
56
|
+
const entries = [];
|
|
57
|
+
for (const file of files) {
|
|
58
|
+
const path = file.webkitRelativePath || file.name;
|
|
59
|
+
if (options.skipHidden && isHiddenPath(path)) continue;
|
|
60
|
+
entries.push({ file, path });
|
|
61
|
+
}
|
|
62
|
+
return { files: entries, directories: directoriesOf(entries.map((entry) => entry.path)) };
|
|
63
|
+
}
|
package/dist/internal/keys.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Component, ComputedRef, InjectionKey, Ref } from 'vue';
|
|
2
|
-
import type { FieldContext } from '../composables/useField';
|
|
2
|
+
import type { FieldContext, FieldControlRole } from '../composables/useField';
|
|
3
3
|
import type { FormContext } from '../composables/useForm';
|
|
4
4
|
import type { IconRegistry } from '../composables/useIcons';
|
|
5
5
|
import type { ThemePreference } from '../composables/useTheme';
|
|
@@ -11,6 +11,7 @@ export declare const THEME_KEY: InjectionKey<Ref<ThemePreference>>;
|
|
|
11
11
|
export declare const PRESET_KEY: InjectionKey<Ref<ThemePreset>>;
|
|
12
12
|
export declare const LINK_KEY: InjectionKey<Component>;
|
|
13
13
|
export declare const FIELD_KEY: InjectionKey<FieldContext>;
|
|
14
|
+
export declare const FIELD_ROLE_KEY: InjectionKey<FieldControlRole>;
|
|
14
15
|
export declare const ROOT_KEY: InjectionKey<true>;
|
|
15
16
|
export declare const FORM_KEY: InjectionKey<FormContext>;
|
|
16
17
|
export declare const LOCALE_KEY: InjectionKey<ComputedRef<KLocale>>;
|
package/dist/internal/keys.mjs
CHANGED
|
@@ -4,6 +4,7 @@ export const THEME_KEY = Symbol("k-theme");
|
|
|
4
4
|
export const PRESET_KEY = Symbol("k-preset");
|
|
5
5
|
export const LINK_KEY = Symbol("k-link");
|
|
6
6
|
export const FIELD_KEY = Symbol("k-field");
|
|
7
|
+
export const FIELD_ROLE_KEY = Symbol("k-field-role");
|
|
7
8
|
export const ROOT_KEY = Symbol("k-root");
|
|
8
9
|
export const FORM_KEY = Symbol("k-form");
|
|
9
10
|
export const LOCALE_KEY = Symbol("k-locale");
|
|
@@ -29,6 +29,6 @@ export function matchesPath(current, target, exact) {
|
|
|
29
29
|
const from = normalize(current);
|
|
30
30
|
const to = normalize(target);
|
|
31
31
|
if (from === to) return true;
|
|
32
|
-
if (exact) return false;
|
|
32
|
+
if (exact || !to) return false;
|
|
33
33
|
return from.startsWith(to === "/" ? "/" : `${to}/`) && to !== "/";
|
|
34
34
|
}
|
package/dist/locale/en.mjs
CHANGED
|
@@ -189,6 +189,8 @@ export const en = {
|
|
|
189
189
|
list: "Selected files",
|
|
190
190
|
selected: "{count} file selected | {count} files selected",
|
|
191
191
|
directoryTitle: "Drop a folder here or click to browse",
|
|
192
|
+
mixedTitle: "Drop files or folders here or click to browse",
|
|
193
|
+
browseDirectory: "Upload a folder",
|
|
192
194
|
loading: "Uploading",
|
|
193
195
|
added: "{count} file added | {count} files added"
|
|
194
196
|
},
|
|
@@ -285,7 +287,8 @@ export const en = {
|
|
|
285
287
|
},
|
|
286
288
|
toaster: {
|
|
287
289
|
label: "Notifications",
|
|
288
|
-
dismiss: "Dismiss notification"
|
|
290
|
+
dismiss: "Dismiss notification",
|
|
291
|
+
actionHint: "Press {hotkey} to reach {action}"
|
|
289
292
|
},
|
|
290
293
|
tree: {
|
|
291
294
|
label: "Tree",
|
package/dist/locale/it.mjs
CHANGED
|
@@ -189,6 +189,8 @@ export const it = {
|
|
|
189
189
|
list: "File selezionati",
|
|
190
190
|
selected: "{count} file selezionato | {count} file selezionati",
|
|
191
191
|
directoryTitle: "Trascina una cartella qui o fai clic per sfogliare",
|
|
192
|
+
mixedTitle: "Trascina file o cartelle qui o fai clic per sfogliare",
|
|
193
|
+
browseDirectory: "Carica una cartella",
|
|
192
194
|
loading: "Caricamento in corso",
|
|
193
195
|
added: "{count} file aggiunto | {count} file aggiunti"
|
|
194
196
|
},
|
|
@@ -285,7 +287,8 @@ export const it = {
|
|
|
285
287
|
},
|
|
286
288
|
toaster: {
|
|
287
289
|
label: "Notifiche",
|
|
288
|
-
dismiss: "Chiudi notifica"
|
|
290
|
+
dismiss: "Chiudi notifica",
|
|
291
|
+
actionHint: "Premi {hotkey} per raggiungere {action}"
|
|
289
292
|
},
|
|
290
293
|
tree: {
|
|
291
294
|
label: "Albero",
|
package/dist/locale/types.d.ts
CHANGED
|
@@ -187,6 +187,8 @@ export interface KMessages {
|
|
|
187
187
|
list: string;
|
|
188
188
|
selected: string;
|
|
189
189
|
directoryTitle: string;
|
|
190
|
+
mixedTitle: string;
|
|
191
|
+
browseDirectory: string;
|
|
190
192
|
loading: string;
|
|
191
193
|
added: string;
|
|
192
194
|
};
|
|
@@ -284,6 +286,7 @@ export interface KMessages {
|
|
|
284
286
|
toaster: {
|
|
285
287
|
label: string;
|
|
286
288
|
dismiss: string;
|
|
289
|
+
actionHint: string;
|
|
287
290
|
};
|
|
288
291
|
tree: {
|
|
289
292
|
label: string;
|
package/dist/nuxt/module.mjs
CHANGED
|
@@ -292,7 +292,7 @@ function filterCollection(data, option) {
|
|
|
292
292
|
([name, alias]) => keep(name) && alias.parent in icons
|
|
293
293
|
)
|
|
294
294
|
);
|
|
295
|
-
return { prefix:
|
|
295
|
+
return { prefix: option.prefix, icons, aliases, width: data.width, height: data.height };
|
|
296
296
|
}
|
|
297
297
|
function collectionSvg(collections, name) {
|
|
298
298
|
const index = name.indexOf(":");
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
.k-btn > .k-icon {
|
|
67
|
-
transition: transform var(--k-duration-lg) var(--k-ease-
|
|
67
|
+
transition: transform var(--k-duration-lg) var(--k-ease-out);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
.k-btn:hover > .k-icon[data-icon='arrow-right'],
|
|
@@ -77,18 +77,6 @@
|
|
|
77
77
|
transform: translateX(-2px);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
.k-btn:hover > .k-icon[data-icon='plus'] {
|
|
81
|
-
transform: rotate(90deg);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.k-btn:hover > .k-icon[data-icon='arrow-path'] {
|
|
85
|
-
transform: rotate(60deg);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.k-btn:hover > .k-icon[data-icon='arrow-up-tray'] {
|
|
89
|
-
transform: translateY(-1px);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
80
|
.k-btn-xs {
|
|
93
81
|
--k-btn-size: var(--k-size-xs);
|
|
94
82
|
--k-icon-size: 0.875rem;
|