@iris-ui-kit/vue 0.2.21 → 0.2.22

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.
Files changed (46) hide show
  1. package/dist/admin.cjs +14 -5
  2. package/dist/admin.cjs.map +1 -1
  3. package/dist/admin.js +2 -2
  4. package/dist/async.cjs +48 -2
  5. package/dist/async.cjs.map +1 -1
  6. package/dist/async.d.cts +27 -3
  7. package/dist/async.d.ts +27 -3
  8. package/dist/async.js +1 -1
  9. package/dist/chunk-25NYWSXP.js +20 -0
  10. package/dist/chunk-25NYWSXP.js.map +1 -0
  11. package/dist/{chunk-4FLIW67H.js → chunk-IAG64CHL.js} +21 -28
  12. package/dist/chunk-IAG64CHL.js.map +1 -0
  13. package/dist/chunk-NNOF7KUH.js +24 -0
  14. package/dist/chunk-NNOF7KUH.js.map +1 -0
  15. package/dist/{chunk-4WQOZ2OV.js → chunk-QQ25RKOO.js} +6 -4
  16. package/dist/chunk-QQ25RKOO.js.map +1 -0
  17. package/dist/chunk-SJB5DIIY.js +119 -0
  18. package/dist/chunk-SJB5DIIY.js.map +1 -0
  19. package/dist/form.cjs +28 -23
  20. package/dist/form.cjs.map +1 -1
  21. package/dist/form.d.cts +3 -2
  22. package/dist/form.d.ts +3 -2
  23. package/dist/form.js +2 -1
  24. package/dist/index.cjs +88 -35
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +10 -9
  27. package/dist/index.js.map +1 -1
  28. package/dist/machine.cjs +11 -4
  29. package/dist/machine.cjs.map +1 -1
  30. package/dist/machine.d.cts +12 -3
  31. package/dist/machine.d.ts +12 -3
  32. package/dist/machine.js +1 -1
  33. package/dist/resource.cjs +7 -3
  34. package/dist/resource.cjs.map +1 -1
  35. package/dist/resource.d.cts +10 -1
  36. package/dist/resource.d.ts +10 -1
  37. package/dist/resource.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-4FLIW67H.js.map +0 -1
  40. package/dist/chunk-4WQOZ2OV.js.map +0 -1
  41. package/dist/chunk-TQB5LVOH.js +0 -17
  42. package/dist/chunk-TQB5LVOH.js.map +0 -1
  43. package/dist/chunk-XSLBANUV.js +0 -16
  44. package/dist/chunk-XSLBANUV.js.map +0 -1
  45. package/dist/chunk-ZY4NLITM.js +0 -73
  46. package/dist/chunk-ZY4NLITM.js.map +0 -1
