@shwfed/nuxt 0.7.10 → 0.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 +2 -1
- package/dist/runtime/components/table.d.vue.ts +68 -2
- package/dist/runtime/components/table.vue +0 -1
- package/dist/runtime/components/table.vue.d.ts +68 -2
- package/dist/runtime/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
- package/dist/runtime/components/ui/field/FieldLabel.vue +1 -1
- package/dist/runtime/components/ui/fields/Fields.vue +14 -6
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +28 -14
- package/dist/runtime/components/ui/icon-picker/IconPicker.d.vue.ts +15 -0
- package/dist/runtime/components/ui/icon-picker/IconPicker.vue +178 -0
- package/dist/runtime/components/ui/icon-picker/IconPicker.vue.d.ts +15 -0
- package/dist/runtime/components/ui/icon-picker/index.d.ts +1 -0
- package/dist/runtime/components/ui/icon-picker/index.js +1 -0
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue +1 -1
- package/dist/runtime/components/ui/input-group/InputGroupInput.vue +1 -1
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue +1 -1
- package/dist/runtime/components/ui/input-group/InputGroupTextarea.vue +1 -1
- package/dist/runtime/components/ui/native-select/NativeSelect.d.vue.ts +2 -2
- package/dist/runtime/components/ui/native-select/NativeSelect.vue +1 -1
- package/dist/runtime/components/ui/native-select/NativeSelect.vue.d.ts +2 -2
- package/dist/runtime/components/ui/switch/Switch.vue +2 -2
- package/dist/runtime/components/ui/table/Table.d.vue.ts +69 -3
- package/dist/runtime/components/ui/table/Table.vue +201 -41
- package/dist/runtime/components/ui/table/Table.vue.d.ts +69 -3
- package/dist/runtime/components/ui/table/schema.d.ts +107 -4
- package/dist/runtime/components/ui/table/schema.js +106 -90
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.d.vue.ts +68 -2
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue +732 -257
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue.d.ts +68 -2
- package/dist/runtime/components/ui/textarea/Textarea.vue +1 -1
- package/dist/runtime/layouts/default.d.vue.ts +40 -0
- package/dist/runtime/layouts/default.vue +19 -0
- package/dist/runtime/layouts/default.vue.d.ts +40 -0
- package/dist/runtime/plugins/toast/index.d.ts +2 -2
- package/dist/runtime/table-renderers/builtins.js +151 -75
- package/dist/runtime/table-renderers/registry.d.ts +1 -1
- package/dist/runtime/utils/coders.d.ts +2 -0
- package/dist/runtime/utils/coders.js +13 -0
- package/package.json +6 -6
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, addImportsDir, addComponentsDir, addRouteMiddleware } from '@nuxt/kit';
|
|
2
|
+
import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, addImportsDir, addComponentsDir, addRouteMiddleware, addLayout } from '@nuxt/kit';
|
|
3
3
|
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
|
|
4
4
|
import TailwindCSS from '@tailwindcss/vite';
|
|
5
5
|
import defu from 'defu';
|
|
@@ -75,6 +75,7 @@ const module$1 = defineNuxtModule({
|
|
|
75
75
|
name: "token",
|
|
76
76
|
global: true
|
|
77
77
|
});
|
|
78
|
+
addLayout(resolver.resolve("runtime/layouts/default.vue"), "default");
|
|
78
79
|
},
|
|
79
80
|
hooks: {
|
|
80
81
|
"build:error": (error) => {
|
|
@@ -35,9 +35,42 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
35
35
|
grow?: boolean;
|
|
36
36
|
}>[];
|
|
37
37
|
cellStyles?: import("./table.vue.js").Expression;
|
|
38
|
-
props?:
|
|
38
|
+
props?: Readonly<{
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
initialState?: Readonly<{
|
|
41
|
+
columnVisibility?: Record<string, boolean>;
|
|
42
|
+
columnOrder?: ReadonlyArray<string>;
|
|
43
|
+
columnPinning?: Readonly<{
|
|
44
|
+
left?: ReadonlyArray<string>;
|
|
45
|
+
right?: ReadonlyArray<string>;
|
|
46
|
+
}>;
|
|
47
|
+
rowPinning?: Readonly<{
|
|
48
|
+
top?: ReadonlyArray<string>;
|
|
49
|
+
bottom?: ReadonlyArray<string>;
|
|
50
|
+
}>;
|
|
51
|
+
columnFilters?: ReadonlyArray<Readonly<{
|
|
52
|
+
id: string;
|
|
53
|
+
value: unknown;
|
|
54
|
+
}>>;
|
|
55
|
+
globalFilter?: unknown;
|
|
56
|
+
sorting?: ReadonlyArray<Readonly<{
|
|
57
|
+
id: string;
|
|
58
|
+
desc: boolean;
|
|
59
|
+
}>>;
|
|
60
|
+
expanded?: boolean | Record<string, boolean>;
|
|
61
|
+
grouping?: ReadonlyArray<string>;
|
|
62
|
+
columnSizing?: Record<string, number>;
|
|
63
|
+
columnSizingInfo?: Record<string, unknown>;
|
|
64
|
+
pagination?: Readonly<{
|
|
65
|
+
pageIndex?: number;
|
|
66
|
+
pageSize?: number;
|
|
67
|
+
}>;
|
|
68
|
+
rowSelection?: Record<string, boolean>;
|
|
69
|
+
}>;
|
|
70
|
+
}>;
|
|
39
71
|
paginationLeft?: import("./table.vue.js").Markdown;
|
|
40
72
|
paginationRight?: import("./table.vue.js").Markdown;
|
|
73
|
+
paginationPageSizes?: ReadonlyArray<number>;
|
|
41
74
|
}>) => any;
|
|
42
75
|
}, string, import("vue").PublicProps, Readonly<{
|
|
43
76
|
config: import("effect").Effect.Effect<TableConfig>;
|
|
@@ -63,9 +96,42 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
63
96
|
grow?: boolean;
|
|
64
97
|
}>[];
|
|
65
98
|
cellStyles?: import("./table.vue.js").Expression;
|
|
66
|
-
props?:
|
|
99
|
+
props?: Readonly<{
|
|
100
|
+
[key: string]: unknown;
|
|
101
|
+
initialState?: Readonly<{
|
|
102
|
+
columnVisibility?: Record<string, boolean>;
|
|
103
|
+
columnOrder?: ReadonlyArray<string>;
|
|
104
|
+
columnPinning?: Readonly<{
|
|
105
|
+
left?: ReadonlyArray<string>;
|
|
106
|
+
right?: ReadonlyArray<string>;
|
|
107
|
+
}>;
|
|
108
|
+
rowPinning?: Readonly<{
|
|
109
|
+
top?: ReadonlyArray<string>;
|
|
110
|
+
bottom?: ReadonlyArray<string>;
|
|
111
|
+
}>;
|
|
112
|
+
columnFilters?: ReadonlyArray<Readonly<{
|
|
113
|
+
id: string;
|
|
114
|
+
value: unknown;
|
|
115
|
+
}>>;
|
|
116
|
+
globalFilter?: unknown;
|
|
117
|
+
sorting?: ReadonlyArray<Readonly<{
|
|
118
|
+
id: string;
|
|
119
|
+
desc: boolean;
|
|
120
|
+
}>>;
|
|
121
|
+
expanded?: boolean | Record<string, boolean>;
|
|
122
|
+
grouping?: ReadonlyArray<string>;
|
|
123
|
+
columnSizing?: Record<string, number>;
|
|
124
|
+
columnSizingInfo?: Record<string, unknown>;
|
|
125
|
+
pagination?: Readonly<{
|
|
126
|
+
pageIndex?: number;
|
|
127
|
+
pageSize?: number;
|
|
128
|
+
}>;
|
|
129
|
+
rowSelection?: Record<string, boolean>;
|
|
130
|
+
}>;
|
|
131
|
+
}>;
|
|
67
132
|
paginationLeft?: import("./table.vue.js").Markdown;
|
|
68
133
|
paginationRight?: import("./table.vue.js").Markdown;
|
|
134
|
+
paginationPageSizes?: ReadonlyArray<number>;
|
|
69
135
|
}>) => any) | undefined;
|
|
70
136
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
71
137
|
[x: string]: ((props: {
|
|
@@ -35,9 +35,42 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
35
35
|
grow?: boolean;
|
|
36
36
|
}>[];
|
|
37
37
|
cellStyles?: import("./table.vue.js").Expression;
|
|
38
|
-
props?:
|
|
38
|
+
props?: Readonly<{
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
initialState?: Readonly<{
|
|
41
|
+
columnVisibility?: Record<string, boolean>;
|
|
42
|
+
columnOrder?: ReadonlyArray<string>;
|
|
43
|
+
columnPinning?: Readonly<{
|
|
44
|
+
left?: ReadonlyArray<string>;
|
|
45
|
+
right?: ReadonlyArray<string>;
|
|
46
|
+
}>;
|
|
47
|
+
rowPinning?: Readonly<{
|
|
48
|
+
top?: ReadonlyArray<string>;
|
|
49
|
+
bottom?: ReadonlyArray<string>;
|
|
50
|
+
}>;
|
|
51
|
+
columnFilters?: ReadonlyArray<Readonly<{
|
|
52
|
+
id: string;
|
|
53
|
+
value: unknown;
|
|
54
|
+
}>>;
|
|
55
|
+
globalFilter?: unknown;
|
|
56
|
+
sorting?: ReadonlyArray<Readonly<{
|
|
57
|
+
id: string;
|
|
58
|
+
desc: boolean;
|
|
59
|
+
}>>;
|
|
60
|
+
expanded?: boolean | Record<string, boolean>;
|
|
61
|
+
grouping?: ReadonlyArray<string>;
|
|
62
|
+
columnSizing?: Record<string, number>;
|
|
63
|
+
columnSizingInfo?: Record<string, unknown>;
|
|
64
|
+
pagination?: Readonly<{
|
|
65
|
+
pageIndex?: number;
|
|
66
|
+
pageSize?: number;
|
|
67
|
+
}>;
|
|
68
|
+
rowSelection?: Record<string, boolean>;
|
|
69
|
+
}>;
|
|
70
|
+
}>;
|
|
39
71
|
paginationLeft?: import("./table.vue.js").Markdown;
|
|
40
72
|
paginationRight?: import("./table.vue.js").Markdown;
|
|
73
|
+
paginationPageSizes?: ReadonlyArray<number>;
|
|
41
74
|
}>) => any;
|
|
42
75
|
}, string, import("vue").PublicProps, Readonly<{
|
|
43
76
|
config: import("effect").Effect.Effect<TableConfig>;
|
|
@@ -63,9 +96,42 @@ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
63
96
|
grow?: boolean;
|
|
64
97
|
}>[];
|
|
65
98
|
cellStyles?: import("./table.vue.js").Expression;
|
|
66
|
-
props?:
|
|
99
|
+
props?: Readonly<{
|
|
100
|
+
[key: string]: unknown;
|
|
101
|
+
initialState?: Readonly<{
|
|
102
|
+
columnVisibility?: Record<string, boolean>;
|
|
103
|
+
columnOrder?: ReadonlyArray<string>;
|
|
104
|
+
columnPinning?: Readonly<{
|
|
105
|
+
left?: ReadonlyArray<string>;
|
|
106
|
+
right?: ReadonlyArray<string>;
|
|
107
|
+
}>;
|
|
108
|
+
rowPinning?: Readonly<{
|
|
109
|
+
top?: ReadonlyArray<string>;
|
|
110
|
+
bottom?: ReadonlyArray<string>;
|
|
111
|
+
}>;
|
|
112
|
+
columnFilters?: ReadonlyArray<Readonly<{
|
|
113
|
+
id: string;
|
|
114
|
+
value: unknown;
|
|
115
|
+
}>>;
|
|
116
|
+
globalFilter?: unknown;
|
|
117
|
+
sorting?: ReadonlyArray<Readonly<{
|
|
118
|
+
id: string;
|
|
119
|
+
desc: boolean;
|
|
120
|
+
}>>;
|
|
121
|
+
expanded?: boolean | Record<string, boolean>;
|
|
122
|
+
grouping?: ReadonlyArray<string>;
|
|
123
|
+
columnSizing?: Record<string, number>;
|
|
124
|
+
columnSizingInfo?: Record<string, unknown>;
|
|
125
|
+
pagination?: Readonly<{
|
|
126
|
+
pageIndex?: number;
|
|
127
|
+
pageSize?: number;
|
|
128
|
+
}>;
|
|
129
|
+
rowSelection?: Record<string, boolean>;
|
|
130
|
+
}>;
|
|
131
|
+
}>;
|
|
67
132
|
paginationLeft?: import("./table.vue.js").Markdown;
|
|
68
133
|
paginationRight?: import("./table.vue.js").Markdown;
|
|
134
|
+
paginationPageSizes?: ReadonlyArray<number>;
|
|
69
135
|
}>) => any) | undefined;
|
|
70
136
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
71
137
|
[x: string]: ((props: {
|
|
@@ -22,7 +22,7 @@ const forwardedProps = useForwardPropsEmits(delegatedProps, emits);
|
|
|
22
22
|
:data-inset="inset ? '' : void 0"
|
|
23
23
|
:data-variant="variant"
|
|
24
24
|
v-bind="forwardedProps"
|
|
25
|
-
:class="cn('focus:bg-[color-mix(in_srgb,var(--primary)_10%,white)] hover:bg-[color-mix(in_srgb,var(--primary)_10%,white)] focus:text-(--primary) hover:text-(--primary) data-[variant=destructive]:text-red-600 data-[variant=destructive]:focus:bg-red-200
|
|
25
|
+
:class="cn('focus:bg-[color-mix(in_srgb,var(--primary)_10%,white)] hover:bg-[color-mix(in_srgb,var(--primary)_10%,white)] focus:text-(--primary) hover:text-(--primary) data-[variant=destructive]:text-red-600 data-[variant=destructive]:focus:bg-red-200 data-[variant=destructive]:focus:text-red-600 data-[variant=destructive]:*:[svg]:text-red-600! [&_svg:not([class*=\'text-\'])]:text-zinc-300 relative flex cursor-pointer items-center gap-2 rounded-sm p-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4', props.class)"
|
|
26
26
|
>
|
|
27
27
|
<slot />
|
|
28
28
|
</DropdownMenuItem>
|
|
@@ -12,7 +12,7 @@ const props = defineProps({
|
|
|
12
12
|
:class="cn(
|
|
13
13
|
'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:text-zinc-600 group-data-[disabled=true]/field:opacity-100',
|
|
14
14
|
'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border *:data-[slot=field]:p-4',
|
|
15
|
-
'has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary
|
|
15
|
+
'has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary',
|
|
16
16
|
props.class
|
|
17
17
|
)"
|
|
18
18
|
>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { Icon } from "@iconify/vue";
|
|
3
3
|
import { useNuxtApp } from "#app";
|
|
4
|
-
import { ref, toRaw, useId, watchEffect } from "vue";
|
|
4
|
+
import { ref, toRaw, useId, watch, watchEffect } from "vue";
|
|
5
5
|
import { Field, FieldContent, FieldError, FieldLabel } from "../field";
|
|
6
6
|
import { Button } from "../button";
|
|
7
7
|
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupCombobox, InputGroupInput, InputGroupNumberField } from "../input-group";
|
|
@@ -16,7 +16,7 @@ import { Skeleton } from "../skeleton";
|
|
|
16
16
|
import z from "zod";
|
|
17
17
|
import { Effect } from "effect";
|
|
18
18
|
import { computedAsync } from "@vueuse/core";
|
|
19
|
-
import { localeC, dotPropC, expressionC } from "../../../utils/coders";
|
|
19
|
+
import { getLocalizedText, localeC, dotPropC, expressionC } from "../../../utils/coders";
|
|
20
20
|
import { CalendarDate, getLocalTimeZone } from "@internationalized/date";
|
|
21
21
|
import { format, parse } from "date-fns";
|
|
22
22
|
import { TZDate } from "@date-fns/tz";
|
|
@@ -102,6 +102,7 @@ const modelValue = defineModel("modelValue", { type: Object, ...{
|
|
|
102
102
|
} });
|
|
103
103
|
const isCheating = useCheating();
|
|
104
104
|
const isConfiguratorOpen = ref(false);
|
|
105
|
+
const displayFields = ref([]);
|
|
105
106
|
const validationErrors = ref({});
|
|
106
107
|
const calendarOpen = ref({});
|
|
107
108
|
function toCalendarDateValue(value, valueFormat) {
|
|
@@ -159,6 +160,9 @@ function validateField(field) {
|
|
|
159
160
|
function isFieldInvalid(field) {
|
|
160
161
|
return validationErrors.value[field.path] !== void 0;
|
|
161
162
|
}
|
|
163
|
+
function getFieldLabel(field) {
|
|
164
|
+
return getLocalizedText(field.title, locale.value) ?? field.path;
|
|
165
|
+
}
|
|
162
166
|
function renderValidationMessage(field) {
|
|
163
167
|
const error = validationErrors.value[field.path];
|
|
164
168
|
if (!error) return "";
|
|
@@ -180,11 +184,15 @@ function handleCalendarBlur(field) {
|
|
|
180
184
|
}, 0);
|
|
181
185
|
}
|
|
182
186
|
function handleConfiguratorConfirm(nextFields) {
|
|
187
|
+
displayFields.value = nextFields.slice();
|
|
183
188
|
emit("update:fields", nextFields);
|
|
184
189
|
}
|
|
190
|
+
watch(fields, (value) => {
|
|
191
|
+
displayFields.value = (value ?? []).slice();
|
|
192
|
+
}, { immediate: true });
|
|
185
193
|
watchEffect(() => {
|
|
186
194
|
const activePaths = /* @__PURE__ */ new Set();
|
|
187
|
-
for (const field of
|
|
195
|
+
for (const field of displayFields.value) {
|
|
188
196
|
if (!isFieldHidden(field) && !isFieldDisabled(field)) {
|
|
189
197
|
activePaths.add(field.path);
|
|
190
198
|
}
|
|
@@ -224,7 +232,7 @@ watchEffect(() => {
|
|
|
224
232
|
<FieldsConfiguratorDialog
|
|
225
233
|
v-if="fields"
|
|
226
234
|
v-model:open="isConfiguratorOpen"
|
|
227
|
-
:fields="
|
|
235
|
+
:fields="displayFields"
|
|
228
236
|
@confirm="handleConfiguratorConfirm"
|
|
229
237
|
/>
|
|
230
238
|
|
|
@@ -234,7 +242,7 @@ watchEffect(() => {
|
|
|
234
242
|
/>
|
|
235
243
|
|
|
236
244
|
<template
|
|
237
|
-
v-for="field in
|
|
245
|
+
v-for="field in displayFields"
|
|
238
246
|
:key="field.path"
|
|
239
247
|
>
|
|
240
248
|
<Field
|
|
@@ -245,7 +253,7 @@ watchEffect(() => {
|
|
|
245
253
|
:style="field.style ? $dsl.evaluate`${field.style}`() : {}"
|
|
246
254
|
>
|
|
247
255
|
<FieldLabel :for="['string', 'number'].includes(field.type) ? `${id}:${field.path}` : void 0">
|
|
248
|
-
{{ field
|
|
256
|
+
{{ getFieldLabel(field) }}
|
|
249
257
|
<span v-if="isCheating">
|
|
250
258
|
<span class="font-mono">{{ field.path }}</span>
|
|
251
259
|
</span>
|
|
@@ -4,6 +4,7 @@ import { Icon } from "@iconify/vue";
|
|
|
4
4
|
import { computed, nextTick, ref, watch } from "vue";
|
|
5
5
|
import { useI18n } from "vue-i18n";
|
|
6
6
|
import { cn } from "../../../utils/cn";
|
|
7
|
+
import { getLocalizedText } from "../../../utils/coders";
|
|
7
8
|
import { Button } from "../button";
|
|
8
9
|
import {
|
|
9
10
|
Dialog,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
DialogHeader,
|
|
14
15
|
DialogTitle
|
|
15
16
|
} from "../dialog";
|
|
17
|
+
import Locale from "../locale/Locale.vue";
|
|
16
18
|
const props = defineProps({
|
|
17
19
|
fields: { type: Array, required: true }
|
|
18
20
|
});
|
|
@@ -30,8 +32,7 @@ const generalItem = computed(() => ({
|
|
|
30
32
|
label: t("general")
|
|
31
33
|
}));
|
|
32
34
|
function getFieldLabel(field) {
|
|
33
|
-
|
|
34
|
-
return localizedTitle?.message ?? field.path;
|
|
35
|
+
return getLocalizedText(field.title, locale.value) ?? field.path;
|
|
35
36
|
}
|
|
36
37
|
const fieldItems = computed(() => draftFields.value.map((field) => ({
|
|
37
38
|
itemId: field.path,
|
|
@@ -144,6 +145,12 @@ function deleteField(itemId) {
|
|
|
144
145
|
const nextField = nextFields[deleteIndex] ?? nextFields[deleteIndex - 1];
|
|
145
146
|
selectedItemId.value = nextField?.path ?? "general";
|
|
146
147
|
}
|
|
148
|
+
function updateSelectedFieldTitle(value) {
|
|
149
|
+
if (!selectedField.value) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
draftFields.value = draftFields.value.map((field) => field.path === selectedField.value?.path ? { ...field, title: value } : field);
|
|
153
|
+
}
|
|
147
154
|
function confirmChanges() {
|
|
148
155
|
emit("confirm", draftFields.value.slice());
|
|
149
156
|
open.value = false;
|
|
@@ -230,12 +237,6 @@ function confirmChanges() {
|
|
|
230
237
|
>
|
|
231
238
|
{{ item.label }}
|
|
232
239
|
</span>
|
|
233
|
-
<span
|
|
234
|
-
data-slot="fields-configurator-field-path"
|
|
235
|
-
class="block truncate text-xs text-zinc-400"
|
|
236
|
-
>
|
|
237
|
-
{{ item.path }}
|
|
238
|
-
</span>
|
|
239
240
|
</button>
|
|
240
241
|
|
|
241
242
|
<button
|
|
@@ -286,10 +287,23 @@ function confirmChanges() {
|
|
|
286
287
|
|
|
287
288
|
<div
|
|
288
289
|
v-else-if="selectedField"
|
|
289
|
-
data-slot="fields-configurator-field-
|
|
290
|
-
class="mt-6 flex
|
|
290
|
+
data-slot="fields-configurator-field-main"
|
|
291
|
+
class="mt-6 flex flex-col gap-6"
|
|
291
292
|
>
|
|
292
|
-
|
|
293
|
+
<section
|
|
294
|
+
data-slot="fields-configurator-field-label-section"
|
|
295
|
+
class="flex flex-col gap-2"
|
|
296
|
+
>
|
|
297
|
+
<p class="text-xs font-medium text-zinc-500">
|
|
298
|
+
{{ t("field-label") }}
|
|
299
|
+
</p>
|
|
300
|
+
|
|
301
|
+
<Locale
|
|
302
|
+
data-slot="fields-configurator-field-title-locale"
|
|
303
|
+
:model-value="selectedField.title"
|
|
304
|
+
@update:model-value="updateSelectedFieldTitle"
|
|
305
|
+
/>
|
|
306
|
+
</section>
|
|
293
307
|
</div>
|
|
294
308
|
</section>
|
|
295
309
|
</div>
|
|
@@ -326,7 +340,7 @@ function confirmChanges() {
|
|
|
326
340
|
"general": "通用",
|
|
327
341
|
"general-placeholder": "这里会放置字段集合的通用配置。",
|
|
328
342
|
"general-empty": "通用配置区域预留中。",
|
|
329
|
-
"field-
|
|
343
|
+
"field-label": "Label",
|
|
330
344
|
"no-fields": "还没有字段。",
|
|
331
345
|
"drag-field": "拖拽调整字段顺序:{field}",
|
|
332
346
|
"delete-field": "删除字段:{field}",
|
|
@@ -339,7 +353,7 @@ function confirmChanges() {
|
|
|
339
353
|
"general": "共通",
|
|
340
354
|
"general-placeholder": "ここにはフィールド群の共通設定を配置します。",
|
|
341
355
|
"general-empty": "共通設定エリアはまだ準備中です。",
|
|
342
|
-
"field-
|
|
356
|
+
"field-label": "Label",
|
|
343
357
|
"no-fields": "フィールドがありません。",
|
|
344
358
|
"drag-field": "{field} の順序をドラッグで変更",
|
|
345
359
|
"delete-field": "{field} を削除",
|
|
@@ -352,7 +366,7 @@ function confirmChanges() {
|
|
|
352
366
|
"general": "General",
|
|
353
367
|
"general-placeholder": "Shared settings for this field group will live here.",
|
|
354
368
|
"general-empty": "The shared settings area is reserved for now.",
|
|
355
|
-
"field-
|
|
369
|
+
"field-label": "Label",
|
|
356
370
|
"no-fields": "No fields yet.",
|
|
357
371
|
"drag-field": "Drag to reorder field {field}",
|
|
358
372
|
"delete-field": "Delete field {field}",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'vue';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
modelValue?: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
invalid?: boolean;
|
|
7
|
+
class?: HTMLAttributes['class'];
|
|
8
|
+
};
|
|
9
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
|
+
"update:modelValue": (args_0: string | undefined) => any;
|
|
11
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
|
+
"onUpdate:modelValue"?: ((args_0: string | undefined) => any) | undefined;
|
|
13
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { icons } from "@iconify-json/fluent";
|
|
3
|
+
import { Icon } from "@iconify/vue";
|
|
4
|
+
import { useVirtualizer } from "@tanstack/vue-virtual";
|
|
5
|
+
import { computed, shallowRef, ref, watch } from "vue";
|
|
6
|
+
import { cn } from "../../../utils/cn";
|
|
7
|
+
import { InputGroup, InputGroupAddon, InputGroupInput } from "../input-group";
|
|
8
|
+
defineOptions({
|
|
9
|
+
inheritAttrs: false
|
|
10
|
+
});
|
|
11
|
+
const ICON_COLUMNS = 6;
|
|
12
|
+
const ICON_ROW_HEIGHT = 60;
|
|
13
|
+
const props = defineProps({
|
|
14
|
+
modelValue: { type: String, required: false },
|
|
15
|
+
placeholder: { type: String, required: false },
|
|
16
|
+
disabled: { type: Boolean, required: false },
|
|
17
|
+
invalid: { type: Boolean, required: false },
|
|
18
|
+
class: { type: null, required: false }
|
|
19
|
+
});
|
|
20
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
21
|
+
const availableIcons = Object.entries(icons.icons).filter(([name]) => name.endsWith("-20-regular")).map(([name, icon]) => ({
|
|
22
|
+
id: `fluent:${name}`,
|
|
23
|
+
icon
|
|
24
|
+
}));
|
|
25
|
+
const availableIconIds = new Set(availableIcons.map((icon) => icon.id));
|
|
26
|
+
const searchQuery = ref("");
|
|
27
|
+
const galleryElement = shallowRef(null);
|
|
28
|
+
const selectedIcon = computed(() => props.modelValue && availableIconIds.has(props.modelValue) ? availableIcons.find((icon) => icon.id === props.modelValue) : void 0);
|
|
29
|
+
const filteredIcons = computed(() => {
|
|
30
|
+
const term = searchQuery.value.trim().toLowerCase();
|
|
31
|
+
if (!term) {
|
|
32
|
+
return availableIcons;
|
|
33
|
+
}
|
|
34
|
+
return availableIcons.filter((icon) => icon.id.includes(term));
|
|
35
|
+
});
|
|
36
|
+
const rowCount = computed(() => Math.ceil(filteredIcons.value.length / ICON_COLUMNS));
|
|
37
|
+
const rowVirtualizer = useVirtualizer(computed(() => ({
|
|
38
|
+
count: rowCount.value,
|
|
39
|
+
getScrollElement: () => galleryElement.value,
|
|
40
|
+
estimateSize: () => ICON_ROW_HEIGHT,
|
|
41
|
+
overscan: 4
|
|
42
|
+
})));
|
|
43
|
+
watch(() => props.modelValue, (value) => {
|
|
44
|
+
if (value && availableIconIds.has(value)) {
|
|
45
|
+
searchQuery.value = "";
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
function iconsForRow(index) {
|
|
49
|
+
const start = index * ICON_COLUMNS;
|
|
50
|
+
return filteredIcons.value.slice(start, start + ICON_COLUMNS);
|
|
51
|
+
}
|
|
52
|
+
function selectIcon(iconId) {
|
|
53
|
+
if (props.disabled) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
searchQuery.value = "";
|
|
57
|
+
emit("update:modelValue", iconId);
|
|
58
|
+
}
|
|
59
|
+
function clearIcon() {
|
|
60
|
+
if (props.disabled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
searchQuery.value = "";
|
|
64
|
+
emit("update:modelValue", void 0);
|
|
65
|
+
}
|
|
66
|
+
function handleSearchUpdate(value) {
|
|
67
|
+
searchQuery.value = String(value);
|
|
68
|
+
}
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<div
|
|
73
|
+
v-bind="$attrs"
|
|
74
|
+
data-slot="icon-picker"
|
|
75
|
+
:data-disabled="props.disabled ? 'true' : void 0"
|
|
76
|
+
:class="cn(
|
|
77
|
+
'flex min-h-88 flex-col overflow-hidden rounded-xl border bg-white',
|
|
78
|
+
props.invalid ? 'border-red-400' : 'border-zinc-200',
|
|
79
|
+
props.disabled ? 'bg-zinc-50' : void 0,
|
|
80
|
+
props.class
|
|
81
|
+
)"
|
|
82
|
+
>
|
|
83
|
+
<section
|
|
84
|
+
v-if="selectedIcon"
|
|
85
|
+
data-slot="icon-picker-selected"
|
|
86
|
+
class="flex flex-1 flex-col items-center justify-center gap-4 px-6 py-8 text-center"
|
|
87
|
+
>
|
|
88
|
+
<div
|
|
89
|
+
data-slot="icon-picker-hero"
|
|
90
|
+
:data-icon-id="selectedIcon.id"
|
|
91
|
+
class="relative flex size-40 items-center justify-center rounded-3xl border border-zinc-200 bg-zinc-50 text-zinc-700 shadow-xs"
|
|
92
|
+
>
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
data-slot="icon-picker-clear"
|
|
96
|
+
class="absolute right-3 top-3 flex size-8 items-center justify-center rounded-full border border-zinc-200 bg-white text-zinc-500 transition-colors hover:border-red-200 hover:bg-red-50 hover:text-red-600 disabled:pointer-events-none disabled:opacity-60"
|
|
97
|
+
:disabled="props.disabled"
|
|
98
|
+
@click="clearIcon"
|
|
99
|
+
>
|
|
100
|
+
<Icon icon="fluent:dismiss-20-regular" />
|
|
101
|
+
</button>
|
|
102
|
+
<Icon
|
|
103
|
+
:icon="selectedIcon.icon"
|
|
104
|
+
class="size-16"
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<p
|
|
109
|
+
data-slot="icon-picker-name"
|
|
110
|
+
class="font-mono text-sm text-zinc-500"
|
|
111
|
+
>
|
|
112
|
+
{{ selectedIcon.id }}
|
|
113
|
+
</p>
|
|
114
|
+
</section>
|
|
115
|
+
|
|
116
|
+
<template v-else>
|
|
117
|
+
<div
|
|
118
|
+
data-slot="icon-picker-search"
|
|
119
|
+
class="border-b border-zinc-200 p-4"
|
|
120
|
+
>
|
|
121
|
+
<InputGroup class="overflow-hidden">
|
|
122
|
+
<InputGroupAddon>
|
|
123
|
+
<Icon icon="fluent:search-20-regular" />
|
|
124
|
+
</InputGroupAddon>
|
|
125
|
+
<InputGroupInput
|
|
126
|
+
:model-value="searchQuery"
|
|
127
|
+
:placeholder="props.placeholder ?? 'Search icons'"
|
|
128
|
+
:disabled="props.disabled"
|
|
129
|
+
@update:model-value="handleSearchUpdate"
|
|
130
|
+
/>
|
|
131
|
+
</InputGroup>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div
|
|
135
|
+
v-if="filteredIcons.length > 0"
|
|
136
|
+
ref="galleryElement"
|
|
137
|
+
data-slot="icon-picker-gallery"
|
|
138
|
+
class="h-80 overflow-auto px-4 py-4"
|
|
139
|
+
>
|
|
140
|
+
<div
|
|
141
|
+
class="relative w-full"
|
|
142
|
+
:style="{ height: `${rowVirtualizer.getTotalSize()}px` }"
|
|
143
|
+
>
|
|
144
|
+
<div
|
|
145
|
+
v-for="row in rowVirtualizer.getVirtualItems()"
|
|
146
|
+
:key="String(row.key)"
|
|
147
|
+
class="absolute left-0 top-0 grid w-full grid-cols-6 gap-2"
|
|
148
|
+
:style="{
|
|
149
|
+
height: `${row.size}px`,
|
|
150
|
+
transform: `translateY(${row.start}px)`
|
|
151
|
+
}"
|
|
152
|
+
>
|
|
153
|
+
<button
|
|
154
|
+
v-for="item in iconsForRow(row.index)"
|
|
155
|
+
:key="item.id"
|
|
156
|
+
type="button"
|
|
157
|
+
data-slot="icon-picker-item"
|
|
158
|
+
:data-icon-id="item.id"
|
|
159
|
+
class="flex h-12 items-center justify-center rounded-lg border border-transparent bg-zinc-50 text-zinc-600 transition-colors hover:border-zinc-200 hover:bg-zinc-100 hover:text-zinc-800 disabled:pointer-events-none disabled:opacity-60"
|
|
160
|
+
:disabled="props.disabled"
|
|
161
|
+
@click="selectIcon(item.id)"
|
|
162
|
+
>
|
|
163
|
+
<Icon :icon="item.icon" />
|
|
164
|
+
</button>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div
|
|
170
|
+
v-else
|
|
171
|
+
data-slot="icon-picker-empty"
|
|
172
|
+
class="flex h-80 items-center justify-center px-6 text-center text-sm text-zinc-400"
|
|
173
|
+
>
|
|
174
|
+
No icons match your search.
|
|
175
|
+
</div>
|
|
176
|
+
</template>
|
|
177
|
+
</div>
|
|
178
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'vue';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
modelValue?: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
invalid?: boolean;
|
|
7
|
+
class?: HTMLAttributes['class'];
|
|
8
|
+
};
|
|
9
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
10
|
+
"update:modelValue": (args_0: string | undefined) => any;
|
|
11
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
|
+
"onUpdate:modelValue"?: ((args_0: string | undefined) => any) | undefined;
|
|
13
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as IconPicker } from './IconPicker.vue.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as IconPicker } from "./IconPicker.vue";
|
|
@@ -20,7 +20,7 @@ function handleBlur(event) {
|
|
|
20
20
|
:data-value="!!filterState.search"
|
|
21
21
|
:aria-invalid="props.invalid ? 'true' : void 0"
|
|
22
22
|
:class="cn(
|
|
23
|
-
'flex-1 px-2 rounded-none border-none bg-transparent shadow-none
|
|
23
|
+
'flex-1 px-2 rounded-none border-none bg-transparent shadow-none peer outline-none disabled:cursor-not-allowed disabled:text-zinc-600 disabled:opacity-100'
|
|
24
24
|
)"
|
|
25
25
|
@blur="handleBlur"
|
|
26
26
|
/>
|
|
@@ -13,7 +13,7 @@ const props = defineProps({
|
|
|
13
13
|
data-slot="input-group-control"
|
|
14
14
|
:treat-empty-as-different-state-from-null="treatEmptyAsDifferentStateFromNull"
|
|
15
15
|
:class="cn(
|
|
16
|
-
'flex-1 rounded-none border-0 bg-transparent shadow-none
|
|
16
|
+
'flex-1 rounded-none border-0 bg-transparent shadow-none peer disabled:text-zinc-600 disabled:opacity-100',
|
|
17
17
|
props.class
|
|
18
18
|
)"
|
|
19
19
|
/>
|
|
@@ -43,7 +43,7 @@ const delegatedProps = reactiveOmit(props, "class", "invalid");
|
|
|
43
43
|
:data-value="typeof delegatedProps.modelValue === 'number'"
|
|
44
44
|
:aria-invalid="props.invalid ? 'true' : void 0"
|
|
45
45
|
:class="cn(
|
|
46
|
-
'flex-1 px-2 py-1 rounded-none w-full text-sm text-zinc-700 border-0 bg-transparent shadow-none
|
|
46
|
+
'flex-1 px-2 py-1 rounded-none w-full text-sm text-zinc-700 border-0 bg-transparent shadow-none outline-none peer disabled:cursor-not-allowed disabled:text-zinc-600 disabled:opacity-100'
|
|
47
47
|
)"
|
|
48
48
|
@blur="emits('blur', $event)"
|
|
49
49
|
/>
|