@sfxcode/nuxt-ui-formkit 1.2.11 → 1.3.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/runtime/components/inputs/FUSelect.vue +6 -1
- package/dist/runtime/components/inputs/FUSelectMenu.vue +6 -1
- package/dist/runtime/composables/useFormKitEditor.d.ts +36 -0
- package/dist/runtime/composables/useFormKitEditor.js +120 -0
- package/dist/runtime/composables/useFormKitEditorSchema.d.ts +74 -0
- package/dist/runtime/composables/useFormKitEditorSchema.js +337 -0
- package/package.json +12 -12
package/dist/module.json
CHANGED
|
@@ -10,12 +10,17 @@ const { handleInput, handleChange, isInvalid, color, styleClass, modelValue, ite
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<template>
|
|
13
|
+
<!-- Nuxt UI's own USelect shrinks its trigger to whatever's currently
|
|
14
|
+
selected (or just the chevron, unselected) - `w-fit min-w-48` keeps it from
|
|
15
|
+
collapsing that small while still growing for longer option labels, rather
|
|
16
|
+
than forcing every instance to one fixed width. Only applies when the
|
|
17
|
+
consumer hasn't already set their own `class`. -->
|
|
13
18
|
<USelect
|
|
14
19
|
:id="context.id"
|
|
15
20
|
v-model="modelValue"
|
|
16
21
|
:name="context.node.name"
|
|
17
22
|
v-bind="{ ...context?.attrs }"
|
|
18
|
-
:class="styleClass"
|
|
23
|
+
:class="[styleClass, !context?.attrs?.class && 'w-fit min-w-48']"
|
|
19
24
|
:disabled="!!context?.disabled"
|
|
20
25
|
:style="context?.attrs.style"
|
|
21
26
|
:color="color"
|
|
@@ -10,12 +10,17 @@ const { handleInput, handleChange, isInvalid, styleClass, color, modelValue, ite
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<template>
|
|
13
|
+
<!-- Nuxt UI's own USelectMenu shrinks its trigger to whatever's currently
|
|
14
|
+
selected (or just the chevron, unselected) - `w-fit min-w-48` keeps it from
|
|
15
|
+
collapsing that small while still growing for longer option labels, rather
|
|
16
|
+
than forcing every instance to one fixed width. Only applies when the
|
|
17
|
+
consumer hasn't already set their own `class`. -->
|
|
13
18
|
<USelectMenu
|
|
14
19
|
:id="context.id"
|
|
15
20
|
v-model="modelValue"
|
|
16
21
|
:name="context.node.name"
|
|
17
22
|
v-bind="{ ...context?.attrs }"
|
|
18
|
-
:class="styleClass"
|
|
23
|
+
:class="[styleClass, !context?.attrs?.class && 'w-fit min-w-48']"
|
|
19
24
|
:disabled="!!context?.disabled"
|
|
20
25
|
:style="context?.attrs.style"
|
|
21
26
|
:color="color"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
type EditorData = Record<string, unknown> & {
|
|
2
|
+
_dollar_formkit?: string;
|
|
3
|
+
$formkit?: string;
|
|
4
|
+
attrs?: {
|
|
5
|
+
attrKey?: string;
|
|
6
|
+
attrValue?: unknown;
|
|
7
|
+
}[];
|
|
8
|
+
options?: unknown[];
|
|
9
|
+
label?: unknown;
|
|
10
|
+
legend?: unknown;
|
|
11
|
+
outerClass?: unknown;
|
|
12
|
+
wrapperClass?: unknown;
|
|
13
|
+
innerClass?: unknown;
|
|
14
|
+
readonly?: unknown;
|
|
15
|
+
disabled?: unknown;
|
|
16
|
+
preserve?: unknown;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Converts between a single FormKit schema node (`{ $formkit: 'nuxtUIInput', ... }`)
|
|
20
|
+
* and the "editor data" shape used by the property-editor form built in
|
|
21
|
+
* `useFormKitEditorSchema`. The editor form is itself a FormKit form, so its
|
|
22
|
+
* field names can't collide with FormKit's own `$`-prefixed schema syntax -
|
|
23
|
+
* `_dollar_formkit` stands in for `$formkit` while editing and gets mapped
|
|
24
|
+
* back on the way out.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useFormKitEditor(): {
|
|
27
|
+
inputNames: string[];
|
|
28
|
+
outputNames: string[];
|
|
29
|
+
inputNamesWithOptions: string[];
|
|
30
|
+
inputNamesWithLegend: string[];
|
|
31
|
+
editorDataToSchema: (data: EditorData) => Record<string, unknown>;
|
|
32
|
+
editorDataToJson: (data: EditorData) => string;
|
|
33
|
+
editorDataToCode: (data: EditorData) => string;
|
|
34
|
+
schemaToEditorData: (schema: Record<string, unknown>) => EditorData;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export function useFormKitEditor() {
|
|
2
|
+
const inputNamesWithOptions = ["nuxtUICheckboxGroup", "nuxtUIInputMenu", "nuxtUIListbox", "nuxtUIRadioGroup", "nuxtUISelect", "nuxtUISelectMenu", "nuxtUITree"];
|
|
3
|
+
const inputNamesWithLegend = ["nuxtUICheckboxGroup", "nuxtUIRadioGroup"];
|
|
4
|
+
const inputNames = ["nuxtUICalendar", "nuxtUICheckbox", "nuxtUICheckboxGroup", "nuxtUIColorPicker", "nuxtUIEditor", "nuxtUIFileUpload", "nuxtUIInput", "nuxtUIInputDate", "nuxtUIInputMenu", "nuxtUIInputNumber", "nuxtUIInputRating", "nuxtUIInputTags", "nuxtUIInputTime", "nuxtUIListbox", "nuxtUIPinInput", "nuxtUIRadioGroup", "nuxtUISelect", "nuxtUISelectMenu", "nuxtUISlider", "nuxtUISwitch", "nuxtUITextarea", "nuxtUITree"].sort();
|
|
5
|
+
const outputNames = ["nuxtUIOutputBadge", "nuxtUIOutputBoolean", "nuxtUIOutputDate", "nuxtUIOutputLink", "nuxtUIOutputList", "nuxtUIOutputNumber", "nuxtUIOutputProgress", "nuxtUIOutputRating", "nuxtUIOutputText", "nuxtUIOutputUser"].sort();
|
|
6
|
+
function parseAttrValue(value) {
|
|
7
|
+
if (typeof value !== "string")
|
|
8
|
+
return value;
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
if (trimmed.length === 0)
|
|
11
|
+
return value;
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(trimmed);
|
|
14
|
+
} catch {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function sanitizeOptions(options) {
|
|
19
|
+
if (!Array.isArray(options))
|
|
20
|
+
return void 0;
|
|
21
|
+
return options.filter((option) => Boolean(option) && typeof option === "object").filter((option) => option.label !== "" || option.value !== "").map((option) => ({ ...option }));
|
|
22
|
+
}
|
|
23
|
+
function editorDataToSchema(data) {
|
|
24
|
+
if (!data)
|
|
25
|
+
return {};
|
|
26
|
+
const formkitInput = data._dollar_formkit;
|
|
27
|
+
let extraAttrs = {};
|
|
28
|
+
if (data.attrs && Array.isArray(data.attrs) && data.attrs.length > 0) {
|
|
29
|
+
const mapped = data.attrs.filter((entry) => entry && typeof entry === "object" && entry.attrKey).map((entry) => [entry.attrKey, parseAttrValue(entry.attrValue)]);
|
|
30
|
+
extraAttrs = Object.fromEntries(mapped);
|
|
31
|
+
}
|
|
32
|
+
const defaultObject = {
|
|
33
|
+
readonly: data.readonly === true ? true : void 0,
|
|
34
|
+
disabled: data.disabled === true ? true : void 0,
|
|
35
|
+
preserve: data.preserve === true ? true : void 0
|
|
36
|
+
};
|
|
37
|
+
const outerClass = data.outerClass ? String(data.outerClass).trim() : void 0;
|
|
38
|
+
const wrapperClass = data.wrapperClass ? String(data.wrapperClass).trim() : void 0;
|
|
39
|
+
const innerClass = data.innerClass ? String(data.innerClass).trim() : void 0;
|
|
40
|
+
const undefinedObject = {
|
|
41
|
+
attrs: void 0,
|
|
42
|
+
schemaResultFormKey: void 0,
|
|
43
|
+
_dollar_formkit: void 0,
|
|
44
|
+
section: void 0,
|
|
45
|
+
slots: void 0
|
|
46
|
+
};
|
|
47
|
+
const useOptions = formkitInput ? inputNamesWithOptions.includes(formkitInput) : false;
|
|
48
|
+
const useLegend = formkitInput ? inputNamesWithLegend.includes(formkitInput) : false;
|
|
49
|
+
const result = {
|
|
50
|
+
...data,
|
|
51
|
+
$formkit: formkitInput,
|
|
52
|
+
...extraAttrs,
|
|
53
|
+
...undefinedObject,
|
|
54
|
+
...defaultObject,
|
|
55
|
+
outerClass,
|
|
56
|
+
wrapperClass,
|
|
57
|
+
innerClass,
|
|
58
|
+
// Cloned (not the same array/objects as `data.options`) so nothing
|
|
59
|
+
// downstream - the live preview's own rendered Select, `JSON.stringify`
|
|
60
|
+
// for the debug/copy views - can ever mutate the editor form's own
|
|
61
|
+
// repeater value by touching a shared reference. Also drops rows the
|
|
62
|
+
// user hasn't finished filling in yet, which Nuxt UI's Select/SelectMenu
|
|
63
|
+
// otherwise reject for having an empty `value`.
|
|
64
|
+
options: useOptions ? sanitizeOptions(data.options) : void 0,
|
|
65
|
+
label: useLegend ? void 0 : data.label,
|
|
66
|
+
legend: useLegend ? data.legend : void 0
|
|
67
|
+
};
|
|
68
|
+
for (const key in result) {
|
|
69
|
+
const value = result[key];
|
|
70
|
+
if (typeof value === "string" && value.trim().length === 0)
|
|
71
|
+
result[key] = void 0;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function editorDataToJson(data) {
|
|
76
|
+
if (!data)
|
|
77
|
+
return "{}";
|
|
78
|
+
return JSON.stringify(editorDataToSchema(data));
|
|
79
|
+
}
|
|
80
|
+
function objectToString(data) {
|
|
81
|
+
if (!data)
|
|
82
|
+
return "{}";
|
|
83
|
+
return `{ ${Object.entries(data).filter(([, value]) => value !== void 0).map(([key, value]) => {
|
|
84
|
+
if (key === "options" && Array.isArray(value) && value.length > 0) {
|
|
85
|
+
const options = value.map((option) => option && typeof option === "object" ? objectToString(option) : JSON.stringify(option));
|
|
86
|
+
return `${key}: [${options.join(", ")}]`;
|
|
87
|
+
}
|
|
88
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
89
|
+
return `${key}: ${value}`;
|
|
90
|
+
}
|
|
91
|
+
return `${key}: '${value}'`;
|
|
92
|
+
}).join(", ")} }`;
|
|
93
|
+
}
|
|
94
|
+
function editorDataToCode(data) {
|
|
95
|
+
if (!data)
|
|
96
|
+
return "{}";
|
|
97
|
+
try {
|
|
98
|
+
const jsonData = editorDataToJson(data);
|
|
99
|
+
return objectToString(JSON.parse(jsonData));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error("Error in editorDataToCode:", error);
|
|
102
|
+
return "{}";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function schemaToEditorData(schema) {
|
|
106
|
+
if (!schema)
|
|
107
|
+
return {};
|
|
108
|
+
return { ...schema, _dollar_formkit: schema.$formkit };
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
inputNames,
|
|
112
|
+
outputNames,
|
|
113
|
+
inputNamesWithOptions,
|
|
114
|
+
inputNamesWithLegend,
|
|
115
|
+
editorDataToSchema,
|
|
116
|
+
editorDataToJson,
|
|
117
|
+
editorDataToCode,
|
|
118
|
+
schemaToEditorData
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export declare function useFormKitEditorSchema(isChangeTypePossible?: boolean): {
|
|
2
|
+
editorSchema: (typeOptionsOverride?: {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
}[]) => (string | import("@formkit/core").FormKitSchemaDOMNode | import("@formkit/core").FormKitSchemaComponent | import("@formkit/core").FormKitSchemaCondition | import("@formkit/core").FormKitSchemaFormKit | {
|
|
6
|
+
$formkit: string;
|
|
7
|
+
if: string;
|
|
8
|
+
name: string;
|
|
9
|
+
label: string;
|
|
10
|
+
help: string;
|
|
11
|
+
listClass: string;
|
|
12
|
+
listItemClass: string;
|
|
13
|
+
insertButtonLabel: string;
|
|
14
|
+
alwaysDisplayInsertButton: boolean;
|
|
15
|
+
displayDeleteButton: boolean;
|
|
16
|
+
draggable: boolean;
|
|
17
|
+
displayDragHandle: boolean;
|
|
18
|
+
dragHandleClass: string;
|
|
19
|
+
buttonGroupClass: string;
|
|
20
|
+
hideMoveButtons: boolean;
|
|
21
|
+
newItem: {
|
|
22
|
+
label: string;
|
|
23
|
+
value: string;
|
|
24
|
+
attrKey?: undefined;
|
|
25
|
+
attrValue?: undefined;
|
|
26
|
+
};
|
|
27
|
+
key: string;
|
|
28
|
+
preserve: boolean;
|
|
29
|
+
children: {
|
|
30
|
+
$formkit: string;
|
|
31
|
+
label: string;
|
|
32
|
+
name: string;
|
|
33
|
+
outerClass: string;
|
|
34
|
+
}[];
|
|
35
|
+
} | {
|
|
36
|
+
$formkit: string;
|
|
37
|
+
if: string;
|
|
38
|
+
name: string;
|
|
39
|
+
label: string;
|
|
40
|
+
help: string;
|
|
41
|
+
listClass: string;
|
|
42
|
+
listItemClass: string;
|
|
43
|
+
insertButtonLabel: string;
|
|
44
|
+
alwaysDisplayInsertButton: boolean;
|
|
45
|
+
displayDeleteButton: boolean;
|
|
46
|
+
draggable: boolean;
|
|
47
|
+
displayDragHandle: boolean;
|
|
48
|
+
dragHandleClass: string;
|
|
49
|
+
buttonGroupClass: string;
|
|
50
|
+
hideMoveButtons: boolean;
|
|
51
|
+
newItem: {
|
|
52
|
+
attrKey: string;
|
|
53
|
+
attrValue: string;
|
|
54
|
+
label?: undefined;
|
|
55
|
+
value?: undefined;
|
|
56
|
+
};
|
|
57
|
+
key: string;
|
|
58
|
+
preserve: boolean;
|
|
59
|
+
children: {
|
|
60
|
+
$formkit: string;
|
|
61
|
+
label: string;
|
|
62
|
+
name: string;
|
|
63
|
+
outerClass: string;
|
|
64
|
+
}[];
|
|
65
|
+
})[];
|
|
66
|
+
fieldTypeOptions: {
|
|
67
|
+
label: string;
|
|
68
|
+
value: string;
|
|
69
|
+
}[];
|
|
70
|
+
typeOptions: (names: string[], kind?: string) => {
|
|
71
|
+
label: string;
|
|
72
|
+
value: string;
|
|
73
|
+
}[];
|
|
74
|
+
};
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { useFormKitSchema } from "./useFormKitSchema.js";
|
|
2
|
+
import { useFormKitEditor } from "./useFormKitEditor.ts";
|
|
3
|
+
let instanceCounter = 0;
|
|
4
|
+
export function useFormKitEditorSchema(isChangeTypePossible = true) {
|
|
5
|
+
const { addElement } = useFormKitSchema();
|
|
6
|
+
const { inputNames, outputNames, inputNamesWithOptions, inputNamesWithLegend } = useFormKitEditor();
|
|
7
|
+
instanceCounter += 1;
|
|
8
|
+
const dollarFormkitId = `_dollar_formkit_${instanceCounter}`;
|
|
9
|
+
const sectionId = `section_${instanceCounter}`;
|
|
10
|
+
function ifGet(id, value) {
|
|
11
|
+
return `$get(${id}).value === '${value}'`;
|
|
12
|
+
}
|
|
13
|
+
function ifSection(section) {
|
|
14
|
+
return ifGet(sectionId, section);
|
|
15
|
+
}
|
|
16
|
+
function ifAnyType(names) {
|
|
17
|
+
return names.map((name) => ifGet(dollarFormkitId, name)).join(" || ");
|
|
18
|
+
}
|
|
19
|
+
function ifNoneType(names) {
|
|
20
|
+
return names.map((name) => `$get(${dollarFormkitId}).value !== '${name}'`).join(" && ");
|
|
21
|
+
}
|
|
22
|
+
const optionsCapableIf = `(${ifAnyType(inputNamesWithOptions)})`;
|
|
23
|
+
const notOptionsCapableIf = `(${ifNoneType(inputNamesWithOptions)})`;
|
|
24
|
+
const legendTypeIf = `(${ifAnyType(inputNamesWithLegend)})`;
|
|
25
|
+
const notLegendTypeIf = `(${ifNoneType(inputNamesWithLegend)})`;
|
|
26
|
+
function typeOptions(names, kind) {
|
|
27
|
+
return names.map((name) => ({ label: kind ? `${name} (${kind})` : name, value: name }));
|
|
28
|
+
}
|
|
29
|
+
const fieldTypeOptions = [...typeOptions(inputNames), ...typeOptions(outputNames, "output")];
|
|
30
|
+
const sectionOptions = [
|
|
31
|
+
{ label: "Base", value: "base" },
|
|
32
|
+
{ label: "Display", value: "display" },
|
|
33
|
+
{ label: "Style", value: "style" },
|
|
34
|
+
{ label: "Validation", value: "validation" },
|
|
35
|
+
{ label: "Options", value: "options" },
|
|
36
|
+
{ label: "Attributes", value: "attrs" }
|
|
37
|
+
];
|
|
38
|
+
const validationVisibilityOptions = [
|
|
39
|
+
{ label: "Blur", value: "blur" },
|
|
40
|
+
{ label: "Live", value: "live" },
|
|
41
|
+
{ label: "Dirty", value: "dirty" },
|
|
42
|
+
{ label: "Submit", value: "submit" }
|
|
43
|
+
];
|
|
44
|
+
function editorSchema(typeOptionsOverride = fieldTypeOptions) {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
$formkit: "nuxtUISelectMenu",
|
|
48
|
+
// `$get()` addresses nodes by `id`, not `name` - this has to match
|
|
49
|
+
// `dollarFormkitId`, the identifier `ifGet`/`ifAnyType`/`ifNoneType`
|
|
50
|
+
// use below. `name` stays the fixed `'_dollar_formkit'` string -
|
|
51
|
+
// that's the form-data key consumers (`useFormKitEditor`, the
|
|
52
|
+
// Form Builder/Input Editor pages) read, and unlike `id` it isn't
|
|
53
|
+
// addressed through FormKit's cross-instance global registry, so it
|
|
54
|
+
// doesn't need to be unique.
|
|
55
|
+
// Disabled rather than hidden (`if: false`) when type-switching is
|
|
56
|
+
// turned off - hiding would unmount this node entirely, and every
|
|
57
|
+
// other `$get(dollarFormkitId)` comparison below (legend vs label,
|
|
58
|
+
// the Options tab's capability check) would then find nothing and
|
|
59
|
+
// always evaluate false.
|
|
60
|
+
id: dollarFormkitId,
|
|
61
|
+
name: "_dollar_formkit",
|
|
62
|
+
disabled: !isChangeTypePossible,
|
|
63
|
+
label: "Field Type",
|
|
64
|
+
value: "nuxtUIInput",
|
|
65
|
+
// Without `valueKey`, nuxtUISelectMenu's model value is the whole
|
|
66
|
+
// `{ label, value }` option object rather than just its `value` -
|
|
67
|
+
// every `$get(_dollar_formkit).value === '...'` comparison below
|
|
68
|
+
// would silently compare an object to a string and always be false.
|
|
69
|
+
valueKey: "value",
|
|
70
|
+
options: typeOptionsOverride,
|
|
71
|
+
key: "schema_inputSelection",
|
|
72
|
+
preserve: true
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
$formkit: "nuxtUIInput",
|
|
76
|
+
name: "name",
|
|
77
|
+
label: "Field Name",
|
|
78
|
+
validation: "required",
|
|
79
|
+
validationVisibility: "live",
|
|
80
|
+
key: "schema_name",
|
|
81
|
+
preserve: true
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
$formkit: "nuxtUIRadioGroup",
|
|
85
|
+
// See the `dollarFormkitId` comment above - same reasoning, `id`
|
|
86
|
+
// needs to be globally unique per mount, `name` doesn't.
|
|
87
|
+
id: sectionId,
|
|
88
|
+
name: "section",
|
|
89
|
+
legend: "Properties",
|
|
90
|
+
options: sectionOptions,
|
|
91
|
+
orientation: "horizontal",
|
|
92
|
+
// Nuxt UI's horizontal RadioGroup fieldset is `flex flex-row` with no
|
|
93
|
+
// wrap by default - in the narrow Properties panel that overflows
|
|
94
|
+
// instead of dropping "Attributes" etc. to a second line.
|
|
95
|
+
ui: { fieldset: "flex-wrap" },
|
|
96
|
+
value: "base",
|
|
97
|
+
key: "schema_section",
|
|
98
|
+
preserve: true
|
|
99
|
+
},
|
|
100
|
+
// --- Base -----------------------------------------------------------
|
|
101
|
+
{
|
|
102
|
+
$formkit: "nuxtUIInput",
|
|
103
|
+
if: `${ifSection("base")} && ${notLegendTypeIf}`,
|
|
104
|
+
name: "label",
|
|
105
|
+
label: "Field Label",
|
|
106
|
+
key: "schema_label",
|
|
107
|
+
preserve: true
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
$formkit: "nuxtUIInput",
|
|
111
|
+
if: `${ifSection("base")} && ${legendTypeIf}`,
|
|
112
|
+
name: "legend",
|
|
113
|
+
label: "Field Legend",
|
|
114
|
+
key: "schema_legend",
|
|
115
|
+
preserve: true
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
$formkit: "nuxtUIInput",
|
|
119
|
+
if: ifSection("base"),
|
|
120
|
+
name: "help",
|
|
121
|
+
label: "Help Text",
|
|
122
|
+
key: "schema_help",
|
|
123
|
+
preserve: true
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
$formkit: "nuxtUIInput",
|
|
127
|
+
if: ifSection("base"),
|
|
128
|
+
name: "value",
|
|
129
|
+
label: "Default Value",
|
|
130
|
+
key: "schema_value",
|
|
131
|
+
preserve: true
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
$formkit: "nuxtUIInput",
|
|
135
|
+
if: ifSection("base"),
|
|
136
|
+
name: "id",
|
|
137
|
+
label: "Field ID",
|
|
138
|
+
key: "schema_id",
|
|
139
|
+
preserve: true
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
$formkit: "nuxtUIInput",
|
|
143
|
+
if: ifSection("base"),
|
|
144
|
+
name: "key",
|
|
145
|
+
label: "Schema Key",
|
|
146
|
+
help: "Forces FormKit to re-render this node when the key changes.",
|
|
147
|
+
key: "schema_key",
|
|
148
|
+
preserve: true
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
$formkit: "nuxtUICheckbox",
|
|
152
|
+
if: ifSection("base"),
|
|
153
|
+
name: "preserve",
|
|
154
|
+
label: "Preserve value when hidden",
|
|
155
|
+
key: "schema_preserve",
|
|
156
|
+
value: false,
|
|
157
|
+
preserve: true
|
|
158
|
+
},
|
|
159
|
+
// --- Display ----------------------------------------------------------
|
|
160
|
+
{
|
|
161
|
+
$formkit: "nuxtUIInput",
|
|
162
|
+
if: ifSection("display"),
|
|
163
|
+
name: "class",
|
|
164
|
+
label: "CSS Class",
|
|
165
|
+
key: "schema_class",
|
|
166
|
+
preserve: true
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
$formkit: "nuxtUIInput",
|
|
170
|
+
if: ifSection("display"),
|
|
171
|
+
name: "if",
|
|
172
|
+
label: "Render Condition (if-expression)",
|
|
173
|
+
help: "e.g. $get(otherField).value === true",
|
|
174
|
+
key: "schema_if",
|
|
175
|
+
preserve: true
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
$formkit: "nuxtUICheckbox",
|
|
179
|
+
if: ifSection("display"),
|
|
180
|
+
name: "disabled",
|
|
181
|
+
label: "Disabled",
|
|
182
|
+
key: "schema_disabled",
|
|
183
|
+
value: false,
|
|
184
|
+
preserve: true
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
$formkit: "nuxtUICheckbox",
|
|
188
|
+
if: ifSection("display"),
|
|
189
|
+
name: "readonly",
|
|
190
|
+
label: "Read Only",
|
|
191
|
+
key: "schema_readonly",
|
|
192
|
+
value: false,
|
|
193
|
+
preserve: true
|
|
194
|
+
},
|
|
195
|
+
// --- Style ------------------------------------------------------------
|
|
196
|
+
{
|
|
197
|
+
$formkit: "nuxtUIInput",
|
|
198
|
+
if: ifSection("style"),
|
|
199
|
+
name: "outerClass",
|
|
200
|
+
label: "Outer Class",
|
|
201
|
+
key: "schema_outerClass",
|
|
202
|
+
preserve: true
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
$formkit: "nuxtUIInput",
|
|
206
|
+
if: ifSection("style"),
|
|
207
|
+
name: "wrapperClass",
|
|
208
|
+
label: "Wrapper Class",
|
|
209
|
+
key: "schema_wrapperClass",
|
|
210
|
+
preserve: true
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
$formkit: "nuxtUIInput",
|
|
214
|
+
if: ifSection("style"),
|
|
215
|
+
name: "innerClass",
|
|
216
|
+
label: "Inner Class",
|
|
217
|
+
key: "schema_innerClass",
|
|
218
|
+
preserve: true
|
|
219
|
+
},
|
|
220
|
+
// --- Validation ---------------------------------------------------------
|
|
221
|
+
{
|
|
222
|
+
$formkit: "nuxtUIInput",
|
|
223
|
+
if: ifSection("validation"),
|
|
224
|
+
name: "validation",
|
|
225
|
+
label: "Validation Rules",
|
|
226
|
+
help: "e.g. required|email",
|
|
227
|
+
key: "schema_validation",
|
|
228
|
+
preserve: true
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
$formkit: "nuxtUISelect",
|
|
232
|
+
if: ifSection("validation"),
|
|
233
|
+
name: "validationVisibility",
|
|
234
|
+
label: "Validation Visibility",
|
|
235
|
+
options: validationVisibilityOptions,
|
|
236
|
+
key: "schema_validationVisibility",
|
|
237
|
+
preserve: true
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
$formkit: "nuxtUIInput",
|
|
241
|
+
if: ifSection("validation"),
|
|
242
|
+
name: "validationLabel",
|
|
243
|
+
label: "Validation Label",
|
|
244
|
+
key: "schema_validationLabel",
|
|
245
|
+
preserve: true
|
|
246
|
+
},
|
|
247
|
+
// --- Options (only for field types with an options/items list) --------
|
|
248
|
+
// `listClass`/`listItemClass` match the flex row layout from the
|
|
249
|
+
// Repeater sample (pages/form/repeater-sample.vue); `draggable` +
|
|
250
|
+
// `displayDragHandle` add drag-and-drop reordering the way the
|
|
251
|
+
// Drag-and-Drop Repeater sample (pages/form/repeater-drag.vue) does -
|
|
252
|
+
// `hideMoveButtons` follows that same sample's convention of hiding the
|
|
253
|
+
// up/down buttons once dragging covers reordering.
|
|
254
|
+
addElement("p", "The selected field type does not use an options list.", { class: "text-muted text-sm" }, `${ifSection("options")} && ${notOptionsCapableIf}`),
|
|
255
|
+
{
|
|
256
|
+
$formkit: "nuxtUIRepeater",
|
|
257
|
+
if: `${ifSection("options")} && ${optionsCapableIf}`,
|
|
258
|
+
name: "options",
|
|
259
|
+
label: "Options",
|
|
260
|
+
help: "Shown to the user as label, submitted as value.",
|
|
261
|
+
listClass: "grid gap-2",
|
|
262
|
+
listItemClass: "flex items-start gap-2",
|
|
263
|
+
insertButtonLabel: "Add Option",
|
|
264
|
+
alwaysDisplayInsertButton: true,
|
|
265
|
+
displayDeleteButton: true,
|
|
266
|
+
draggable: true,
|
|
267
|
+
displayDragHandle: true,
|
|
268
|
+
// Each row's children render with their own label above their input
|
|
269
|
+
// (like every other field), but the drag handle and delete button
|
|
270
|
+
// don't have one - `items-start` alone lines them up with the
|
|
271
|
+
// *labels*, not the inputs below. `mt-6` nudges both down to roughly
|
|
272
|
+
// where the inputs start instead.
|
|
273
|
+
dragHandleClass: "mt-6 cursor-grab active:cursor-grabbing text-muted",
|
|
274
|
+
buttonGroupClass: "mt-6 shrink-0",
|
|
275
|
+
hideMoveButtons: true,
|
|
276
|
+
newItem: { label: "", value: "" },
|
|
277
|
+
key: "schema_options",
|
|
278
|
+
preserve: true,
|
|
279
|
+
children: [
|
|
280
|
+
{
|
|
281
|
+
$formkit: "nuxtUIInput",
|
|
282
|
+
label: "Label",
|
|
283
|
+
name: "label",
|
|
284
|
+
outerClass: "w-48"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
$formkit: "nuxtUIInput",
|
|
288
|
+
label: "Value",
|
|
289
|
+
name: "value",
|
|
290
|
+
outerClass: "w-48"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
},
|
|
294
|
+
// --- Attributes (escape hatch for anything not covered above) ---------
|
|
295
|
+
{
|
|
296
|
+
$formkit: "nuxtUIRepeater",
|
|
297
|
+
if: ifSection("attrs"),
|
|
298
|
+
name: "attrs",
|
|
299
|
+
label: "Extra Properties",
|
|
300
|
+
help: "Any additional schema property this field type supports (e.g. rows, min, max, icon).",
|
|
301
|
+
listClass: "grid gap-2",
|
|
302
|
+
listItemClass: "flex items-start gap-2",
|
|
303
|
+
insertButtonLabel: "Add Property",
|
|
304
|
+
alwaysDisplayInsertButton: true,
|
|
305
|
+
displayDeleteButton: true,
|
|
306
|
+
draggable: true,
|
|
307
|
+
displayDragHandle: true,
|
|
308
|
+
// Each row's children render with their own label above their input
|
|
309
|
+
// (like every other field), but the drag handle and delete button
|
|
310
|
+
// don't have one - `items-start` alone lines them up with the
|
|
311
|
+
// *labels*, not the inputs below. `mt-6` nudges both down to roughly
|
|
312
|
+
// where the inputs start instead.
|
|
313
|
+
dragHandleClass: "mt-6 cursor-grab active:cursor-grabbing text-muted",
|
|
314
|
+
buttonGroupClass: "mt-6 shrink-0",
|
|
315
|
+
hideMoveButtons: true,
|
|
316
|
+
newItem: { attrKey: "", attrValue: "" },
|
|
317
|
+
key: "schema_attrs",
|
|
318
|
+
preserve: true,
|
|
319
|
+
children: [
|
|
320
|
+
{
|
|
321
|
+
$formkit: "nuxtUIInput",
|
|
322
|
+
label: "Property",
|
|
323
|
+
name: "attrKey",
|
|
324
|
+
outerClass: "w-48"
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
$formkit: "nuxtUIInput",
|
|
328
|
+
label: "Value",
|
|
329
|
+
name: "attrValue",
|
|
330
|
+
outerClass: "w-48"
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
return { editorSchema, fieldTypeOptions, typeOptions };
|
|
337
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sfxcode/nuxt-ui-formkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "FormKit integration for Nuxt UI - Seamlessly connect FormKit form handling with Nuxt UI components",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Tom",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"dev": "npm run dev:prepare && nuxi dev playground",
|
|
79
79
|
"dev:build": "nuxi build playground",
|
|
80
80
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
81
|
-
"release": "npm run lint && npm run test && npm run prepack && changelogen --
|
|
81
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --minor --release && npm publish --access=public && git push --follow-tags",
|
|
82
82
|
"lint": "eslint .",
|
|
83
83
|
"test": "vitest run",
|
|
84
84
|
"test:watch": "vitest watch",
|
|
@@ -92,38 +92,38 @@
|
|
|
92
92
|
"@formkit/nuxt": "^2.1.2",
|
|
93
93
|
"@nuxt/ui": "^4.10.0",
|
|
94
94
|
"@standard-schema/spec": "^1.1.0",
|
|
95
|
-
"@vue/compiler-dom": "^3.5.
|
|
95
|
+
"@vue/compiler-dom": "^3.5.41"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@codemirror/lang-json": "^6.0.2",
|
|
99
99
|
"@codemirror/state": "^6.7.1",
|
|
100
100
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
101
|
-
"@codemirror/view": "^6.43.
|
|
101
|
+
"@codemirror/view": "^6.43.8",
|
|
102
102
|
"@iconify-json/heroicons": "^1.2.3",
|
|
103
|
-
"@iconify-json/lucide": "^1.2.
|
|
103
|
+
"@iconify-json/lucide": "^1.2.123",
|
|
104
104
|
"@iconify-json/simple-icons": "^1.2.93",
|
|
105
105
|
"@nuxt/devtools": "4.0.0-alpha.9",
|
|
106
|
-
"@nuxt/eslint-config": "^1.
|
|
107
|
-
"@nuxt/kit": "^4.5.
|
|
106
|
+
"@nuxt/eslint-config": "^1.17.0",
|
|
107
|
+
"@nuxt/kit": "^4.5.2",
|
|
108
108
|
"@nuxt/module-builder": "^1.0.3",
|
|
109
|
-
"@nuxt/schema": "^4.5.
|
|
109
|
+
"@nuxt/schema": "^4.5.2",
|
|
110
110
|
"@nuxt/test-utils": "^4.1.0",
|
|
111
111
|
"@types/node": "latest",
|
|
112
112
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
113
113
|
"@vue/test-utils": "^2.4.11",
|
|
114
114
|
"changelogen": "^0.6.2",
|
|
115
115
|
"codemirror": "^6.0.2",
|
|
116
|
-
"eslint": "^10.8.
|
|
116
|
+
"eslint": "^10.8.1",
|
|
117
117
|
"jsdom": "^30.0.1",
|
|
118
118
|
"mkdist": "^2.4.1",
|
|
119
|
-
"nuxt": "^4.5.
|
|
120
|
-
"sass-embedded": "^1.
|
|
119
|
+
"nuxt": "^4.5.2",
|
|
120
|
+
"sass-embedded": "^1.102.0",
|
|
121
121
|
"typescript": "~6.0.3",
|
|
122
122
|
"unbuild": "^3.6.1",
|
|
123
123
|
"valibot": "^1.4.2",
|
|
124
124
|
"vitepress": "2.0.0-alpha.17",
|
|
125
125
|
"vitest": "^4.1.10",
|
|
126
|
-
"vue": "^3.5.
|
|
126
|
+
"vue": "^3.5.41",
|
|
127
127
|
"vue-i18n": "^11.4.8",
|
|
128
128
|
"vue-tsc": "^3.3.9",
|
|
129
129
|
"zod": "^4.4.3"
|