package/dist/admin.js CHANGED
@@ -1,6 +1,6 @@
1
- export { IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-4WQOZ2OV.js';
1
+ export { IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-QQ25RKOO.js';
2
2
  import './chunk-3FJUCHCC.js';
3
- import './chunk-TQB5LVOH.js';
3
+ import './chunk-NNOF7KUH.js';
4
4
  import './chunk-F77KIPOH.js';
5
5
  import './chunk-3V46HGRC.js';
6
6
  import './chunk-ZTPIXL7Q.js';
package/dist/async.cjs CHANGED
@@ -5,8 +5,31 @@ var core = require('@iris-ui-kit/core');
5
5
 
6
6
  // src/async/useAsyncResource.ts
7
7
  function useAsyncResource(fetcher, options = {}) {
8
+ const holder = {
9
+ // isRef-only resolution. NEVER toValue() here: the element type is itself
10
+ // a function, so toValue(fetcher) would *invoke the fetcher at setup*
11
+ // (spurious request) and toValue(getter) would do the same. isRef narrows
12
+ // to the only unambiguous reactive form: ref(fetcher) / computed(() =>
13
+ // fetcher) / shallowRef(fetcher) — a ComputedRef IS a Ref.
14
+ // F3 elision point — the ONE explicit narrowing cast: after the isRef
15
+ // guard the else arm is `F | (() => F)`, and the bare-getter arm's return
16
+ // `F` (a function) is not assignable to `F` (TS2322). No runtime
17
+ // discriminant distinguishes a plain fetcher from a bare getter (both are
18
+ // functions), so the cast encodes exactly the documented F3 ambiguity and
19
+ // adds no unsafety beyond F3.
20
+ current: vue.isRef(fetcher) ? fetcher.value : fetcher
21
+ };
22
+ if (vue.isRef(fetcher)) {
23
+ vue.watch(
24
+ fetcher,
25
+ (next) => {
26
+ holder.current = next;
27
+ },
28
+ { flush: "sync" }
29
+ );
30
+ }
8
31
  const config = "initialData" in options ? { initialData: options.initialData } : {};
9
- const resource = core.createAsyncResource(fetcher, config);
32
+ const resource = core.createAsyncResource((...params) => holder.current(...params), config);
10
33
  const state = vue.ref(resource.getState());
11
34
  const unsubscribe = resource.subscribe((next) => {
12
35
  state.value = next;
@@ -34,7 +57,30 @@ function useAsyncResource(fetcher, options = {}) {
34
57
  };
35
58
  }
36
59
  function usePaginatedResource(fetcher, options = {}) {
37
- const resource = core.createPaginatedResource(fetcher, {
60
+ const holder = {
61
+ // isRef-only resolution. NEVER toValue() here: the element type is itself
62
+ // a function, so toValue(fetcher) would *invoke the fetcher at setup*
63
+ // (spurious request) and toValue(getter) would do the same. isRef narrows
64
+ // to the only unambiguous reactive form: ref(fetcher) / computed(() =>
65
+ // fetcher) / shallowRef(fetcher) — a ComputedRef IS a Ref.
66
+ // F3 elision point — the ONE explicit narrowing cast: after the isRef
67
+ // guard the else arm is `F | (() => F)`, and the bare-getter arm's return
68
+ // `F` (a function) is not assignable to `F` (TS2322). No runtime
69
+ // discriminant distinguishes a plain fetcher from a bare getter (both are
70
+ // functions), so the cast encodes exactly the documented F3 ambiguity and
71
+ // adds no unsafety beyond F3.
72
+ current: vue.isRef(fetcher) ? fetcher.value : fetcher
73
+ };
74
+ if (vue.isRef(fetcher)) {
75
+ vue.watch(
76
+ fetcher,
77
+ (next) => {
78
+ holder.current = next;
79
+ },
80
+ { flush: "sync" }
81
+ );
82
+ }
83
+ const resource = core.createPaginatedResource((query) => holder.current(query), {
38
84
  pageSize: options.pageSize,
39
85
  mode: options.mode
40
86
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/async/useAsyncResource.ts","../src/async/usePaginatedResource.ts"],"names":["createAsyncResource","ref","onBeforeUnmount","onMounted","computed","createPaginatedResource"],"mappings":"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","file":"async.cjs","sourcesContent":["import { computed, onBeforeUnmount, onMounted, ref, type ComputedRef, type Ref } from 'vue'\nimport {\n createAsyncResource,\n type AsyncResource,\n type AsyncResourceConfig,\n type AsyncState,\n type AsyncStatus,\n} from '@iris-ui-kit/core'\n\nexport interface UseAsyncResourceOptions<T> extends AsyncResourceConfig<T> {\n /** Run `load()` (no params) once on mount. Default `false`. */\n immediate?: boolean\n}\n\nexport interface UseAsyncResourceReturn<T, P extends unknown[]> {\n status: ComputedRef<AsyncStatus>\n data: ComputedRef<T | undefined>\n error: ComputedRef<unknown>\n isLoading: ComputedRef<boolean>\n isError: ComputedRef<boolean>\n load: AsyncResource<T, P>['load']\n reload: AsyncResource<T, P>['reload']\n mutate: AsyncResource<T, P>['mutate']\n cancel: AsyncResource<T, P>['cancel']\n reset: AsyncResource<T, P>['reset']\n}\n\n/**\n * Vue binding for the framework-agnostic async resource. Creates the resource\n * in `setup()` (runs once, so the fetcher closure stays live) and bridges its\n * state into computed refs.\n *\n * ```ts\n * const users = useAsyncResource(() => api.listUsers(), { immediate: true })\n * // <IrisTable :data=\"users.data.value ?? []\" :loading=\"users.isLoading.value\" />\n * ```\n */\nexport function useAsyncResource<T, P extends unknown[] = []>(\n fetcher: (...params: P) => Promise<T>,\n options: UseAsyncResourceOptions<T> = {},\n): UseAsyncResourceReturn<T, P> {\n const config: AsyncResourceConfig<T> =\n 'initialData' in options ? { initialData: options.initialData } : {}\n const resource = createAsyncResource<T, P>(fetcher, config)\n\n const state = ref(resource.getState()) as Ref<AsyncState<T>>\n const unsubscribe = resource.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(() => {\n unsubscribe()\n // Abort any in-flight request on unmount so it can't write back into an\n // unmounted component (and cancels the underlying request when the fetcher\n // honors `signal`). Idempotent and safe with no in-flight load.\n resource.cancel()\n })\n\n if (options.immediate) {\n onMounted(() => {\n void resource.load(...([] as unknown as P))\n })\n }\n\n return {\n status: computed(() => state.value.status),\n data: computed(() => state.value.data),\n error: computed(() => state.value.error),\n isLoading: computed(() => state.value.status === 'loading'),\n isError: computed(() => state.value.status === 'error'),\n load: resource.load,\n reload: resource.reload,\n mutate: resource.mutate,\n cancel: resource.cancel,\n reset: resource.reset,\n }\n}\n","import { computed, onBeforeUnmount, onMounted, ref, type ComputedRef, type Ref } from 'vue'\nimport {\n createPaginatedResource,\n type PageQuery,\n type PageResult,\n type PaginatedResource,\n type PaginatedState,\n type PaginationMode,\n} from '@iris-ui-kit/core'\n\nexport interface UsePaginatedResourceOptions {\n pageSize?: number\n mode?: PaginationMode\n /** Load the first page on mount (page 1, or `loadMore` in infinite mode). */\n immediate?: boolean\n}\n\nexport interface UsePaginatedResourceReturn<T> {\n status: ComputedRef<PaginatedState<T>['status']>\n items: ComputedRef<T[]>\n page: ComputedRef<number>\n pageSize: ComputedRef<number>\n total: ComputedRef<number | undefined>\n error: ComputedRef<unknown>\n isLoading: ComputedRef<boolean>\n isError: ComputedRef<boolean>\n hasMore: ComputedRef<boolean>\n goToPage: PaginatedResource<T>['goToPage']\n loadMore: PaginatedResource<T>['loadMore']\n refresh: PaginatedResource<T>['refresh']\n setPageSize: PaginatedResource<T>['setPageSize']\n}\n\n/**\n * Vue binding for the server-side pagination resource. Created in `setup()`\n * (fetcher closure stays live) and bridged to computed refs.\n */\nexport function usePaginatedResource<T>(\n fetcher: (query: PageQuery) => Promise<PageResult<T>>,\n options: UsePaginatedResourceOptions = {},\n): UsePaginatedResourceReturn<T> {\n const resource = createPaginatedResource<T>(fetcher, {\n pageSize: options.pageSize,\n mode: options.mode,\n })\n\n const state = ref(resource.getState()) as Ref<PaginatedState<T>>\n const unsubscribe = resource.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(unsubscribe)\n\n if (options.immediate) {\n onMounted(() => {\n void (options.mode === 'infinite' ? resource.loadMore() : resource.goToPage(1))\n })\n }\n\n return {\n status: computed(() => state.value.status),\n items: computed(() => state.value.items),\n page: computed(() => state.value.page),\n pageSize: computed(() => state.value.pageSize),\n total: computed(() => state.value.total),\n error: computed(() => state.value.error),\n isLoading: computed(() => state.value.status === 'loading'),\n isError: computed(() => state.value.status === 'error'),\n // Touch state so this recomputes on every store change; hasMore() then\n // reads the resource's fresh internal batch state.\n hasMore: computed(() => {\n void state.value\n return resource.hasMore()\n }),\n goToPage: resource.goToPage,\n loadMore: resource.loadMore,\n refresh: resource.refresh,\n setPageSize: resource.setPageSize,\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/async/useAsyncResource.ts","../src/async/usePaginatedResource.ts"],"names":["isRef","watch","createAsyncResource","ref","onBeforeUnmount","onMounted","computed","createPaginatedResource"],"mappings":"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","file":"async.cjs","sourcesContent":["import {\n computed,\n isRef,\n onBeforeUnmount,\n onMounted,\n ref,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\nimport {\n createAsyncResource,\n type AsyncResource,\n type AsyncResourceConfig,\n type AsyncState,\n type AsyncStatus,\n} from '@iris-ui-kit/core'\n\nexport interface UseAsyncResourceOptions<T> extends AsyncResourceConfig<T> {\n /** Run `load()` (no params) once on mount. Default `false`. */\n immediate?: boolean\n}\n\nexport interface UseAsyncResourceReturn<T, P extends unknown[]> {\n status: ComputedRef<AsyncStatus>\n data: ComputedRef<T | undefined>\n error: ComputedRef<unknown>\n isLoading: ComputedRef<boolean>\n isError: ComputedRef<boolean>\n load: AsyncResource<T, P>['load']\n reload: AsyncResource<T, P>['reload']\n mutate: AsyncResource<T, P>['mutate']\n cancel: AsyncResource<T, P>['cancel']\n reset: AsyncResource<T, P>['reset']\n}\n\n/**\n * Vue binding for the framework-agnostic async resource. Creates the resource\n * in `setup()` (runs once, so the fetcher closure stays live) and bridges its\n * state into computed refs.\n *\n * ```ts\n * const users = useAsyncResource(() => api.listUsers(), { immediate: true })\n * // <IrisTable :data=\"users.data.value ?? []\" :loading=\"users.isLoading.value\" />\n * ```\n *\n * The fetcher may be reactive — pass `ref(fetcher)` or `computed(() => fetcher)`\n * and the *current* closure is used on every `load()`/`reload()`. Updating it\n * does NOT auto-refetch; call `reload()` yourself:\n *\n * ```ts\n * const users = useAsyncResource(computed(() => api.listUsers(props.id)), { immediate: true })\n * watch(() => props.id, () => void users.reload())\n * ```\n *\n * A bare getter `() => fetcher` is indistinguishable from a plain zero-arg\n * fetcher and is treated as one — use `computed`/`ref` for reactive fetchers.\n */\nexport function useAsyncResource<T, P extends unknown[] = []>(\n fetcher: MaybeRefOrGetter<(...params: P) => Promise<T>>,\n options: UseAsyncResourceOptions<T> = {},\n): UseAsyncResourceReturn<T, P> {\n // Plain-object holder (the Vue analog of React's `latest.current` ref):\n // core only ever sees the wrapper below, which re-reads `holder.current` at\n // call time — so `load()`, `reload()` and its `lastParams` replay all use\n // the fresh closure for the component's whole lifetime.\n const holder: { current: (...params: P) => Promise<T> } = {\n // isRef-only resolution. NEVER toValue() here: the element type is itself\n // a function, so toValue(fetcher) would *invoke the fetcher at setup*\n // (spurious request) and toValue(getter) would do the same. isRef narrows\n // to the only unambiguous reactive form: ref(fetcher) / computed(() =>\n // fetcher) / shallowRef(fetcher) — a ComputedRef IS a Ref.\n // F3 elision point — the ONE explicit narrowing cast: after the isRef\n // guard the else arm is `F | (() => F)`, and the bare-getter arm's return\n // `F` (a function) is not assignable to `F` (TS2322). No runtime\n // discriminant distinguishes a plain fetcher from a bare getter (both are\n // functions), so the cast encodes exactly the documented F3 ambiguity and\n // adds no unsafety beyond F3.\n current: (isRef(fetcher) ? fetcher.value : fetcher) as (...params: P) => Promise<T>,\n }\n if (isRef(fetcher)) {\n // Ref sources only. watch(fn, cb) would treat a plain function source as a\n // getter: evaluate it once at setup (spurious fetcher invocation) and track\n // its deps. flush: 'sync' (precedent useAdminShell.ts:67) guarantees a\n // load()/reload() in the same tick as the ref assignment sees the new\n // closure. Plain-object holder => the swap is not reactive, no re-render.\n watch(\n fetcher,\n (next) => {\n holder.current = next\n },\n { flush: 'sync' },\n )\n }\n\n const config: AsyncResourceConfig<T> =\n 'initialData' in options ? { initialData: options.initialData } : {}\n const resource = createAsyncResource<T, P>((...params) => holder.current(...params), config)\n\n const state = ref(resource.getState()) as Ref<AsyncState<T>>\n const unsubscribe = resource.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(() => {\n unsubscribe()\n // Abort any in-flight request on unmount so it can't write back into an\n // unmounted component (and cancels the underlying request when the fetcher\n // honors `signal`). Idempotent and safe with no in-flight load.\n resource.cancel()\n })\n\n if (options.immediate) {\n onMounted(() => {\n void resource.load(...([] as unknown as P))\n })\n }\n\n return {\n status: computed(() => state.value.status),\n data: computed(() => state.value.data),\n error: computed(() => state.value.error),\n isLoading: computed(() => state.value.status === 'loading'),\n isError: computed(() => state.value.status === 'error'),\n load: resource.load,\n reload: resource.reload,\n mutate: resource.mutate,\n cancel: resource.cancel,\n reset: resource.reset,\n }\n}\n","import {\n computed,\n isRef,\n onBeforeUnmount,\n onMounted,\n ref,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\nimport {\n createPaginatedResource,\n type PageQuery,\n type PageResult,\n type PaginatedResource,\n type PaginatedState,\n type PaginationMode,\n} from '@iris-ui-kit/core'\n\nexport interface UsePaginatedResourceOptions {\n pageSize?: number\n mode?: PaginationMode\n /** Load the first page on mount (page 1, or `loadMore` in infinite mode). */\n immediate?: boolean\n}\n\nexport interface UsePaginatedResourceReturn<T> {\n status: ComputedRef<PaginatedState<T>['status']>\n items: ComputedRef<T[]>\n page: ComputedRef<number>\n pageSize: ComputedRef<number>\n total: ComputedRef<number | undefined>\n error: ComputedRef<unknown>\n isLoading: ComputedRef<boolean>\n isError: ComputedRef<boolean>\n hasMore: ComputedRef<boolean>\n goToPage: PaginatedResource<T>['goToPage']\n loadMore: PaginatedResource<T>['loadMore']\n refresh: PaginatedResource<T>['refresh']\n setPageSize: PaginatedResource<T>['setPageSize']\n}\n\n/**\n * Vue binding for the server-side pagination resource. Created in `setup()`\n * (fetcher closure stays live) and bridged to computed refs.\n *\n * The fetcher may be reactive — pass `ref(fetcher)` or `computed(() => fetcher)`\n * and the *current* closure is used on every page load (`goToPage`/`loadMore`/\n * `refresh`). Updating it does NOT auto-refetch; trigger a load yourself:\n *\n * ```ts\n * const p = usePaginatedResource(computed(() => api.listUsers(props.id)))\n * watch(() => props.id, () => void p.refresh())\n * ```\n *\n * A bare getter `() => fetcher` is indistinguishable from a plain fetcher and\n * is treated as one — use `computed`/`ref` for reactive fetchers.\n */\nexport function usePaginatedResource<T>(\n fetcher: MaybeRefOrGetter<(query: PageQuery) => Promise<PageResult<T>>>,\n options: UsePaginatedResourceOptions = {},\n): UsePaginatedResourceReturn<T> {\n // Plain-object holder (the Vue analog of React's `latest.current` ref):\n // core only ever sees the wrapper below, which re-reads `holder.current` at\n // call time — so every page load (and `refresh`'s replay) uses the fresh\n // closure for the component's whole lifetime.\n const holder: { current: (query: PageQuery) => Promise<PageResult<T>> } = {\n // isRef-only resolution. NEVER toValue() here: the element type is itself\n // a function, so toValue(fetcher) would *invoke the fetcher at setup*\n // (spurious request) and toValue(getter) would do the same. isRef narrows\n // to the only unambiguous reactive form: ref(fetcher) / computed(() =>\n // fetcher) / shallowRef(fetcher) — a ComputedRef IS a Ref.\n // F3 elision point — the ONE explicit narrowing cast: after the isRef\n // guard the else arm is `F | (() => F)`, and the bare-getter arm's return\n // `F` (a function) is not assignable to `F` (TS2322). No runtime\n // discriminant distinguishes a plain fetcher from a bare getter (both are\n // functions), so the cast encodes exactly the documented F3 ambiguity and\n // adds no unsafety beyond F3.\n current: (isRef(fetcher) ? fetcher.value : fetcher) as (\n query: PageQuery,\n ) => Promise<PageResult<T>>,\n }\n if (isRef(fetcher)) {\n // Ref sources only. watch(fn, cb) would treat a plain function source as a\n // getter: evaluate it once at setup (spurious fetcher invocation) and track\n // its deps. flush: 'sync' (precedent useAdminShell.ts:67) guarantees a\n // page load in the same tick as the ref assignment sees the new closure.\n // Plain-object holder => the swap is not reactive, no re-render.\n watch(\n fetcher,\n (next) => {\n holder.current = next\n },\n { flush: 'sync' },\n )\n }\n\n const resource = createPaginatedResource<T>((query) => holder.current(query), {\n pageSize: options.pageSize,\n mode: options.mode,\n })\n\n const state = ref(resource.getState()) as Ref<PaginatedState<T>>\n const unsubscribe = resource.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(unsubscribe)\n\n if (options.immediate) {\n onMounted(() => {\n void (options.mode === 'infinite' ? resource.loadMore() : resource.goToPage(1))\n })\n }\n\n return {\n status: computed(() => state.value.status),\n items: computed(() => state.value.items),\n page: computed(() => state.value.page),\n pageSize: computed(() => state.value.pageSize),\n total: computed(() => state.value.total),\n error: computed(() => state.value.error),\n isLoading: computed(() => state.value.status === 'loading'),\n isError: computed(() => state.value.status === 'error'),\n // Touch state so this recomputes on every store change; hasMore() then\n // reads the resource's fresh internal batch state.\n hasMore: computed(() => {\n void state.value\n return resource.hasMore()\n }),\n goToPage: resource.goToPage,\n loadMore: resource.loadMore,\n refresh: resource.refresh,\n setPageSize: resource.setPageSize,\n }\n}\n"]}
package/dist/async.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ComputedRef } from 'vue';
1
+ import { ComputedRef, MaybeRefOrGetter } from 'vue';
2
2
  import { AsyncResourceConfig, AsyncStatus, AsyncResource, PaginationMode, PaginatedState, PaginatedResource, PageQuery, PageResult } from '@iris-ui-kit/core';
3
3
 
4
4
  interface UseAsyncResourceOptions<T> extends AsyncResourceConfig<T> {
@@ -26,8 +26,20 @@ interface UseAsyncResourceReturn<T, P extends unknown[]> {
26
26
  * const users = useAsyncResource(() => api.listUsers(), { immediate: true })
27
27
  * // <IrisTable :data="users.data.value ?? []" :loading="users.isLoading.value" />
28
28
  * ```
29
+ *
30
+ * The fetcher may be reactive — pass `ref(fetcher)` or `computed(() => fetcher)`
31
+ * and the *current* closure is used on every `load()`/`reload()`. Updating it
32
+ * does NOT auto-refetch; call `reload()` yourself:
33
+ *
34
+ * ```ts
35
+ * const users = useAsyncResource(computed(() => api.listUsers(props.id)), { immediate: true })
36
+ * watch(() => props.id, () => void users.reload())
37
+ * ```
38
+ *
39
+ * A bare getter `() => fetcher` is indistinguishable from a plain zero-arg
40
+ * fetcher and is treated as one — use `computed`/`ref` for reactive fetchers.
29
41
  */
30
- declare function useAsyncResource<T, P extends unknown[] = []>(fetcher: (...params: P) => Promise<T>, options?: UseAsyncResourceOptions<T>): UseAsyncResourceReturn<T, P>;
42
+ declare function useAsyncResource<T, P extends unknown[] = []>(fetcher: MaybeRefOrGetter<(...params: P) => Promise<T>>, options?: UseAsyncResourceOptions<T>): UseAsyncResourceReturn<T, P>;
31
43
 
32
44
  interface UsePaginatedResourceOptions {
33
45
  pageSize?: number;
@@ -53,7 +65,19 @@ interface UsePaginatedResourceReturn<T> {
53
65
  /**
54
66
  * Vue binding for the server-side pagination resource. Created in `setup()`
55
67
  * (fetcher closure stays live) and bridged to computed refs.
68
+ *
69
+ * The fetcher may be reactive — pass `ref(fetcher)` or `computed(() => fetcher)`
70
+ * and the *current* closure is used on every page load (`goToPage`/`loadMore`/
71
+ * `refresh`). Updating it does NOT auto-refetch; trigger a load yourself:
72
+ *
73
+ * ```ts
74
+ * const p = usePaginatedResource(computed(() => api.listUsers(props.id)))
75
+ * watch(() => props.id, () => void p.refresh())
76
+ * ```
77
+ *
78
+ * A bare getter `() => fetcher` is indistinguishable from a plain fetcher and
79
+ * is treated as one — use `computed`/`ref` for reactive fetchers.
56
80
  */
57
- declare function usePaginatedResource<T>(fetcher: (query: PageQuery) => Promise<PageResult<T>>, options?: UsePaginatedResourceOptions): UsePaginatedResourceReturn<T>;
81
+ declare function usePaginatedResource<T>(fetcher: MaybeRefOrGetter<(query: PageQuery) => Promise<PageResult<T>>>, options?: UsePaginatedResourceOptions): UsePaginatedResourceReturn<T>;
58
82
 
59
83
  export { type UseAsyncResourceOptions, type UseAsyncResourceReturn, type UsePaginatedResourceOptions, type UsePaginatedResourceReturn, useAsyncResource, usePaginatedResource };
package/dist/async.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ComputedRef } from 'vue';
1
+ import { ComputedRef, MaybeRefOrGetter } from 'vue';
2
2
  import { AsyncResourceConfig, AsyncStatus, AsyncResource, PaginationMode, PaginatedState, PaginatedResource, PageQuery, PageResult } from '@iris-ui-kit/core';
3
3
 
4
4
  interface UseAsyncResourceOptions<T> extends AsyncResourceConfig<T> {
@@ -26,8 +26,20 @@ interface UseAsyncResourceReturn<T, P extends unknown[]> {
26
26
  * const users = useAsyncResource(() => api.listUsers(), { immediate: true })
27
27
  * // <IrisTable :data="users.data.value ?? []" :loading="users.isLoading.value" />
28
28
  * ```
29
+ *
30
+ * The fetcher may be reactive — pass `ref(fetcher)` or `computed(() => fetcher)`
31
+ * and the *current* closure is used on every `load()`/`reload()`. Updating it
32
+ * does NOT auto-refetch; call `reload()` yourself:
33
+ *
34
+ * ```ts
35
+ * const users = useAsyncResource(computed(() => api.listUsers(props.id)), { immediate: true })
36
+ * watch(() => props.id, () => void users.reload())
37
+ * ```
38
+ *
39
+ * A bare getter `() => fetcher` is indistinguishable from a plain zero-arg
40
+ * fetcher and is treated as one — use `computed`/`ref` for reactive fetchers.
29
41
  */
30
- declare function useAsyncResource<T, P extends unknown[] = []>(fetcher: (...params: P) => Promise<T>, options?: UseAsyncResourceOptions<T>): UseAsyncResourceReturn<T, P>;
42
+ declare function useAsyncResource<T, P extends unknown[] = []>(fetcher: MaybeRefOrGetter<(...params: P) => Promise<T>>, options?: UseAsyncResourceOptions<T>): UseAsyncResourceReturn<T, P>;
31
43
 
32
44
  interface UsePaginatedResourceOptions {
33
45
  pageSize?: number;
@@ -53,7 +65,19 @@ interface UsePaginatedResourceReturn<T> {
53
65
  /**
54
66
  * Vue binding for the server-side pagination resource. Created in `setup()`
55
67
  * (fetcher closure stays live) and bridged to computed refs.
68
+ *
69
+ * The fetcher may be reactive — pass `ref(fetcher)` or `computed(() => fetcher)`
70
+ * and the *current* closure is used on every page load (`goToPage`/`loadMore`/
71
+ * `refresh`). Updating it does NOT auto-refetch; trigger a load yourself:
72
+ *
73
+ * ```ts
74
+ * const p = usePaginatedResource(computed(() => api.listUsers(props.id)))
75
+ * watch(() => props.id, () => void p.refresh())
76
+ * ```
77
+ *
78
+ * A bare getter `() => fetcher` is indistinguishable from a plain fetcher and
79
+ * is treated as one — use `computed`/`ref` for reactive fetchers.
56
80
  */
57
- declare function usePaginatedResource<T>(fetcher: (query: PageQuery) => Promise<PageResult<T>>, options?: UsePaginatedResourceOptions): UsePaginatedResourceReturn<T>;
81
+ declare function usePaginatedResource<T>(fetcher: MaybeRefOrGetter<(query: PageQuery) => Promise<PageResult<T>>>, options?: UsePaginatedResourceOptions): UsePaginatedResourceReturn<T>;
58
82
 
59
83
  export { type UseAsyncResourceOptions, type UseAsyncResourceReturn, type UsePaginatedResourceOptions, type UsePaginatedResourceReturn, useAsyncResource, usePaginatedResource };
package/dist/async.js CHANGED
@@ -1,3 +1,3 @@
1
- export { useAsyncResource, usePaginatedResource } from './chunk-ZY4NLITM.js';
1
+ export { useAsyncResource, usePaginatedResource } from './chunk-SJB5DIIY.js';
2
2
  //# sourceMappingURL=async.js.map
3
3
  //# sourceMappingURL=async.js.map
@@ -0,0 +1,20 @@
1
+ import { onMounted, onScopeDispose, shallowRef } from 'vue';
2
+ import { createResourceController } from '@iris-ui-kit/core';
3
+ export { createClientFetcher, createResourceController } from '@iris-ui-kit/core';
4
+
5
+ // src/resource/useResourceController.ts
6
+ function useResourceController(config) {
7
+ const controller = createResourceController({ ...config, immediate: false });
8
+ const immediate = config.immediate !== false;
9
+ if (immediate) {
10
+ onMounted(() => void controller.load());
11
+ }
12
+ onScopeDispose(() => controller.destroy());
13
+ const state = shallowRef(controller.getState());
14
+ onScopeDispose(controller.subscribe((next) => state.value = next));
15
+ return { ...controller, state };
16
+ }
17
+
18
+ export { useResourceController };
19
+ //# sourceMappingURL=chunk-25NYWSXP.js.map
20
+ //# sourceMappingURL=chunk-25NYWSXP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/resource/useResourceController.ts"],"names":[],"mappings":";;;;;AA4BO,SAAS,sBACd,MAAA,EAC0B;AAC1B,EAAA,MAAM,aAAa,wBAAA,CAAyB,EAAE,GAAG,MAAA,EAAQ,SAAA,EAAW,OAAO,CAAA;AAC3E,EAAA,MAAM,SAAA,GAAY,OAAO,SAAA,KAAc,KAAA;AAEvC,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,CAAU,MAAM,KAAK,UAAA,CAAW,IAAA,EAAM,CAAA;AAAA,EACxC;AAKA,EAAA,cAAA,CAAe,MAAM,UAAA,CAAW,OAAA,EAAS,CAAA;AAEzC,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,UAAA,CAAW,QAAA,EAAU,CAAA;AAC9C,EAAA,cAAA,CAAe,WAAW,SAAA,CAAU,CAAC,SAAU,KAAA,CAAM,KAAA,GAAQ,IAAK,CAAC,CAAA;AACnE,EAAA,OAAO,EAAE,GAAG,UAAA,EAAY,KAAA,EAAM;AAChC","file":"chunk-25NYWSXP.js","sourcesContent":["import { onMounted, onScopeDispose, shallowRef, type ShallowRef } from 'vue'\nimport {\n createResourceController,\n type ResourceController,\n type ResourceControllerConfig,\n type ResourceState,\n} from '@iris-ui-kit/core'\n\nexport interface UseResourceController<T> extends ResourceController<T> {\n /** The live resource state as a reactive ref (rows, total, page, loading, selectedKeys). */\n state: ShallowRef<ResourceState<T>>\n}\n\n/**\n * Vue bridge over the framework-agnostic {@link createResourceController} (L4\n * CRUD list composite). Creates the controller once in `setup()`, mirrors its\n * store into a `shallowRef`, and returns the controller plus that live `state`.\n * A thin bridge — all logic lives in `@iris-ui-kit/core`.\n *\n * Constructed with `immediate: false` so no fetch fires during `setup()`\n * (SSR-safe: server rendering never runs `onMounted`); the initial load is\n * kicked from `onMounted` when the caller's config says `immediate !== false`.\n * The `onMounted` registration is conditional on that flag — inside a bare\n * `effectScope` there is no component instance and Vue would warn on an\n * unconditional registration. The controller is torn down on scope dispose\n * (aborting any in-flight request and detaching the state mirror), so a late\n * response never writes back to a torn-down instance.\n */\nexport function useResourceController<T>(\n config: ResourceControllerConfig<T>,\n): UseResourceController<T> {\n const controller = createResourceController({ ...config, immediate: false })\n const immediate = config.immediate !== false\n\n if (immediate) {\n onMounted(() => void controller.load())\n }\n // Abort any in-flight fetch + detach the controller's internal subscriptions\n // on scope dispose (a late response must not write back to a torn-down\n // instance). Fires on unmount inside a component scope and on `scope.stop()`\n // inside a bare effectScope.\n onScopeDispose(() => controller.destroy())\n\n const state = shallowRef(controller.getState())\n onScopeDispose(controller.subscribe((next) => (state.value = next)))\n return { ...controller, state }\n}\n"]}
@@ -1,7 +1,7 @@
1
- import { defineComponent, provide, ref, h, inject, onBeforeUnmount, computed } from 'vue';
2
- import { createFormStore, formatPath, getByPath } from '@iris-ui-kit/core';
1
+ import { useStore, useStoreSelector } from './chunk-75GSYEJS.js';
2
+ import { defineComponent, provide, ref, h, inject, computed } from 'vue';
3
+ import { createFormStore, formatPath, parsePath, getByPath } from '@iris-ui-kit/core';
3
4
 
4
- // src/form/Form.ts
5
5
  var FormInjectionKey = /* @__PURE__ */ Symbol("IrisForm");
6
6
  function useFormContext() {
7
7
  const form = inject(FormInjectionKey, null);
@@ -59,11 +59,7 @@ var IrisForm = defineComponent({
59
59
  });
60
60
  function useForm(config) {
61
61
  const form = createFormStore(config);
62
- const state = ref(form.getState());
63
- const unsubscribe = form.subscribe((next) => {
64
- state.value = next;
65
- });
66
- onBeforeUnmount(unsubscribe);
62
+ const state = useStore(form.store);
67
63
  return {
68
64
  form,
69
65
  state,
@@ -88,22 +84,24 @@ function useForm(config) {
88
84
  }
89
85
  function useField(name) {
90
86
  const form = useFormContext();
91
- const state = ref(form.getState());
92
- const unsubscribe = form.subscribe((next) => {
93
- state.value = next;
94
- });
95
- onBeforeUnmount(unsubscribe);
96
87
  const key = formatPath(name);
97
- const value = computed(() => getByPath(state.value.values, name));
98
- const error = computed(() => state.value.errors[key]);
88
+ const segments = parsePath(name);
89
+ const valueSlice = useStoreSelector(form.store, (s) => getByPath(s.values, segments));
90
+ const errorSlice = useStoreSelector(form.store, (s) => s.errors[key]);
91
+ const touchedSlice = useStoreSelector(form.store, (s) => Boolean(s.touched[key]));
92
+ const dirtySlice = useStoreSelector(form.store, (s) => Boolean(s.dirty[key]));
93
+ const value = computed(() => valueSlice.value);
94
+ const error = computed(() => errorSlice.value);
95
+ const touched = computed(() => touchedSlice.value);
96
+ const dirty = computed(() => dirtySlice.value);
99
97
  return {
100
98
  name,
101
99
  value,
102
100
  error,
103
- touched: computed(() => Boolean(state.value.touched[key])),
104
- dirty: computed(() => Boolean(state.value.dirty[key])),
101
+ touched,
102
+ dirty,
105
103
  setValue: (next) => form.setFieldValue(name, next),
106
- setTouched: (touched = true) => form.setFieldTouched(name, touched),
104
+ setTouched: (touched2 = true) => form.setFieldTouched(name, touched2),
107
105
  fieldProps: computed(() => ({
108
106
  modelValue: value.value,
109
107
  "onUpdate:modelValue": (next) => form.setFieldValue(name, next),
@@ -114,16 +112,11 @@ function useField(name) {
114
112
  }
115
113
  function useFieldArray(name) {
116
114
  const form = useFormContext();
117
- const state = ref(form.getState());
118
- const unsubscribe = form.subscribe((next) => {
119
- state.value = next;
120
- });
121
- onBeforeUnmount(unsubscribe);
115
+ const raw = useStoreSelector(form.store, (s) => s.values[name]);
116
+ const fields = computed(() => Array.isArray(raw.value) ? raw.value : []);
122
117
  return {
123
118
  name,
124
- fields: computed(
125
- () => Array.isArray(state.value.values[name]) ? state.value.values[name] : []
126
- ),
119
+ fields,
127
120
  push: (item) => form.arrayPush(name, item),
128
121
  remove: (index) => form.arrayRemove(name, index),
129
122
  insert: (index, item) => form.arrayInsert(name, index, item),
@@ -133,5 +126,5 @@ function useFieldArray(name) {
133
126
  }
134
127
 
135
128
  export { FormInjectionKey, IrisForm, useField, useFieldArray, useForm, useFormContext };
136
- //# sourceMappingURL=chunk-4FLIW67H.js.map
137
- //# sourceMappingURL=chunk-4FLIW67H.js.map
129
+ //# sourceMappingURL=chunk-IAG64CHL.js.map
130
+ //# sourceMappingURL=chunk-IAG64CHL.js.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":["computed","touched"],"mappings":"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","file":"chunk-IAG64CHL.js","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, 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'\nimport { useStore } from '../useStore'\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 `useStore` (a\n * shallow snapshot ref updated on each store change, detached on scope\n * dispose). 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\n // Whole-state bridge via the shared `useStore` (shallow, detached on scope\n // dispose — identical to the old `onBeforeUnmount` for setup-scope consumers,\n // and correct for nested scopes too). The published `state: Ref<FormState<V>>`\n // type is unchanged (R5); the runtime is a shallow snapshot — the store is the\n // only write path (it always was), so this localized cast is type-only. The\n // per-field narrowing payoff comes from `useField` / `useFieldArray`; the\n // form-level computeds below are whole-form aggregates by design.\n const state = useStore(form.store) as unknown as Ref<FormState<V>>\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, type ComputedRef } from 'vue'\nimport { formatPath, getByPath, parsePath } from '@iris-ui-kit/core'\nimport { useFormContext } from './context'\nimport { useStoreSelector } from '../useStore'\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\n // Canonical key for per-field state lookups (a flat key maps to itself).\n const key = formatPath(name)\n // Hoisted once — selectors run on every store emission (core `subscribeWith`).\n const segments = parsePath(name)\n\n // Four narrow per-field slices, each Object.is-gated by core `subscribeWith`.\n // Core writes use structural sharing (`setByPath`, per-key flag spread-copies),\n // so an unrelated keystroke does NOT move these refs and this field's computeds\n // do not invalidate (previously a whole-form deep `ref` re-ran every field's\n // computeds on every emission). Selectors are allocation-free by construction.\n const valueSlice = useStoreSelector(form.store, (s) => getByPath(s.values, segments) as T)\n const errorSlice = useStoreSelector(form.store, (s) => s.errors[key])\n const touchedSlice = useStoreSelector(form.store, (s) => Boolean(s.touched[key]))\n const dirtySlice = useStoreSelector(form.store, (s) => Boolean(s.dirty[key]))\n\n // computed wrappers keep the published ComputedRef<T> member types exactly as\n // declared (a bare shallow ref is not assignable to ComputedRef in Vue 3.4+),\n // and each computed invalidates only when its own slice actually moves.\n const value = computed(() => valueSlice.value)\n const error = computed(() => errorSlice.value)\n const touched = computed(() => touchedSlice.value)\n const dirty = computed(() => dirtySlice.value)\n\n return {\n name,\n value,\n error,\n touched,\n dirty,\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, type ComputedRef } from 'vue'\nimport { useFormContext } from './context'\nimport { useStoreSelector } from '../useStore'\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\n // One narrow slice for the whole array, Object.is-gated by core `subscribeWith`.\n // CRITICAL: the selector must not allocate — `(s) => s.values[name] ?? []` would\n // mint a fresh [] per emission and defeat the Object.is gate — so the `[]`\n // fallback lives in the computed wrapper (re-evaluated only when the raw slice\n // moves). Array mutations produce a fresh array (correct re-render); untouched\n // rows are unaffected.\n const raw = useStoreSelector(form.store, (s) => s.values[name])\n const fields = computed(() => (Array.isArray(raw.value) ? raw.value : []) as T[])\n\n return {\n name,\n fields,\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"]}
@@ -0,0 +1,24 @@
1
+ import { ref, onScopeDispose } from 'vue';
2
+
3
+ // src/machine/useMachine.ts
4
+ function useMachine(machine, options = {}) {
5
+ const state = ref(machine.store.getState());
6
+ const unsubscribe = machine.store.subscribe((next) => {
7
+ state.value = next;
8
+ });
9
+ onScopeDispose(
10
+ () => {
11
+ unsubscribe();
12
+ if (options.stopOnUnmount !== false) machine.stop();
13
+ },
14
+ // failSilently: outside any effect scope there is nothing to dispose —
15
+ // registering is a no-op and must not emit the "no active effect scope"
16
+ // dev warning. Within a component/effectScope this arg changes nothing.
17
+ true
18
+ );
19
+ return { state, send: machine.send };
20
+ }
21
+
22
+ export { useMachine };
23
+ //# sourceMappingURL=chunk-NNOF7KUH.js.map
24
+ //# sourceMappingURL=chunk-NNOF7KUH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/machine/useMachine.ts"],"names":[],"mappings":";;;AAmBO,SAAS,UAAA,CACd,OAAA,EACA,OAAA,GAAuC,EAAC,EAIxC;AACA,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAA;AAC1C,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,CAAM,SAAA,CAAU,CAAC,IAAA,KAAS;AACpD,IAAA,KAAA,CAAM,KAAA,GAAQ,IAAA;AAAA,EAChB,CAAC,CAAA;AACD,EAAA,cAAA;AAAA,IACE,MAAM;AAEJ,MAAA,WAAA,EAAY;AACZ,MAAA,IAAI,OAAA,CAAQ,aAAA,KAAkB,KAAA,EAAO,OAAA,CAAQ,IAAA,EAAK;AAAA,IACpD,CAAA;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA,GACF;AACA,EAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,OAAA,CAAQ,IAAA,EAAK;AACrC","file":"chunk-NNOF7KUH.js","sourcesContent":["import { onScopeDispose, ref, type Ref } from 'vue'\nimport type { Machine, MachineEvent, MachineState } from '@iris-ui-kit/core'\n\n/**\n * Bridge a framework-agnostic `Machine` into Vue reactivity.\n *\n * Returns a reactive ref of the current `{ value, context }` state and a\n * stable `send` function. Subscription is created eagerly and cleaned up on\n * scope dispose, so this hook is safe to call from any component's `setup()`.\n *\n * Teardown semantics: when the owning scope is disposed (component unmount,\n * `effectScope().stop()`, end of SSR render), the store subscription is\n * detached AND — by default — `machine.stop()` is called: pending `after`\n * timers are cancelled and further `send` calls become no-ops, so a delayed\n * transition can never fire into a disposed consumer (see the core `Machine`\n * contract). Pass `{ stopOnUnmount: false }` only when the machine is shared\n * with other consumers that outlive this scope — the subscription is still\n * detached, the machine keeps running.\n */\nexport function useMachine<TState extends string, TContext, TEvent extends MachineEvent>(\n machine: Machine<TState, TContext, TEvent>,\n options: { stopOnUnmount?: boolean } = {},\n): {\n state: Ref<MachineState<TState, TContext>>\n send: (event: TEvent) => void\n} {\n const state = ref(machine.store.getState()) as Ref<MachineState<TState, TContext>>\n const unsubscribe = machine.store.subscribe((next) => {\n state.value = next\n })\n onScopeDispose(\n () => {\n // Detach the bridge first (conservative order), then stop the machine.\n unsubscribe()\n if (options.stopOnUnmount !== false) machine.stop()\n },\n // failSilently: outside any effect scope there is nothing to dispose —\n // registering is a no-op and must not emit the \"no active effect scope\"\n // dev warning. Within a component/effectScope this arg changes nothing.\n true,\n )\n return { state, send: machine.send }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { useFloating, useDismiss } from './chunk-3FJUCHCC.js';
2
- import { useMachine } from './chunk-TQB5LVOH.js';
2
+ import { useMachine } from './chunk-NNOF7KUH.js';
3
3
  import { findFirstElement, mergeSlotProps } from './chunk-F77KIPOH.js';
4
4
  import { IrisHeaderLayout, IrisSidebarLayout } from './chunk-3V46HGRC.js';
5
5
  import { IrisThemeKey } from './chunk-ZTPIXL7Q.js';
@@ -1296,7 +1296,9 @@ var IrisAdminTabs = defineComponent({
1296
1296
  onClick: (e) => {
1297
1297
  e.stopPropagation();
1298
1298
  close(tab.key);
1299
- const root = e.currentTarget.closest('[role="tablist"]');
1299
+ const root = e.currentTarget.closest(
1300
+ '[role="tablist"]'
1301
+ );
1300
1302
  void nextTick(() => focusTab(root, props.nav.getState().activeKey));
1301
1303
  }
1302
1304
  },
@@ -1819,5 +1821,5 @@ function useAdminPreferences(preferences, hydrate = true) {
1819
1821
  }
1820
1822
 
1821
1823
  export { DropdownContextKey, IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisBreadcrumb, IrisBreadcrumbItem, IrisDropdown, IrisDropdownItem, IrisDropdownMenu, IrisDropdownSeparator, IrisDropdownTrigger, IrisIcon, IrisNavMenu, useAdminPreferences, useAdminShell, useTabsNav };
1822
- //# sourceMappingURL=chunk-4WQOZ2OV.js.map
1823
- //# sourceMappingURL=chunk-4WQOZ2OV.js.map
1824
+ //# sourceMappingURL=chunk-QQ25RKOO.js.map
1825
+ //# sourceMappingURL=chunk-QQ25RKOO.js.map