@iris-ui-kit/vue 0.1.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/LICENSE +21 -0
- package/dist/admin.cjs +1869 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +376 -0
- package/dist/admin.d.ts +376 -0
- package/dist/admin.js +10 -0
- package/dist/admin.js.map +1 -0
- package/dist/async.cjs +76 -0
- package/dist/async.cjs.map +1 -0
- package/dist/async.d.cts +59 -0
- package/dist/async.d.ts +59 -0
- package/dist/async.js +3 -0
- package/dist/async.js.map +1 -0
- package/dist/behaviors.cjs +659 -0
- package/dist/behaviors.cjs.map +1 -0
- package/dist/behaviors.d.cts +441 -0
- package/dist/behaviors.d.ts +441 -0
- package/dist/behaviors.js +3 -0
- package/dist/behaviors.js.map +1 -0
- package/dist/chunk-3FJUCHCC.js +146 -0
- package/dist/chunk-3FJUCHCC.js.map +1 -0
- package/dist/chunk-3R3QDLJC.js +172 -0
- package/dist/chunk-3R3QDLJC.js.map +1 -0
- package/dist/chunk-4FLIW67H.js +137 -0
- package/dist/chunk-4FLIW67H.js.map +1 -0
- package/dist/chunk-4FQ7XF52.js +3 -0
- package/dist/chunk-4FQ7XF52.js.map +1 -0
- package/dist/chunk-72PZMU6R.js +99 -0
- package/dist/chunk-72PZMU6R.js.map +1 -0
- package/dist/chunk-75GSYEJS.js +17 -0
- package/dist/chunk-75GSYEJS.js.map +1 -0
- package/dist/chunk-7IOJADZC.js +38 -0
- package/dist/chunk-7IOJADZC.js.map +1 -0
- package/dist/chunk-CIZGSGXN.js +651 -0
- package/dist/chunk-CIZGSGXN.js.map +1 -0
- package/dist/chunk-F77KIPOH.js +103 -0
- package/dist/chunk-F77KIPOH.js.map +1 -0
- package/dist/chunk-FJBYAIL4.js +1318 -0
- package/dist/chunk-FJBYAIL4.js.map +1 -0
- package/dist/chunk-H7HVVPYR.js +66 -0
- package/dist/chunk-H7HVVPYR.js.map +1 -0
- package/dist/chunk-I6D5VXQK.js +167 -0
- package/dist/chunk-I6D5VXQK.js.map +1 -0
- package/dist/chunk-PPYNXNGZ.js +73 -0
- package/dist/chunk-PPYNXNGZ.js.map +1 -0
- package/dist/chunk-QLUIKEMJ.js +1197 -0
- package/dist/chunk-QLUIKEMJ.js.map +1 -0
- package/dist/chunk-ROJV6RON.js +3 -0
- package/dist/chunk-ROJV6RON.js.map +1 -0
- package/dist/chunk-S43IHHBU.js +82 -0
- package/dist/chunk-S43IHHBU.js.map +1 -0
- package/dist/chunk-TCAXRWT6.js +108 -0
- package/dist/chunk-TCAXRWT6.js.map +1 -0
- package/dist/chunk-TQB5LVOH.js +17 -0
- package/dist/chunk-TQB5LVOH.js.map +1 -0
- package/dist/chunk-VM2CXEUA.js +46 -0
- package/dist/chunk-VM2CXEUA.js.map +1 -0
- package/dist/chunk-W2JVHGFR.js +103 -0
- package/dist/chunk-W2JVHGFR.js.map +1 -0
- package/dist/chunk-WL67XCH5.js +59 -0
- package/dist/chunk-WL67XCH5.js.map +1 -0
- package/dist/chunk-XCNCIG2I.js +165 -0
- package/dist/chunk-XCNCIG2I.js.map +1 -0
- package/dist/chunk-XSLBANUV.js +16 -0
- package/dist/chunk-XSLBANUV.js.map +1 -0
- package/dist/chunk-ZY4NLITM.js +73 -0
- package/dist/chunk-ZY4NLITM.js.map +1 -0
- package/dist/data.cjs +59 -0
- package/dist/data.cjs.map +1 -0
- package/dist/data.d.cts +46 -0
- package/dist/data.d.ts +46 -0
- package/dist/data.js +4 -0
- package/dist/data.js.map +1 -0
- package/dist/error-boundary.cjs +115 -0
- package/dist/error-boundary.cjs.map +1 -0
- package/dist/error-boundary.d.cts +60 -0
- package/dist/error-boundary.d.ts +60 -0
- package/dist/error-boundary.js +4 -0
- package/dist/error-boundary.js.map +1 -0
- package/dist/floating.cjs +149 -0
- package/dist/floating.cjs.map +1 -0
- package/dist/floating.d.cts +106 -0
- package/dist/floating.d.ts +106 -0
- package/dist/floating.js +4 -0
- package/dist/floating.js.map +1 -0
- package/dist/form.cjs +144 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.cts +128 -0
- package/dist/form.d.ts +128 -0
- package/dist/form.js +3 -0
- package/dist/form.js.map +1 -0
- package/dist/i18n.cjs +112 -0
- package/dist/i18n.cjs.map +1 -0
- package/dist/i18n.d.cts +99 -0
- package/dist/i18n.d.ts +99 -0
- package/dist/i18n.js +3 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.cjs +18673 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8087 -0
- package/dist/index.d.ts +8087 -0
- package/dist/index.js +13525 -0
- package/dist/index.js.map +1 -0
- package/dist/layouts.cjs +392 -0
- package/dist/layouts.cjs.map +1 -0
- package/dist/layouts.d.cts +406 -0
- package/dist/layouts.d.ts +406 -0
- package/dist/layouts.js +5 -0
- package/dist/layouts.js.map +1 -0
- package/dist/machine.cjs +19 -0
- package/dist/machine.cjs.map +1 -0
- package/dist/machine.d.cts +18 -0
- package/dist/machine.d.ts +18 -0
- package/dist/machine.js +4 -0
- package/dist/machine.js.map +1 -0
- package/dist/modal-utils.cjs +170 -0
- package/dist/modal-utils.cjs.map +1 -0
- package/dist/modal-utils.d.cts +43 -0
- package/dist/modal-utils.d.ts +43 -0
- package/dist/modal-utils.js +3 -0
- package/dist/modal-utils.js.map +1 -0
- package/dist/motion.cjs +88 -0
- package/dist/motion.cjs.map +1 -0
- package/dist/motion.d.cts +43 -0
- package/dist/motion.d.ts +43 -0
- package/dist/motion.js +3 -0
- package/dist/motion.js.map +1 -0
- package/dist/provider.cjs +272 -0
- package/dist/provider.cjs.map +1 -0
- package/dist/provider.d.cts +130 -0
- package/dist/provider.d.ts +130 -0
- package/dist/provider.js +6 -0
- package/dist/provider.js.map +1 -0
- package/dist/resource.cjs +25 -0
- package/dist/resource.cjs.map +1 -0
- package/dist/resource.d.cts +17 -0
- package/dist/resource.d.ts +17 -0
- package/dist/resource.js +3 -0
- package/dist/resource.js.map +1 -0
- package/dist/skeletons.cjs +1659 -0
- package/dist/skeletons.cjs.map +1 -0
- package/dist/skeletons.d.cts +179 -0
- package/dist/skeletons.d.ts +179 -0
- package/dist/skeletons.js +7 -0
- package/dist/skeletons.js.map +1 -0
- package/dist/skins.cjs +138 -0
- package/dist/skins.cjs.map +1 -0
- package/dist/skins.d.cts +67 -0
- package/dist/skins.d.ts +67 -0
- package/dist/skins.js +3 -0
- package/dist/skins.js.map +1 -0
- package/dist/theme.cjs +106 -0
- package/dist/theme.cjs.map +1 -0
- package/dist/theme.d.cts +103 -0
- package/dist/theme.d.ts +103 -0
- package/dist/theme.js +3 -0
- package/dist/theme.js.map +1 -0
- package/dist/undo.cjs +51 -0
- package/dist/undo.cjs.map +1 -0
- package/dist/undo.d.cts +48 -0
- package/dist/undo.d.ts +48 -0
- package/dist/undo.js +3 -0
- package/dist/undo.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Ref, ComputedRef } from 'vue';
|
|
2
|
+
import { Placement, Strategy, Middleware } from '@floating-ui/dom';
|
|
3
|
+
export { Middleware, Strategy } from '@floating-ui/dom';
|
|
4
|
+
|
|
5
|
+
interface UseFloatingOptions {
|
|
6
|
+
/** Element the floating panel is positioned relative to. */
|
|
7
|
+
anchor: Ref<HTMLElement | null | undefined>;
|
|
8
|
+
/** The floating element itself. */
|
|
9
|
+
floating: Ref<HTMLElement | null | undefined>;
|
|
10
|
+
/** Whether the floating element is visible. Controls `autoUpdate` lifecycle. */
|
|
11
|
+
open: Ref<boolean>;
|
|
12
|
+
/** Default placement. Floating UI may flip it if `flip` is true. */
|
|
13
|
+
placement?: Placement;
|
|
14
|
+
/** CSS position strategy. */
|
|
15
|
+
strategy?: Strategy;
|
|
16
|
+
/** Pixel offset between anchor and floating element. */
|
|
17
|
+
offset?: number;
|
|
18
|
+
/** Flip to the opposite side when the requested side overflows. Default `true`. */
|
|
19
|
+
flip?: boolean;
|
|
20
|
+
/** Shift along the cross-axis to stay in view. Default `true`. */
|
|
21
|
+
shift?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Constrain the floating element to the available viewport space — sets
|
|
24
|
+
* `maxWidth`/`maxHeight` (minus 8px padding) on it so a long dropdown/popover
|
|
25
|
+
* never overflows the screen (pair with `overflow:auto` for scroll). Off by
|
|
26
|
+
* default. Pass a number to override the viewport padding.
|
|
27
|
+
*/
|
|
28
|
+
size?: boolean | number;
|
|
29
|
+
/** Extra Floating UI middleware to append. */
|
|
30
|
+
middleware?: Middleware[];
|
|
31
|
+
/**
|
|
32
|
+
* Optional ref to an arrow element inside the floating panel. When provided,
|
|
33
|
+
* `arrowX`, `arrowY`, and `arrowSide` in the return value are populated.
|
|
34
|
+
*/
|
|
35
|
+
arrow?: Ref<HTMLElement | null | undefined>;
|
|
36
|
+
}
|
|
37
|
+
interface UseFloatingReturn {
|
|
38
|
+
/** Computed x coordinate (px). */
|
|
39
|
+
x: Ref<number>;
|
|
40
|
+
/** Computed y coordinate (px). */
|
|
41
|
+
y: Ref<number>;
|
|
42
|
+
/** Final strategy (echo of input). */
|
|
43
|
+
strategy: Ref<Strategy>;
|
|
44
|
+
/** Final placement, possibly flipped from the input placement. */
|
|
45
|
+
finalPlacement: Ref<Placement>;
|
|
46
|
+
/** Ready-to-bind inline style object for the floating element. */
|
|
47
|
+
floatingStyles: ComputedRef<Record<string, string>>;
|
|
48
|
+
/** Trigger a manual recompute. Usually not needed — `autoUpdate` handles scrolling/resizing. */
|
|
49
|
+
update: () => Promise<void>;
|
|
50
|
+
/** Arrow x offset (px). Present when `arrow` option is set. */
|
|
51
|
+
arrowX: Ref<number | undefined>;
|
|
52
|
+
/** Arrow y offset (px). Present when `arrow` option is set. */
|
|
53
|
+
arrowY: Ref<number | undefined>;
|
|
54
|
+
/**
|
|
55
|
+
* Side of the floating element the arrow is on.
|
|
56
|
+
* E.g. `'bottom'` placement → `'top'` (arrow at top pointing up).
|
|
57
|
+
*/
|
|
58
|
+
arrowSide: ComputedRef<'top' | 'right' | 'bottom' | 'left' | undefined>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Reactive Floating UI integration. Subscribes `autoUpdate` whenever
|
|
62
|
+
* `open === true` and both refs are populated; tears it down on close,
|
|
63
|
+
* unmount, or ref clearing. Returns `floatingStyles` ready to be spread onto
|
|
64
|
+
* the floating element.
|
|
65
|
+
*
|
|
66
|
+
* Pure positioning — does NOT render anything, does NOT manage focus,
|
|
67
|
+
* does NOT handle dismiss behavior. Those concerns live in the consuming
|
|
68
|
+
* primitive (Popover, Tooltip, Dialog, Menu, ...).
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* const triggerEl = ref<HTMLElement | null>(null)
|
|
72
|
+
* const contentEl = ref<HTMLElement | null>(null)
|
|
73
|
+
* const open = ref(false)
|
|
74
|
+
* const { floatingStyles } = useFloating({
|
|
75
|
+
* anchor: triggerEl,
|
|
76
|
+
* floating: contentEl,
|
|
77
|
+
* open,
|
|
78
|
+
* placement: 'bottom-start',
|
|
79
|
+
* offset: 8,
|
|
80
|
+
* })
|
|
81
|
+
*/
|
|
82
|
+
declare function useFloating(options: UseFloatingOptions): UseFloatingReturn;
|
|
83
|
+
|
|
84
|
+
interface UseDismissOptions {
|
|
85
|
+
/** While true, the dismiss listeners are active. */
|
|
86
|
+
enabled: Ref<boolean>;
|
|
87
|
+
/** Elements that, if clicked, should NOT trigger dismiss (typically trigger + content). */
|
|
88
|
+
exclude: Array<Ref<HTMLElement | null | undefined>>;
|
|
89
|
+
/** Called when the user attempts to dismiss the surface. */
|
|
90
|
+
onDismiss: () => void;
|
|
91
|
+
/** Close on Escape key. Default `true`. */
|
|
92
|
+
escape?: boolean;
|
|
93
|
+
/** Close on pointerdown outside the excluded elements. Default `true`. */
|
|
94
|
+
outsidePointerDown?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Listen for the universal "dismiss" gestures: Escape key and outside
|
|
98
|
+
* pointer-down. Used by Popover / Dialog / Menu / Combobox. Tooltip does
|
|
99
|
+
* NOT use this — it dismisses on mouseleave/blur instead.
|
|
100
|
+
*
|
|
101
|
+
* Listeners are attached to `document` only while `enabled` is true, and
|
|
102
|
+
* removed on disable / unmount. SSR-safe (no-op without `document`).
|
|
103
|
+
*/
|
|
104
|
+
declare function useDismiss(options: UseDismissOptions): void;
|
|
105
|
+
|
|
106
|
+
export { type UseDismissOptions, type UseFloatingOptions, type UseFloatingReturn, useDismiss, useFloating };
|
package/dist/floating.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"floating.js"}
|
package/dist/form.cjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vue = require('vue');
|
|
4
|
+
var core = require('@iris-ui-kit/core');
|
|
5
|
+
|
|
6
|
+
// src/form/Form.ts
|
|
7
|
+
var FormInjectionKey = /* @__PURE__ */ Symbol("IrisForm");
|
|
8
|
+
function useFormContext() {
|
|
9
|
+
const form = vue.inject(FormInjectionKey, null);
|
|
10
|
+
if (!form) {
|
|
11
|
+
throw new Error("useField / useFormContext must be used within an <IrisForm>");
|
|
12
|
+
}
|
|
13
|
+
return form;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/form/Form.ts
|
|
17
|
+
function focusFirstError(formEl, errors) {
|
|
18
|
+
if (!formEl) return;
|
|
19
|
+
const keys = Object.keys(errors);
|
|
20
|
+
if (keys.length === 0) return;
|
|
21
|
+
const controls = formEl.querySelectorAll(
|
|
22
|
+
"input[name], select[name], textarea[name], [data-iris-field]"
|
|
23
|
+
);
|
|
24
|
+
for (const el of Array.from(controls)) {
|
|
25
|
+
const name = el.getAttribute("name") ?? el.getAttribute("data-iris-field");
|
|
26
|
+
if (name && keys.includes(name)) {
|
|
27
|
+
el.focus();
|
|
28
|
+
try {
|
|
29
|
+
el.scrollIntoView({ block: "center" });
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
var IrisForm = vue.defineComponent({
|
|
37
|
+
name: "IrisForm",
|
|
38
|
+
props: {
|
|
39
|
+
/** The store from `useForm(...).form`. */
|
|
40
|
+
form: {
|
|
41
|
+
type: Object,
|
|
42
|
+
required: true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
setup(props, { slots }) {
|
|
46
|
+
vue.provide(FormInjectionKey, props.form);
|
|
47
|
+
const formRef = vue.ref(null);
|
|
48
|
+
return () => vue.h(
|
|
49
|
+
"form",
|
|
50
|
+
{
|
|
51
|
+
ref: formRef,
|
|
52
|
+
"data-iris-form": "",
|
|
53
|
+
onSubmit: (event) => {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
void props.form.handleSubmit().then(() => focusFirstError(formRef.value, props.form.getState().errors));
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
slots.default?.()
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
function useForm(config) {
|
|
63
|
+
const form = core.createFormStore(config);
|
|
64
|
+
const state = vue.ref(form.getState());
|
|
65
|
+
const unsubscribe = form.subscribe((next) => {
|
|
66
|
+
state.value = next;
|
|
67
|
+
});
|
|
68
|
+
vue.onBeforeUnmount(unsubscribe);
|
|
69
|
+
return {
|
|
70
|
+
form,
|
|
71
|
+
state,
|
|
72
|
+
values: vue.computed(() => state.value.values),
|
|
73
|
+
errors: vue.computed(() => state.value.errors),
|
|
74
|
+
touched: vue.computed(() => state.value.touched),
|
|
75
|
+
dirty: vue.computed(() => state.value.dirty),
|
|
76
|
+
isSubmitting: vue.computed(() => state.value.isSubmitting),
|
|
77
|
+
isValidating: vue.computed(() => state.value.isValidating),
|
|
78
|
+
submitCount: vue.computed(() => state.value.submitCount),
|
|
79
|
+
isValid: vue.computed(() => Object.keys(state.value.errors).length === 0),
|
|
80
|
+
setFieldValue: form.setFieldValue,
|
|
81
|
+
setValues: form.setValues,
|
|
82
|
+
setFieldTouched: form.setFieldTouched,
|
|
83
|
+
setFieldError: form.setFieldError,
|
|
84
|
+
setErrors: form.setErrors,
|
|
85
|
+
validateField: form.validateField,
|
|
86
|
+
validateForm: form.validateForm,
|
|
87
|
+
handleSubmit: form.handleSubmit,
|
|
88
|
+
reset: form.reset
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function useField(name) {
|
|
92
|
+
const form = useFormContext();
|
|
93
|
+
const state = vue.ref(form.getState());
|
|
94
|
+
const unsubscribe = form.subscribe((next) => {
|
|
95
|
+
state.value = next;
|
|
96
|
+
});
|
|
97
|
+
vue.onBeforeUnmount(unsubscribe);
|
|
98
|
+
const key = core.formatPath(name);
|
|
99
|
+
const value = vue.computed(() => core.getByPath(state.value.values, name));
|
|
100
|
+
const error = vue.computed(() => state.value.errors[key]);
|
|
101
|
+
return {
|
|
102
|
+
name,
|
|
103
|
+
value,
|
|
104
|
+
error,
|
|
105
|
+
touched: vue.computed(() => Boolean(state.value.touched[key])),
|
|
106
|
+
dirty: vue.computed(() => Boolean(state.value.dirty[key])),
|
|
107
|
+
setValue: (next) => form.setFieldValue(name, next),
|
|
108
|
+
setTouched: (touched = true) => form.setFieldTouched(name, touched),
|
|
109
|
+
fieldProps: vue.computed(() => ({
|
|
110
|
+
modelValue: value.value,
|
|
111
|
+
"onUpdate:modelValue": (next) => form.setFieldValue(name, next),
|
|
112
|
+
invalid: Boolean(error.value),
|
|
113
|
+
onBlur: () => form.setFieldTouched(name, true)
|
|
114
|
+
}))
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function useFieldArray(name) {
|
|
118
|
+
const form = useFormContext();
|
|
119
|
+
const state = vue.ref(form.getState());
|
|
120
|
+
const unsubscribe = form.subscribe((next) => {
|
|
121
|
+
state.value = next;
|
|
122
|
+
});
|
|
123
|
+
vue.onBeforeUnmount(unsubscribe);
|
|
124
|
+
return {
|
|
125
|
+
name,
|
|
126
|
+
fields: vue.computed(
|
|
127
|
+
() => Array.isArray(state.value.values[name]) ? state.value.values[name] : []
|
|
128
|
+
),
|
|
129
|
+
push: (item) => form.arrayPush(name, item),
|
|
130
|
+
remove: (index) => form.arrayRemove(name, index),
|
|
131
|
+
insert: (index, item) => form.arrayInsert(name, index, item),
|
|
132
|
+
move: (from, to) => form.arrayMove(name, from, to),
|
|
133
|
+
replace: (items) => form.setFieldValue(name, items)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
exports.FormInjectionKey = FormInjectionKey;
|
|
138
|
+
exports.IrisForm = IrisForm;
|
|
139
|
+
exports.useField = useField;
|
|
140
|
+
exports.useFieldArray = useFieldArray;
|
|
141
|
+
exports.useForm = useForm;
|
|
142
|
+
exports.useFormContext = useFormContext;
|
|
143
|
+
//# sourceMappingURL=form.cjs.map
|
|
144
|
+
//# sourceMappingURL=form.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/form/context.ts","../src/form/Form.ts","../src/form/useForm.ts","../src/form/useField.ts","../src/form/useFieldArray.ts"],"names":["inject","defineComponent","provide","ref","h","createFormStore","onBeforeUnmount","computed","formatPath","getByPath"],"mappings":"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","file":"form.cjs","sourcesContent":["import { inject, type InjectionKey } from 'vue'\nimport type { FormStore, FormValues } from '@iris-ui-kit/core'\n\n/**\n * Provides the active form's {@link FormStore} to descendant fields so\n * `useField` can bind without prop drilling. The generic is erased at the\n * injection boundary; `useField<T>` re-applies the value type at the call site.\n */\nexport const FormInjectionKey: InjectionKey<FormStore<FormValues>> = Symbol('IrisForm')\n\nexport function useFormContext(): FormStore<FormValues> {\n const form = inject(FormInjectionKey, null)\n if (!form) {\n throw new Error('useField / useFormContext must be used within an <IrisForm>')\n }\n return form\n}\n","import { defineComponent, h, provide, ref, type PropType } from 'vue'\nimport type { FormStore, FormValues } from '@iris-ui-kit/core'\nimport { FormInjectionKey } from './context'\n\n/** Focus (and best-effort scroll to) the first errored named control in DOM order. */\nfunction focusFirstError(\n formEl: HTMLElement | null,\n errors: Record<string, string | undefined>,\n): void {\n if (!formEl) return\n const keys = Object.keys(errors)\n if (keys.length === 0) return\n const controls = formEl.querySelectorAll<HTMLElement>(\n 'input[name], select[name], textarea[name], [data-iris-field]',\n )\n for (const el of Array.from(controls)) {\n const name = el.getAttribute('name') ?? el.getAttribute('data-iris-field')\n if (name && keys.includes(name)) {\n el.focus()\n try {\n el.scrollIntoView({ block: 'center' })\n } catch {\n /* scrollIntoView is unavailable in jsdom — focus is the contract */\n }\n return\n }\n }\n}\n\n/**\n * Provides the form store to descendant `useField` calls and wires the native\n * `<form>` submit to `handleSubmit` (with `preventDefault`). Composition:\n *\n * ```html\n * <IrisForm :form=\"f.form\">\n * <IrisFormField label=\"Email\"><IrisInput v-bind=\"email.fieldProps.value\" /></IrisFormField>\n * <IrisButton type=\"submit\" :disabled=\"f.isSubmitting.value\">Save</IrisButton>\n * </IrisForm>\n * ```\n */\nexport const IrisForm = defineComponent({\n name: 'IrisForm',\n props: {\n /** The store from `useForm(...).form`. */\n form: {\n type: Object as PropType<FormStore<FormValues>>,\n required: true,\n },\n },\n setup(props, { slots }) {\n provide(FormInjectionKey, props.form)\n const formRef = ref<HTMLElement | null>(null)\n return () =>\n h(\n 'form',\n {\n ref: formRef,\n 'data-iris-form': '',\n onSubmit: (event: Event) => {\n event.preventDefault()\n // On a failed submit, move focus to the first errored field (a11y).\n void props.form\n .handleSubmit()\n .then(() => focusFirstError(formRef.value, props.form.getState().errors))\n },\n },\n slots.default?.(),\n )\n },\n})\n","import { computed, onBeforeUnmount, ref, type ComputedRef, type Ref } from 'vue'\nimport {\n createFormStore,\n type FieldErrors,\n type FieldFlags,\n type FormConfig,\n type FormState,\n type FormStore,\n type FormValues,\n} from '@iris-ui-kit/core'\n\nexport interface UseFormReturn<V extends FormValues> {\n /** The form store — pass to `<IrisForm :form=\"...\">`. */\n form: FormStore<V>\n /** The full reactive state snapshot. */\n state: Ref<FormState<V>>\n values: ComputedRef<V>\n errors: ComputedRef<FieldErrors<V>>\n touched: ComputedRef<FieldFlags<V>>\n dirty: ComputedRef<FieldFlags<V>>\n isSubmitting: ComputedRef<boolean>\n isValidating: ComputedRef<boolean>\n submitCount: ComputedRef<number>\n isValid: ComputedRef<boolean>\n setFieldValue: FormStore<V>['setFieldValue']\n setValues: FormStore<V>['setValues']\n setFieldTouched: FormStore<V>['setFieldTouched']\n setFieldError: FormStore<V>['setFieldError']\n setErrors: FormStore<V>['setErrors']\n validateField: FormStore<V>['validateField']\n validateForm: FormStore<V>['validateForm']\n handleSubmit: FormStore<V>['handleSubmit']\n reset: FormStore<V>['reset']\n}\n\n/**\n * Vue binding for the framework-agnostic form engine. Creates the store in\n * `setup()` (runs once) and bridges it into Vue reactivity via a `ref` updated\n * on each store change, cleaned up on unmount. Because `setup` runs once,\n * `onSubmit` / `validate` closures stay live against reactive state — no\n * stale-callback handling is needed (unlike the React adapter).\n */\nexport function useForm<V extends FormValues>(config: FormConfig<V>): UseFormReturn<V> {\n const form = createFormStore(config)\n const state = ref(form.getState()) as Ref<FormState<V>>\n const unsubscribe = form.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(unsubscribe)\n\n return {\n form,\n state,\n values: computed(() => state.value.values),\n errors: computed(() => state.value.errors),\n touched: computed(() => state.value.touched),\n dirty: computed(() => state.value.dirty),\n isSubmitting: computed(() => state.value.isSubmitting),\n isValidating: computed(() => state.value.isValidating),\n submitCount: computed(() => state.value.submitCount),\n isValid: computed(() => Object.keys(state.value.errors).length === 0),\n setFieldValue: form.setFieldValue,\n setValues: form.setValues,\n setFieldTouched: form.setFieldTouched,\n setFieldError: form.setFieldError,\n setErrors: form.setErrors,\n validateField: form.validateField,\n validateForm: form.validateForm,\n handleSubmit: form.handleSubmit,\n reset: form.reset,\n }\n}\n","import { computed, onBeforeUnmount, ref, type ComputedRef, type Ref } from 'vue'\nimport { formatPath, getByPath, type FormState, type FormValues } from '@iris-ui-kit/core'\nimport { useFormContext } from './context'\n\nexport interface FieldBindProps {\n modelValue: unknown\n 'onUpdate:modelValue': (value: unknown) => void\n invalid: boolean\n onBlur: () => void\n}\n\nexport interface UseFieldReturn<T> {\n name: string\n value: ComputedRef<T>\n error: ComputedRef<string | undefined>\n touched: ComputedRef<boolean>\n dirty: ComputedRef<boolean>\n setValue: (value: T) => void\n setTouched: (touched?: boolean) => void\n /**\n * Bundle for `v-bind` onto a v-model'd Iris control (`IrisInput`,\n * `IrisSelect`, …): wires `modelValue` / `update:modelValue` / `invalid` /\n * `blur`. Use `value` + `setValue` directly for bespoke bindings.\n */\n fieldProps: ComputedRef<FieldBindProps>\n}\n\n/**\n * Binds a single field to the surrounding `<IrisForm>`. `T` is the value type\n * at the call site (the injection erases the form's generic).\n *\n * `name` may be a flat top-level key OR a nested PATH (`'address.city'`,\n * `'items[2].sku'`); a flat key is a 1-segment path and stays back-compatible\n * (v3 R19).\n */\nexport function useField<T = unknown>(name: string): UseFieldReturn<T> {\n const form = useFormContext()\n const state = ref(form.getState()) as Ref<FormState<FormValues>>\n const unsubscribe = form.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(unsubscribe)\n\n // Canonical key for per-field state lookups (a flat key maps to itself).\n const key = formatPath(name)\n const value = computed(() => getByPath(state.value.values, name) as T)\n const error = computed(() => state.value.errors[key])\n\n return {\n name,\n value,\n error,\n touched: computed(() => Boolean(state.value.touched[key])),\n dirty: computed(() => Boolean(state.value.dirty[key])),\n setValue: (next) => form.setFieldValue(name, next as never),\n setTouched: (touched = true) => form.setFieldTouched(name, touched),\n fieldProps: computed(() => ({\n modelValue: value.value,\n 'onUpdate:modelValue': (next: unknown) => form.setFieldValue(name, next as never),\n invalid: Boolean(error.value),\n onBlur: () => form.setFieldTouched(name, true),\n })),\n }\n}\n","import { computed, onBeforeUnmount, ref, type ComputedRef, type Ref } from 'vue'\nimport type { FormState, FormValues } from '@iris-ui-kit/core'\nimport { useFormContext } from './context'\n\nexport interface UseFieldArrayReturn<T> {\n name: string\n /** Current array value (reactive). */\n fields: ComputedRef<T[]>\n /** Append an item. */\n push: (item: T) => void\n /** Remove the item at `index`. */\n remove: (index: number) => void\n /** Insert `item` at `index` (shifting the rest right). */\n insert: (index: number, item: T) => void\n /** Move the item from `from` to `to`. */\n move: (from: number, to: number) => void\n /** Replace the whole array. */\n replace: (items: T[]) => void\n}\n\n/**\n * Manage a dynamic array field (repeatable rows) inside an `<IrisForm>`. Mutators\n * delegate to the core {@link FormStore} array helpers (`arrayPush`/`arrayInsert`/\n * `arrayRemove`/`arrayMove`), which RE-KEY each element's error/touched/dirty/\n * validating state across the mutation — so a row's per-element validation state\n * FOLLOWS the row when rows are removed or reordered (removing `items[0]` shifts\n * `items[2]`'s error down to `items[1]`). `replace` swaps the whole array.\n */\nexport function useFieldArray<T = unknown>(name: string): UseFieldArrayReturn<T> {\n const form = useFormContext()\n const state = ref(form.getState()) as Ref<FormState<FormValues>>\n const unsubscribe = form.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(unsubscribe)\n\n return {\n name,\n fields: computed(\n () => (Array.isArray(state.value.values[name]) ? state.value.values[name] : []) as T[],\n ),\n push: (item) => form.arrayPush(name as never, item as never),\n remove: (index) => form.arrayRemove(name as never, index),\n insert: (index, item) => form.arrayInsert(name as never, index, item as never),\n move: (from, to) => form.arrayMove(name as never, from, to),\n replace: (items) => form.setFieldValue(name, items as never),\n }\n}\n"]}
|
package/dist/form.d.cts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as vue from 'vue';
|
|
2
|
+
import { PropType, Ref, ComputedRef, InjectionKey } from 'vue';
|
|
3
|
+
import { FormStore, FormValues, FormState, FieldErrors, FieldFlags, FormConfig } from '@iris-ui-kit/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Provides the form store to descendant `useField` calls and wires the native
|
|
7
|
+
* `<form>` submit to `handleSubmit` (with `preventDefault`). Composition:
|
|
8
|
+
*
|
|
9
|
+
* ```html
|
|
10
|
+
* <IrisForm :form="f.form">
|
|
11
|
+
* <IrisFormField label="Email"><IrisInput v-bind="email.fieldProps.value" /></IrisFormField>
|
|
12
|
+
* <IrisButton type="submit" :disabled="f.isSubmitting.value">Save</IrisButton>
|
|
13
|
+
* </IrisForm>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
declare const IrisForm: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
17
|
+
/** The store from `useForm(...).form`. */
|
|
18
|
+
form: {
|
|
19
|
+
type: PropType<FormStore<FormValues>>;
|
|
20
|
+
required: true;
|
|
21
|
+
};
|
|
22
|
+
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
25
|
+
/** The store from `useForm(...).form`. */
|
|
26
|
+
form: {
|
|
27
|
+
type: PropType<FormStore<FormValues>>;
|
|
28
|
+
required: true;
|
|
29
|
+
};
|
|
30
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
31
|
+
|
|
32
|
+
interface UseFormReturn<V extends FormValues> {
|
|
33
|
+
/** The form store — pass to `<IrisForm :form="...">`. */
|
|
34
|
+
form: FormStore<V>;
|
|
35
|
+
/** The full reactive state snapshot. */
|
|
36
|
+
state: Ref<FormState<V>>;
|
|
37
|
+
values: ComputedRef<V>;
|
|
38
|
+
errors: ComputedRef<FieldErrors<V>>;
|
|
39
|
+
touched: ComputedRef<FieldFlags<V>>;
|
|
40
|
+
dirty: ComputedRef<FieldFlags<V>>;
|
|
41
|
+
isSubmitting: ComputedRef<boolean>;
|
|
42
|
+
isValidating: ComputedRef<boolean>;
|
|
43
|
+
submitCount: ComputedRef<number>;
|
|
44
|
+
isValid: ComputedRef<boolean>;
|
|
45
|
+
setFieldValue: FormStore<V>['setFieldValue'];
|
|
46
|
+
setValues: FormStore<V>['setValues'];
|
|
47
|
+
setFieldTouched: FormStore<V>['setFieldTouched'];
|
|
48
|
+
setFieldError: FormStore<V>['setFieldError'];
|
|
49
|
+
setErrors: FormStore<V>['setErrors'];
|
|
50
|
+
validateField: FormStore<V>['validateField'];
|
|
51
|
+
validateForm: FormStore<V>['validateForm'];
|
|
52
|
+
handleSubmit: FormStore<V>['handleSubmit'];
|
|
53
|
+
reset: FormStore<V>['reset'];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vue binding for the framework-agnostic form engine. Creates the store in
|
|
57
|
+
* `setup()` (runs once) and bridges it into Vue reactivity via a `ref` updated
|
|
58
|
+
* on each store change, cleaned up on unmount. Because `setup` runs once,
|
|
59
|
+
* `onSubmit` / `validate` closures stay live against reactive state — no
|
|
60
|
+
* stale-callback handling is needed (unlike the React adapter).
|
|
61
|
+
*/
|
|
62
|
+
declare function useForm<V extends FormValues>(config: FormConfig<V>): UseFormReturn<V>;
|
|
63
|
+
|
|
64
|
+
interface FieldBindProps {
|
|
65
|
+
modelValue: unknown;
|
|
66
|
+
'onUpdate:modelValue': (value: unknown) => void;
|
|
67
|
+
invalid: boolean;
|
|
68
|
+
onBlur: () => void;
|
|
69
|
+
}
|
|
70
|
+
interface UseFieldReturn<T> {
|
|
71
|
+
name: string;
|
|
72
|
+
value: ComputedRef<T>;
|
|
73
|
+
error: ComputedRef<string | undefined>;
|
|
74
|
+
touched: ComputedRef<boolean>;
|
|
75
|
+
dirty: ComputedRef<boolean>;
|
|
76
|
+
setValue: (value: T) => void;
|
|
77
|
+
setTouched: (touched?: boolean) => void;
|
|
78
|
+
/**
|
|
79
|
+
* Bundle for `v-bind` onto a v-model'd Iris control (`IrisInput`,
|
|
80
|
+
* `IrisSelect`, …): wires `modelValue` / `update:modelValue` / `invalid` /
|
|
81
|
+
* `blur`. Use `value` + `setValue` directly for bespoke bindings.
|
|
82
|
+
*/
|
|
83
|
+
fieldProps: ComputedRef<FieldBindProps>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Binds a single field to the surrounding `<IrisForm>`. `T` is the value type
|
|
87
|
+
* at the call site (the injection erases the form's generic).
|
|
88
|
+
*
|
|
89
|
+
* `name` may be a flat top-level key OR a nested PATH (`'address.city'`,
|
|
90
|
+
* `'items[2].sku'`); a flat key is a 1-segment path and stays back-compatible
|
|
91
|
+
* (v3 R19).
|
|
92
|
+
*/
|
|
93
|
+
declare function useField<T = unknown>(name: string): UseFieldReturn<T>;
|
|
94
|
+
|
|
95
|
+
interface UseFieldArrayReturn<T> {
|
|
96
|
+
name: string;
|
|
97
|
+
/** Current array value (reactive). */
|
|
98
|
+
fields: ComputedRef<T[]>;
|
|
99
|
+
/** Append an item. */
|
|
100
|
+
push: (item: T) => void;
|
|
101
|
+
/** Remove the item at `index`. */
|
|
102
|
+
remove: (index: number) => void;
|
|
103
|
+
/** Insert `item` at `index` (shifting the rest right). */
|
|
104
|
+
insert: (index: number, item: T) => void;
|
|
105
|
+
/** Move the item from `from` to `to`. */
|
|
106
|
+
move: (from: number, to: number) => void;
|
|
107
|
+
/** Replace the whole array. */
|
|
108
|
+
replace: (items: T[]) => void;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Manage a dynamic array field (repeatable rows) inside an `<IrisForm>`. Mutators
|
|
112
|
+
* delegate to the core {@link FormStore} array helpers (`arrayPush`/`arrayInsert`/
|
|
113
|
+
* `arrayRemove`/`arrayMove`), which RE-KEY each element's error/touched/dirty/
|
|
114
|
+
* validating state across the mutation — so a row's per-element validation state
|
|
115
|
+
* FOLLOWS the row when rows are removed or reordered (removing `items[0]` shifts
|
|
116
|
+
* `items[2]`'s error down to `items[1]`). `replace` swaps the whole array.
|
|
117
|
+
*/
|
|
118
|
+
declare function useFieldArray<T = unknown>(name: string): UseFieldArrayReturn<T>;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Provides the active form's {@link FormStore} to descendant fields so
|
|
122
|
+
* `useField` can bind without prop drilling. The generic is erased at the
|
|
123
|
+
* injection boundary; `useField<T>` re-applies the value type at the call site.
|
|
124
|
+
*/
|
|
125
|
+
declare const FormInjectionKey: InjectionKey<FormStore<FormValues>>;
|
|
126
|
+
declare function useFormContext(): FormStore<FormValues>;
|
|
127
|
+
|
|
128
|
+
export { type FieldBindProps, FormInjectionKey, IrisForm, type UseFieldArrayReturn, type UseFieldReturn, type UseFormReturn, useField, useFieldArray, useForm, useFormContext };
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as vue from 'vue';
|
|
2
|
+
import { PropType, Ref, ComputedRef, InjectionKey } from 'vue';
|
|
3
|
+
import { FormStore, FormValues, FormState, FieldErrors, FieldFlags, FormConfig } from '@iris-ui-kit/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Provides the form store to descendant `useField` calls and wires the native
|
|
7
|
+
* `<form>` submit to `handleSubmit` (with `preventDefault`). Composition:
|
|
8
|
+
*
|
|
9
|
+
* ```html
|
|
10
|
+
* <IrisForm :form="f.form">
|
|
11
|
+
* <IrisFormField label="Email"><IrisInput v-bind="email.fieldProps.value" /></IrisFormField>
|
|
12
|
+
* <IrisButton type="submit" :disabled="f.isSubmitting.value">Save</IrisButton>
|
|
13
|
+
* </IrisForm>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
declare const IrisForm: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
17
|
+
/** The store from `useForm(...).form`. */
|
|
18
|
+
form: {
|
|
19
|
+
type: PropType<FormStore<FormValues>>;
|
|
20
|
+
required: true;
|
|
21
|
+
};
|
|
22
|
+
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
25
|
+
/** The store from `useForm(...).form`. */
|
|
26
|
+
form: {
|
|
27
|
+
type: PropType<FormStore<FormValues>>;
|
|
28
|
+
required: true;
|
|
29
|
+
};
|
|
30
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
31
|
+
|
|
32
|
+
interface UseFormReturn<V extends FormValues> {
|
|
33
|
+
/** The form store — pass to `<IrisForm :form="...">`. */
|
|
34
|
+
form: FormStore<V>;
|
|
35
|
+
/** The full reactive state snapshot. */
|
|
36
|
+
state: Ref<FormState<V>>;
|
|
37
|
+
values: ComputedRef<V>;
|
|
38
|
+
errors: ComputedRef<FieldErrors<V>>;
|
|
39
|
+
touched: ComputedRef<FieldFlags<V>>;
|
|
40
|
+
dirty: ComputedRef<FieldFlags<V>>;
|
|
41
|
+
isSubmitting: ComputedRef<boolean>;
|
|
42
|
+
isValidating: ComputedRef<boolean>;
|
|
43
|
+
submitCount: ComputedRef<number>;
|
|
44
|
+
isValid: ComputedRef<boolean>;
|
|
45
|
+
setFieldValue: FormStore<V>['setFieldValue'];
|
|
46
|
+
setValues: FormStore<V>['setValues'];
|
|
47
|
+
setFieldTouched: FormStore<V>['setFieldTouched'];
|
|
48
|
+
setFieldError: FormStore<V>['setFieldError'];
|
|
49
|
+
setErrors: FormStore<V>['setErrors'];
|
|
50
|
+
validateField: FormStore<V>['validateField'];
|
|
51
|
+
validateForm: FormStore<V>['validateForm'];
|
|
52
|
+
handleSubmit: FormStore<V>['handleSubmit'];
|
|
53
|
+
reset: FormStore<V>['reset'];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vue binding for the framework-agnostic form engine. Creates the store in
|
|
57
|
+
* `setup()` (runs once) and bridges it into Vue reactivity via a `ref` updated
|
|
58
|
+
* on each store change, cleaned up on unmount. Because `setup` runs once,
|
|
59
|
+
* `onSubmit` / `validate` closures stay live against reactive state — no
|
|
60
|
+
* stale-callback handling is needed (unlike the React adapter).
|
|
61
|
+
*/
|
|
62
|
+
declare function useForm<V extends FormValues>(config: FormConfig<V>): UseFormReturn<V>;
|
|
63
|
+
|
|
64
|
+
interface FieldBindProps {
|
|
65
|
+
modelValue: unknown;
|
|
66
|
+
'onUpdate:modelValue': (value: unknown) => void;
|
|
67
|
+
invalid: boolean;
|
|
68
|
+
onBlur: () => void;
|
|
69
|
+
}
|
|
70
|
+
interface UseFieldReturn<T> {
|
|
71
|
+
name: string;
|
|
72
|
+
value: ComputedRef<T>;
|
|
73
|
+
error: ComputedRef<string | undefined>;
|
|
74
|
+
touched: ComputedRef<boolean>;
|
|
75
|
+
dirty: ComputedRef<boolean>;
|
|
76
|
+
setValue: (value: T) => void;
|
|
77
|
+
setTouched: (touched?: boolean) => void;
|
|
78
|
+
/**
|
|
79
|
+
* Bundle for `v-bind` onto a v-model'd Iris control (`IrisInput`,
|
|
80
|
+
* `IrisSelect`, …): wires `modelValue` / `update:modelValue` / `invalid` /
|
|
81
|
+
* `blur`. Use `value` + `setValue` directly for bespoke bindings.
|
|
82
|
+
*/
|
|
83
|
+
fieldProps: ComputedRef<FieldBindProps>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Binds a single field to the surrounding `<IrisForm>`. `T` is the value type
|
|
87
|
+
* at the call site (the injection erases the form's generic).
|
|
88
|
+
*
|
|
89
|
+
* `name` may be a flat top-level key OR a nested PATH (`'address.city'`,
|
|
90
|
+
* `'items[2].sku'`); a flat key is a 1-segment path and stays back-compatible
|
|
91
|
+
* (v3 R19).
|
|
92
|
+
*/
|
|
93
|
+
declare function useField<T = unknown>(name: string): UseFieldReturn<T>;
|
|
94
|
+
|
|
95
|
+
interface UseFieldArrayReturn<T> {
|
|
96
|
+
name: string;
|
|
97
|
+
/** Current array value (reactive). */
|
|
98
|
+
fields: ComputedRef<T[]>;
|
|
99
|
+
/** Append an item. */
|
|
100
|
+
push: (item: T) => void;
|
|
101
|
+
/** Remove the item at `index`. */
|
|
102
|
+
remove: (index: number) => void;
|
|
103
|
+
/** Insert `item` at `index` (shifting the rest right). */
|
|
104
|
+
insert: (index: number, item: T) => void;
|
|
105
|
+
/** Move the item from `from` to `to`. */
|
|
106
|
+
move: (from: number, to: number) => void;
|
|
107
|
+
/** Replace the whole array. */
|
|
108
|
+
replace: (items: T[]) => void;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Manage a dynamic array field (repeatable rows) inside an `<IrisForm>`. Mutators
|
|
112
|
+
* delegate to the core {@link FormStore} array helpers (`arrayPush`/`arrayInsert`/
|
|
113
|
+
* `arrayRemove`/`arrayMove`), which RE-KEY each element's error/touched/dirty/
|
|
114
|
+
* validating state across the mutation — so a row's per-element validation state
|
|
115
|
+
* FOLLOWS the row when rows are removed or reordered (removing `items[0]` shifts
|
|
116
|
+
* `items[2]`'s error down to `items[1]`). `replace` swaps the whole array.
|
|
117
|
+
*/
|
|
118
|
+
declare function useFieldArray<T = unknown>(name: string): UseFieldArrayReturn<T>;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Provides the active form's {@link FormStore} to descendant fields so
|
|
122
|
+
* `useField` can bind without prop drilling. The generic is erased at the
|
|
123
|
+
* injection boundary; `useField<T>` re-applies the value type at the call site.
|
|
124
|
+
*/
|
|
125
|
+
declare const FormInjectionKey: InjectionKey<FormStore<FormValues>>;
|
|
126
|
+
declare function useFormContext(): FormStore<FormValues>;
|
|
127
|
+
|
|
128
|
+
export { type FieldBindProps, FormInjectionKey, IrisForm, type UseFieldArrayReturn, type UseFieldReturn, type UseFormReturn, useField, useFieldArray, useForm, useFormContext };
|
package/dist/form.js
ADDED
package/dist/form.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"form.js"}
|
package/dist/i18n.cjs
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vue = require('vue');
|
|
4
|
+
var core = require('@iris-ui-kit/core');
|
|
5
|
+
var theme = require('@iris-ui-kit/theme');
|
|
6
|
+
|
|
7
|
+
// src/i18n/I18nProvider.ts
|
|
8
|
+
|
|
9
|
+
// src/i18n/context.ts
|
|
10
|
+
var I18nInjectionKey = /* @__PURE__ */ Symbol("IrisI18n");
|
|
11
|
+
|
|
12
|
+
// src/i18n/I18nProvider.ts
|
|
13
|
+
var IrisI18nProvider = vue.defineComponent({
|
|
14
|
+
name: "IrisI18nProvider",
|
|
15
|
+
props: {
|
|
16
|
+
locale: { type: String, default: void 0 },
|
|
17
|
+
messages: { type: Object, default: void 0 },
|
|
18
|
+
i18n: { type: Object, default: void 0 },
|
|
19
|
+
/**
|
|
20
|
+
* When set, the locale's writing direction ({@link localeDirection}) is
|
|
21
|
+
* applied to `directionTarget` (`dir` + `data-iris-dir`) and `<html lang>`
|
|
22
|
+
* is set whenever `locale` changes — so "set locale → flip direction +
|
|
23
|
+
* announce language" needs no manual wiring. Off by default so it never
|
|
24
|
+
* fights an explicit `<ThemeProvider dir>`. Reverts on unmount.
|
|
25
|
+
*/
|
|
26
|
+
autoDirection: { type: Boolean, default: false },
|
|
27
|
+
/** Element to receive dir/lang when `autoDirection` is set. Defaults to `document.documentElement`. */
|
|
28
|
+
directionTarget: { type: Object, default: void 0 }
|
|
29
|
+
},
|
|
30
|
+
setup(props, { slots }) {
|
|
31
|
+
const instance = props.i18n ?? core.createI18n({ locale: props.locale, messages: props.messages });
|
|
32
|
+
vue.provide(I18nInjectionKey, instance);
|
|
33
|
+
if (!props.i18n) {
|
|
34
|
+
vue.watch(
|
|
35
|
+
() => props.locale,
|
|
36
|
+
(locale) => {
|
|
37
|
+
if (locale) instance.setLocale(locale);
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
vue.watch(
|
|
41
|
+
() => props.messages,
|
|
42
|
+
(messages) => {
|
|
43
|
+
if (messages) instance.setMessages(messages);
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
let revertDirection = null;
|
|
48
|
+
vue.watch(
|
|
49
|
+
() => [props.autoDirection, props.locale, props.directionTarget],
|
|
50
|
+
([autoDirection, locale]) => {
|
|
51
|
+
revertDirection?.();
|
|
52
|
+
revertDirection = null;
|
|
53
|
+
if (!autoDirection || !locale || typeof document === "undefined") return;
|
|
54
|
+
const el = props.directionTarget ?? document.documentElement;
|
|
55
|
+
const applied = theme.applyDirection(core.localeDirection(locale), el);
|
|
56
|
+
const prevLang = el.getAttribute("lang");
|
|
57
|
+
el.setAttribute("lang", locale);
|
|
58
|
+
revertDirection = () => {
|
|
59
|
+
applied.revert();
|
|
60
|
+
if (prevLang === null) el.removeAttribute("lang");
|
|
61
|
+
else el.setAttribute("lang", prevLang);
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
{ immediate: true }
|
|
65
|
+
);
|
|
66
|
+
vue.onScopeDispose(() => {
|
|
67
|
+
revertDirection?.();
|
|
68
|
+
revertDirection = null;
|
|
69
|
+
});
|
|
70
|
+
return () => slots.default?.();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
var fallbackI18n = null;
|
|
74
|
+
function getFallback() {
|
|
75
|
+
if (fallbackI18n === null) fallbackI18n = core.createI18n();
|
|
76
|
+
return fallbackI18n;
|
|
77
|
+
}
|
|
78
|
+
function useI18n() {
|
|
79
|
+
const i18n = vue.inject(I18nInjectionKey, null) ?? getFallback();
|
|
80
|
+
const state = vue.ref(i18n.getState());
|
|
81
|
+
const unsubscribe = i18n.subscribe((next) => {
|
|
82
|
+
state.value = next;
|
|
83
|
+
});
|
|
84
|
+
vue.onBeforeUnmount(unsubscribe);
|
|
85
|
+
return {
|
|
86
|
+
locale: vue.computed(() => state.value.locale),
|
|
87
|
+
t: (key, params) => {
|
|
88
|
+
void state.value.locale;
|
|
89
|
+
return i18n.t(key, params);
|
|
90
|
+
},
|
|
91
|
+
formatDate: (value, options) => {
|
|
92
|
+
void state.value.locale;
|
|
93
|
+
return i18n.formatDate(value, options);
|
|
94
|
+
},
|
|
95
|
+
formatNumber: (value, options) => {
|
|
96
|
+
void state.value.locale;
|
|
97
|
+
return i18n.formatNumber(value, options);
|
|
98
|
+
},
|
|
99
|
+
formatRelativeTime: (value, unit, options) => {
|
|
100
|
+
void state.value.locale;
|
|
101
|
+
return i18n.formatRelativeTime(value, unit, options);
|
|
102
|
+
},
|
|
103
|
+
setLocale: i18n.setLocale,
|
|
104
|
+
setMessages: i18n.setMessages
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
exports.I18nInjectionKey = I18nInjectionKey;
|
|
109
|
+
exports.IrisI18nProvider = IrisI18nProvider;
|
|
110
|
+
exports.useI18n = useI18n;
|
|
111
|
+
//# sourceMappingURL=i18n.cjs.map
|
|
112
|
+
//# sourceMappingURL=i18n.cjs.map
|