@iris-ui-kit/vue 0.2.22 → 0.2.24

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 (43) hide show
  1. package/dist/behaviors.cjs +87 -48
  2. package/dist/behaviors.cjs.map +1 -1
  3. package/dist/behaviors.d.cts +2 -2
  4. package/dist/behaviors.d.ts +2 -2
  5. package/dist/behaviors.js +1 -1
  6. package/dist/{chunk-CWXH44OF.js → chunk-3IH7IJ5S.js} +90 -51
  7. package/dist/chunk-3IH7IJ5S.js.map +1 -0
  8. package/dist/{chunk-ENKE2BPC.js → chunk-BEPH6PPL.js} +23 -5
  9. package/dist/chunk-BEPH6PPL.js.map +1 -0
  10. package/dist/{chunk-7IOJADZC.js → chunk-DNRQVM3Q.js} +5 -5
  11. package/dist/chunk-DNRQVM3Q.js.map +1 -0
  12. package/dist/{chunk-PPYNXNGZ.js → chunk-FSDQHBI2.js} +10 -4
  13. package/dist/chunk-FSDQHBI2.js.map +1 -0
  14. package/dist/{chunk-FG5USQSY.js → chunk-I7PR4ILF.js} +8 -3
  15. package/dist/chunk-I7PR4ILF.js.map +1 -0
  16. package/dist/data.cjs +3 -3
  17. package/dist/data.cjs.map +1 -1
  18. package/dist/data.d.cts +8 -4
  19. package/dist/data.d.ts +8 -4
  20. package/dist/data.js +1 -1
  21. package/dist/error-boundary.cjs +7 -1
  22. package/dist/error-boundary.cjs.map +1 -1
  23. package/dist/error-boundary.js +1 -1
  24. package/dist/index.cjs +138 -60
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +21 -11
  27. package/dist/index.js.map +1 -1
  28. package/dist/skeletons.cjs +21 -3
  29. package/dist/skeletons.cjs.map +1 -1
  30. package/dist/skeletons.d.cts +21 -0
  31. package/dist/skeletons.d.ts +21 -0
  32. package/dist/skeletons.js +1 -1
  33. package/dist/undo.cjs +6 -1
  34. package/dist/undo.cjs.map +1 -1
  35. package/dist/undo.d.cts +5 -0
  36. package/dist/undo.d.ts +5 -0
  37. package/dist/undo.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-7IOJADZC.js.map +0 -1
  40. package/dist/chunk-CWXH44OF.js.map +0 -1
  41. package/dist/chunk-ENKE2BPC.js.map +0 -1
  42. package/dist/chunk-FG5USQSY.js.map +0 -1
  43. package/dist/chunk-PPYNXNGZ.js.map +0 -1
package/dist/data.d.ts CHANGED
@@ -17,10 +17,14 @@ interface UseDataSource<T> extends DataSourceController<T> {
17
17
  * mirrors its store into a reactive ref, and returns the controller plus its
18
18
  * live `state`. A thin bridge — all logic lives in `@iris-ui-kit/core`.
19
19
  *
20
- * Constructed with `immediate: false` so no fetch fires during `setup()`; the
21
- * initial load is kicked from `onMounted` and the controller is torn down on
22
- * scope dispose (aborting any in-flight request) so a late response never
23
- * writes back to a torn-down instance.
20
+ * Constructed with `immediate: false` so no fetch fires during `setup()`
21
+ * (SSR-safe: server rendering never runs `onMounted`); the initial load is
22
+ * kicked from `onMounted` when the caller's config says `immediate !== false`.
23
+ * The `onMounted` registration is conditional on that flag — inside a bare
24
+ * `effectScope` there is no component instance and Vue would warn on an
25
+ * unconditional registration. The controller is torn down on scope dispose
26
+ * (aborting any in-flight fetch), so a late response never writes back to a
27
+ * torn-down instance.
24
28
  */
25
29
  declare function useDataSource<T>(config: DataSourceConfig<T>): UseDataSource<T>;
26
30
 
package/dist/data.js CHANGED
@@ -1,4 +1,4 @@
1
- export { createClientDataSource, createDataSource, createGroupedView, useDataSource, useGroupedView } from './chunk-7IOJADZC.js';
1
+ export { createClientDataSource, createDataSource, createGroupedView, useDataSource, useGroupedView } from './chunk-DNRQVM3Q.js';
2
2
  import './chunk-75GSYEJS.js';
3
3
  //# sourceMappingURL=data.js.map
4
4
  //# sourceMappingURL=data.js.map
@@ -59,12 +59,18 @@ var IrisErrorBoundary = vue.defineComponent({
59
59
  setup(props, { attrs, slots }) {
60
60
  const { t } = useI18n();
61
61
  const error = vue.ref(null);
62
+ const appContext = vue.getCurrentInstance().appContext;
62
63
  const reset = () => {
63
64
  error.value = null;
64
65
  };
65
- vue.onErrorCaptured((err, _instance, info) => {
66
+ vue.onErrorCaptured((err, instance, info) => {
66
67
  error.value = err;
67
68
  props.onError?.(err, info);
69
+ try {
70
+ appContext.config.errorHandler?.(err, instance, info);
71
+ } catch (handlerError) {
72
+ console.error(handlerError);
73
+ }
68
74
  return false;
69
75
  });
70
76
  const renderDefaultFallback = () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/i18n/context.ts","../src/i18n/useI18n.ts","../src/error-boundary/IrisErrorBoundary.ts"],"names":["createI18n","inject","ref","onBeforeUnmount","computed","defineComponent","onErrorCaptured","h"],"mappings":"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","file":"error-boundary.cjs","sourcesContent":["import { type InjectionKey } from 'vue'\nimport type { I18n } from '@iris-ui-kit/core'\n\n/** Injection key carrying the active {@link I18n} instance to `useI18n`. */\nexport const I18nInjectionKey: InjectionKey<I18n> = Symbol('IrisI18n')\n","import { computed, inject, onBeforeUnmount, ref, type ComputedRef, type Ref } from 'vue'\nimport { createI18n, type I18n, type I18nState } from '@iris-ui-kit/core'\nimport { I18nInjectionKey } from './context'\n\nexport interface UseI18nReturn {\n locale: ComputedRef<string>\n t: I18n['t']\n formatDate: I18n['formatDate']\n formatNumber: I18n['formatNumber']\n formatRelativeTime: I18n['formatRelativeTime']\n setLocale: I18n['setLocale']\n setMessages: I18n['setMessages']\n}\n\n// Shared fallback so components can call `useI18n()` without an enclosing\n// provider — they get English defaults rather than throwing.\nlet fallbackI18n: I18n | null = null\nfunction getFallback(): I18n {\n if (fallbackI18n === null) fallbackI18n = createI18n()\n return fallbackI18n\n}\n\n/**\n * Reactive access to the active locale + translation/formatting helpers.\n * Works with or without an `<IrisI18nProvider>` (falls back to English\n * defaults). `t` / formatters read the live locale at call time, and `locale`\n * is a reactive ref so templates re-render on change.\n */\nexport function useI18n(): UseI18nReturn {\n const i18n = inject(I18nInjectionKey, null) ?? getFallback()\n const state = ref(i18n.getState()) as Ref<I18nState>\n const unsubscribe = i18n.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(unsubscribe)\n\n return {\n locale: computed(() => state.value.locale),\n t: (key, params) => {\n void state.value.locale\n return i18n.t(key, params)\n },\n formatDate: (value, options) => {\n void state.value.locale\n return i18n.formatDate(value, options)\n },\n formatNumber: (value, options) => {\n void state.value.locale\n return i18n.formatNumber(value, options)\n },\n formatRelativeTime: (value, unit, options) => {\n void state.value.locale\n return i18n.formatRelativeTime(value, unit, options)\n },\n setLocale: i18n.setLocale,\n setMessages: i18n.setMessages,\n }\n}\n","import { defineComponent, h, onErrorCaptured, ref, type PropType, type VNode } from 'vue'\nimport { useI18n } from '../i18n'\n\n/** Render-customization payload handed to the `#fallback` slot / `fallback` prop. */\nexport interface IrisErrorBoundaryFallbackProps {\n /** The error caught while rendering the guarded subtree. */\n error: unknown\n /** Clears the error and re-attempts rendering the children. */\n reset: () => void\n}\n\n/** Public prop surface of {@link IrisErrorBoundary}. */\nexport interface IrisErrorBoundaryProps {\n /**\n * Custom fallback renderer. Receives `{ error, reset }` and returns the\n * fallback UI. A `#fallback` scoped slot takes precedence when both are given.\n * When neither is supplied the themed DEFAULT fallback renders.\n */\n fallback?: (props: IrisErrorBoundaryFallbackProps) => VNode | VNode[] | string\n /** Invoked with the caught error (and Vue's error-info string) for logging. */\n onError?: (error: unknown, info?: string) => void\n}\n\n/**\n * IrisErrorBoundary: catches errors thrown while rendering its guarded subtree\n * (default slot), switches to a fallback, and exposes `reset()` to clear the\n * error and re-attempt rendering. Catching does NOT rethrow — a single broken\n * subtree no longer crashes the whole tree.\n *\n * Customize via a `#fallback` scoped slot or the `fallback` prop, each receiving\n * `{ error, reset }`. Without either, a themed, accessible (`role=\"alert\"`)\n * default fallback shows the error message + a retry button, i18n'd through\n * `useI18n()` (English defaults without a provider).\n */\nexport const IrisErrorBoundary = defineComponent({\n name: 'IrisErrorBoundary',\n inheritAttrs: false,\n props: {\n fallback: {\n type: Function as PropType<IrisErrorBoundaryProps['fallback']>,\n default: undefined,\n },\n onError: {\n type: Function as PropType<IrisErrorBoundaryProps['onError']>,\n default: undefined,\n },\n },\n setup(props, { attrs, slots }) {\n const { t } = useI18n()\n const error = ref<unknown>(null)\n\n const reset = () => {\n error.value = null\n }\n\n onErrorCaptured((err, _instance, info) => {\n error.value = err\n props.onError?.(err, info)\n // Stop further propagation so the error does not crash the parent tree.\n return false\n })\n\n const renderDefaultFallback = (): VNode => {\n const message =\n error.value instanceof Error && error.value.message\n ? error.value.message\n : t('errorBoundary.message')\n\n return h(\n 'div',\n {\n ...attrs,\n 'data-iris-error-boundary': '',\n role: 'alert',\n style: {\n padding: '16px',\n border: '1px solid var(--iris-danger)',\n borderRadius: 'var(--iris-radius-md, 6px)',\n color: 'var(--iris-danger)',\n ...((attrs.style as Record<string, string> | undefined) ?? {}),\n },\n },\n [\n h('div', { 'data-iris-error-boundary-message': '' }, message),\n h(\n 'button',\n {\n type: 'button',\n 'data-iris-error-boundary-retry': '',\n onClick: reset,\n },\n t('errorBoundary.retry'),\n ),\n ],\n )\n }\n\n return () => {\n if (error.value !== null) {\n const custom = slots.fallback ?? props.fallback\n if (custom) {\n return custom({ error: error.value, reset })\n }\n return renderDefaultFallback()\n }\n return slots.default?.()\n }\n },\n})\n"]}
1
+ {"version":3,"sources":["../src/i18n/context.ts","../src/i18n/useI18n.ts","../src/error-boundary/IrisErrorBoundary.ts"],"names":["createI18n","inject","ref","onBeforeUnmount","computed","defineComponent","getCurrentInstance","onErrorCaptured","h"],"mappings":"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","file":"error-boundary.cjs","sourcesContent":["import { type InjectionKey } from 'vue'\nimport type { I18n } from '@iris-ui-kit/core'\n\n/** Injection key carrying the active {@link I18n} instance to `useI18n`. */\nexport const I18nInjectionKey: InjectionKey<I18n> = Symbol('IrisI18n')\n","import { computed, inject, onBeforeUnmount, ref, type ComputedRef, type Ref } from 'vue'\nimport { createI18n, type I18n, type I18nState } from '@iris-ui-kit/core'\nimport { I18nInjectionKey } from './context'\n\nexport interface UseI18nReturn {\n locale: ComputedRef<string>\n t: I18n['t']\n formatDate: I18n['formatDate']\n formatNumber: I18n['formatNumber']\n formatRelativeTime: I18n['formatRelativeTime']\n setLocale: I18n['setLocale']\n setMessages: I18n['setMessages']\n}\n\n// Shared fallback so components can call `useI18n()` without an enclosing\n// provider — they get English defaults rather than throwing.\nlet fallbackI18n: I18n | null = null\nfunction getFallback(): I18n {\n if (fallbackI18n === null) fallbackI18n = createI18n()\n return fallbackI18n\n}\n\n/**\n * Reactive access to the active locale + translation/formatting helpers.\n * Works with or without an `<IrisI18nProvider>` (falls back to English\n * defaults). `t` / formatters read the live locale at call time, and `locale`\n * is a reactive ref so templates re-render on change.\n */\nexport function useI18n(): UseI18nReturn {\n const i18n = inject(I18nInjectionKey, null) ?? getFallback()\n const state = ref(i18n.getState()) as Ref<I18nState>\n const unsubscribe = i18n.subscribe((next) => {\n state.value = next\n })\n onBeforeUnmount(unsubscribe)\n\n return {\n locale: computed(() => state.value.locale),\n t: (key, params) => {\n void state.value.locale\n return i18n.t(key, params)\n },\n formatDate: (value, options) => {\n void state.value.locale\n return i18n.formatDate(value, options)\n },\n formatNumber: (value, options) => {\n void state.value.locale\n return i18n.formatNumber(value, options)\n },\n formatRelativeTime: (value, unit, options) => {\n void state.value.locale\n return i18n.formatRelativeTime(value, unit, options)\n },\n setLocale: i18n.setLocale,\n setMessages: i18n.setMessages,\n }\n}\n","import { defineComponent, getCurrentInstance, h, onErrorCaptured, ref, type PropType, type VNode } from 'vue'\nimport { useI18n } from '../i18n'\n\n/** Render-customization payload handed to the `#fallback` slot / `fallback` prop. */\nexport interface IrisErrorBoundaryFallbackProps {\n /** The error caught while rendering the guarded subtree. */\n error: unknown\n /** Clears the error and re-attempts rendering the children. */\n reset: () => void\n}\n\n/** Public prop surface of {@link IrisErrorBoundary}. */\nexport interface IrisErrorBoundaryProps {\n /**\n * Custom fallback renderer. Receives `{ error, reset }` and returns the\n * fallback UI. A `#fallback` scoped slot takes precedence when both are given.\n * When neither is supplied the themed DEFAULT fallback renders.\n */\n fallback?: (props: IrisErrorBoundaryFallbackProps) => VNode | VNode[] | string\n /** Invoked with the caught error (and Vue's error-info string) for logging. */\n onError?: (error: unknown, info?: string) => void\n}\n\n/**\n * IrisErrorBoundary: catches errors thrown while rendering its guarded subtree\n * (default slot), switches to a fallback, and exposes `reset()` to clear the\n * error and re-attempt rendering. Catching does NOT rethrow — a single broken\n * subtree no longer crashes the whole tree.\n *\n * Customize via a `#fallback` scoped slot or the `fallback` prop, each receiving\n * `{ error, reset }`. Without either, a themed, accessible (`role=\"alert\"`)\n * default fallback shows the error message + a retry button, i18n'd through\n * `useI18n()` (English defaults without a provider).\n */\nexport const IrisErrorBoundary = defineComponent({\n name: 'IrisErrorBoundary',\n inheritAttrs: false,\n props: {\n fallback: {\n type: Function as PropType<IrisErrorBoundaryProps['fallback']>,\n default: undefined,\n },\n onError: {\n type: Function as PropType<IrisErrorBoundaryProps['onError']>,\n default: undefined,\n },\n },\n setup(props, { attrs, slots }) {\n const { t } = useI18n()\n const error = ref<unknown>(null)\n // App-level telemetry handler (Sentry/…). Read live so a handler\n // configured after this component's setup is still picked up.\n const appContext = getCurrentInstance()!.appContext\n\n const reset = () => {\n error.value = null\n }\n\n onErrorCaptured((err, instance, info) => {\n error.value = err\n props.onError?.(err, info)\n // Vue's handleError skips its app.config.errorHandler branch when a hook\n // returns false, so forward explicitly with the exact args Vue would\n // have passed — (err, exposedInstance, errorInfo). Guarded so a throwing\n // handler is logged, not rethrown: a bare call would escape containment\n // from inside the hook and re-enter handleError with the handler's own\n // error (Vue's own path wraps the handler in callWithErrorHandling).\n try {\n appContext.config.errorHandler?.(err, instance, info)\n } catch (handlerError) {\n console.error(handlerError)\n }\n // Stop further propagation so the error does not crash the parent tree.\n return false\n })\n\n const renderDefaultFallback = (): VNode => {\n const message =\n error.value instanceof Error && error.value.message\n ? error.value.message\n : t('errorBoundary.message')\n\n return h(\n 'div',\n {\n ...attrs,\n 'data-iris-error-boundary': '',\n role: 'alert',\n style: {\n padding: '16px',\n border: '1px solid var(--iris-danger)',\n borderRadius: 'var(--iris-radius-md, 6px)',\n color: 'var(--iris-danger)',\n ...((attrs.style as Record<string, string> | undefined) ?? {}),\n },\n },\n [\n h('div', { 'data-iris-error-boundary-message': '' }, message),\n h(\n 'button',\n {\n type: 'button',\n 'data-iris-error-boundary-retry': '',\n onClick: reset,\n },\n t('errorBoundary.retry'),\n ),\n ],\n )\n }\n\n return () => {\n if (error.value !== null) {\n const custom = slots.fallback ?? props.fallback\n if (custom) {\n return custom({ error: error.value, reset })\n }\n return renderDefaultFallback()\n }\n return slots.default?.()\n }\n },\n})\n"]}
@@ -1,4 +1,4 @@
1
- export { IrisErrorBoundary } from './chunk-PPYNXNGZ.js';
1
+ export { IrisErrorBoundary } from './chunk-FSDQHBI2.js';
2
2
  import './chunk-TCAXRWT6.js';
3
3
  //# sourceMappingURL=error-boundary.js.map
4
4
  //# sourceMappingURL=error-boundary.js.map
package/dist/index.cjs CHANGED
@@ -589,9 +589,9 @@ function useResourceController(config) {
589
589
  function useDataSource(config) {
590
590
  const controller = core.createDataSource({ ...config, immediate: false });
591
591
  const immediate = config.immediate !== false;
592
- vue.onMounted(() => {
593
- if (immediate) void controller.load();
594
- });
592
+ if (immediate) {
593
+ vue.onMounted(() => void controller.load());
594
+ }
595
595
  vue.onScopeDispose(() => controller.destroy());
596
596
  const state = useStore(controller.store);
597
597
  return { ...controller, state };
@@ -3089,12 +3089,17 @@ function useUndoStack(options) {
3089
3089
  index: raw.index
3090
3090
  });
3091
3091
  const sync = () => {
3092
- state.value = {
3092
+ const next = {
3093
3093
  canUndo: raw.canUndo(),
3094
3094
  canRedo: raw.canRedo(),
3095
3095
  depth: raw.depth,
3096
3096
  index: raw.index
3097
3097
  };
3098
+ const cur = state.value;
3099
+ if (next.canUndo === cur.canUndo && next.canRedo === cur.canRedo && next.depth === cur.depth && next.index === cur.index) {
3100
+ return;
3101
+ }
3102
+ state.value = next;
3098
3103
  };
3099
3104
  const stack = trackUndoStack(raw, sync);
3100
3105
  const push = (snapshot) => stack.push(snapshot);
@@ -3232,12 +3237,18 @@ var IrisErrorBoundary = vue.defineComponent({
3232
3237
  setup(props, { attrs, slots }) {
3233
3238
  const { t } = useI18n();
3234
3239
  const error = vue.ref(null);
3240
+ const appContext = vue.getCurrentInstance().appContext;
3235
3241
  const reset = () => {
3236
3242
  error.value = null;
3237
3243
  };
3238
- vue.onErrorCaptured((err, _instance, info) => {
3244
+ vue.onErrorCaptured((err, instance, info) => {
3239
3245
  error.value = err;
3240
3246
  props.onError?.(err, info);
3247
+ try {
3248
+ appContext.config.errorHandler?.(err, instance, info);
3249
+ } catch (handlerError) {
3250
+ console.error(handlerError);
3251
+ }
3241
3252
  return false;
3242
3253
  });
3243
3254
  const renderDefaultFallback = () => {
@@ -6345,7 +6356,10 @@ var IrisList = vue.defineComponent({
6345
6356
  const { state, isContent, stateKey, stateProps } = useDataState(() => ({
6346
6357
  loading: props.loading,
6347
6358
  error: props.error,
6348
- empty: props.items.length === 0
6359
+ empty: props.items.length === 0,
6360
+ // Already-loaded items stay mounted during a revalidate (SWR): a load
6361
+ // in flight no longer replaces them with the loading/error state.
6362
+ hasContent: props.items.length > 0
6349
6363
  }));
6350
6364
  const isEnabled = (i) => !props.items[i]?.disabled;
6351
6365
  const nav = core.createKeyboardNav({
@@ -6482,7 +6496,9 @@ var IrisList = vue.defineComponent({
6482
6496
  role: "listbox",
6483
6497
  "aria-label": props.ariaLabel,
6484
6498
  "aria-multiselectable": props.multi ? "true" : void 0,
6485
- "aria-busy": state.value === "loading" ? "true" : void 0,
6499
+ // Decoupled from resolved state: signals both the initial load and
6500
+ // an in-flight background revalidate (content stays mounted).
6501
+ "aria-busy": props.loading ? "true" : void 0,
6486
6502
  "data-iris-list": "",
6487
6503
  onKeydown: onKeyDown,
6488
6504
  style: {
@@ -12059,7 +12075,10 @@ var IrisTree = vue.defineComponent({
12059
12075
  const { state, isContent, stateKey, stateProps } = useDataState(() => ({
12060
12076
  loading: props.loading,
12061
12077
  error: props.error,
12062
- empty: props.nodes.length === 0
12078
+ empty: props.nodes.length === 0,
12079
+ // Already-loaded nodes stay mounted during a revalidate (SWR): a load
12080
+ // in flight no longer replaces them with the loading/error state.
12081
+ hasContent: props.nodes.length > 0
12063
12082
  }));
12064
12083
  const lazyCache = vue.ref(/* @__PURE__ */ new Map());
12065
12084
  const loadingIds = vue.ref(/* @__PURE__ */ new Set());
@@ -12379,7 +12398,9 @@ var IrisTree = vue.defineComponent({
12379
12398
  role: "tree",
12380
12399
  "aria-label": props.ariaLabel ?? t("tree.label"),
12381
12400
  "aria-multiselectable": props.selectionMode === "multi" ? "true" : void 0,
12382
- "aria-busy": state.value === "loading" ? "true" : void 0,
12401
+ // Decoupled from resolved state: signals both the initial load and
12402
+ // an in-flight background revalidate (content stays mounted).
12403
+ "aria-busy": props.loading ? "true" : void 0,
12383
12404
  "data-iris-tree": "",
12384
12405
  style: {
12385
12406
  display: "flex",
@@ -18439,14 +18460,32 @@ var IrisDashboardTemplate = vue.defineComponent({
18439
18460
  activeId: { type: String, default: "" },
18440
18461
  /** Cards to render in the main grid. Skip to use the `default` slot only. */
18441
18462
  cards: { type: Array, default: () => [] },
18442
- defaultCollapsed: { type: Boolean, default: false }
18463
+ defaultCollapsed: { type: Boolean, default: false },
18464
+ /**
18465
+ * Controlled collapsed state. When unset (`undefined`), the component
18466
+ * manages its own state seeded from `defaultCollapsed`; when set, this
18467
+ * value takes precedence and changes are re-emitted via
18468
+ * `update:collapsed` (`v-model:collapsed`).
18469
+ */
18470
+ collapsed: { type: Boolean, default: void 0 }
18443
18471
  },
18444
18472
  emits: {
18445
18473
  "update:activeId": (_id) => true,
18446
18474
  "update:collapsed": (_value) => true
18447
18475
  },
18448
18476
  setup(props, { attrs, slots, emit }) {
18449
- const collapsed = vue.ref(props.defaultCollapsed);
18477
+ const isControlled = vue.computed(() => props.collapsed !== void 0);
18478
+ const internalCollapsed = vue.ref(props.defaultCollapsed);
18479
+ const collapsed = vue.computed(
18480
+ () => isControlled.value ? Boolean(props.collapsed) : internalCollapsed.value
18481
+ );
18482
+ vue.watch(
18483
+ () => props.collapsed,
18484
+ (value) => {
18485
+ if (value !== void 0) internalCollapsed.value = value;
18486
+ },
18487
+ { immediate: true }
18488
+ );
18450
18489
  const onSelectNav = (id) => {
18451
18490
  emit("update:activeId", id);
18452
18491
  };
@@ -18468,7 +18507,7 @@ var IrisDashboardTemplate = vue.defineComponent({
18468
18507
  {
18469
18508
  collapsed: collapsed.value,
18470
18509
  "onUpdate:collapsed": (v) => {
18471
- collapsed.value = v;
18510
+ if (!isControlled.value) internalCollapsed.value = v;
18472
18511
  emit("update:collapsed", v);
18473
18512
  },
18474
18513
  width: 240,
@@ -18655,6 +18694,72 @@ function handlePosition2(handle) {
18655
18694
  s.cursor = HANDLE_CURSORS2[handle];
18656
18695
  return s;
18657
18696
  }
18697
+ var ResizableHandle = vue.defineComponent({
18698
+ name: "ResizableHandle",
18699
+ props: {
18700
+ handle: { type: String, required: true },
18701
+ disabled: { type: Boolean, default: false },
18702
+ keepAspect: { type: Boolean, default: false },
18703
+ minWidth: { type: Number, default: 40 },
18704
+ minHeight: { type: Number, default: 40 },
18705
+ maxWidth: { type: Number, default: Infinity },
18706
+ maxHeight: { type: Number, default: Infinity },
18707
+ /** Stable computed ref from the parent; read only (never mutated). */
18708
+ sizeRef: { type: Object, required: true },
18709
+ /** Stable setup-level callbacks from the parent. */
18710
+ onUpdate: { type: Function, required: true },
18711
+ onResizeStart: {
18712
+ type: Function,
18713
+ default: void 0
18714
+ },
18715
+ onResizeEnd: { type: Function, default: void 0 }
18716
+ },
18717
+ setup(props) {
18718
+ const handleRef = vue.ref(null);
18719
+ let startSize = { width: 0, height: 0 };
18720
+ let aspect = 1;
18721
+ useDrag({
18722
+ handle: handleRef,
18723
+ disabled: vue.computed(() => props.disabled),
18724
+ onStart: () => {
18725
+ startSize = { ...props.sizeRef.value };
18726
+ aspect = startSize.width / Math.max(1, startSize.height);
18727
+ props.onResizeStart?.(startSize);
18728
+ },
18729
+ onDrag: ({ dx, dy }) => {
18730
+ const t = props.handle.includes("top");
18731
+ const b = props.handle.includes("bottom");
18732
+ const l = props.handle.includes("left");
18733
+ const r = props.handle.includes("right");
18734
+ let nextW = startSize.width;
18735
+ let nextH = startSize.height;
18736
+ if (r) nextW = startSize.width + dx;
18737
+ if (l) nextW = startSize.width - dx;
18738
+ if (b) nextH = startSize.height + dy;
18739
+ if (t) nextH = startSize.height - dy;
18740
+ if (props.keepAspect && (t || b) && (l || r)) {
18741
+ nextH = nextW / aspect;
18742
+ }
18743
+ nextW = Math.max(props.minWidth, Math.min(props.maxWidth, nextW));
18744
+ nextH = Math.max(props.minHeight, Math.min(props.maxHeight, nextH));
18745
+ props.onUpdate({ width: nextW, height: nextH });
18746
+ },
18747
+ onEnd: () => props.onResizeEnd?.()
18748
+ });
18749
+ return () => vue.h("div", {
18750
+ ref: (el) => {
18751
+ handleRef.value = el ?? null;
18752
+ },
18753
+ "data-iris-resizable-handle": props.handle,
18754
+ style: {
18755
+ ...handlePosition2(props.handle),
18756
+ touchAction: "none",
18757
+ background: "transparent",
18758
+ zIndex: "1"
18759
+ }
18760
+ });
18761
+ }
18762
+ });
18658
18763
  var IrisResizable = vue.defineComponent({
18659
18764
  name: "IrisResizable",
18660
18765
  inheritAttrs: false,
@@ -18687,53 +18792,8 @@ var IrisResizable = vue.defineComponent({
18687
18792
  if (!isControlled.value) internal.value = next;
18688
18793
  emit("update:size", next);
18689
18794
  };
18690
- const renderHandle = (handle) => {
18691
- const handleRef = vue.ref(null);
18692
- let startSize = { width: 0, height: 0 };
18693
- let aspect = 1;
18694
- useDrag({
18695
- handle: handleRef,
18696
- disabled: vue.computed(() => props.disabled),
18697
- onStart: () => {
18698
- startSize = { ...size.value };
18699
- aspect = startSize.width / Math.max(1, startSize.height);
18700
- emit("resizeStart", startSize);
18701
- },
18702
- onDrag: ({ dx, dy }) => {
18703
- const t = handle.includes("top");
18704
- const b = handle.includes("bottom");
18705
- const l = handle.includes("left");
18706
- const r = handle.includes("right");
18707
- let nextW = startSize.width;
18708
- let nextH = startSize.height;
18709
- if (r) nextW = startSize.width + dx;
18710
- if (l) nextW = startSize.width - dx;
18711
- if (b) nextH = startSize.height + dy;
18712
- if (t) nextH = startSize.height - dy;
18713
- if (props.keepAspect && (t || b) && (l || r)) {
18714
- nextH = nextW / aspect;
18715
- }
18716
- nextW = Math.max(props.minWidth, Math.min(props.maxWidth, nextW));
18717
- nextH = Math.max(props.minHeight, Math.min(props.maxHeight, nextH));
18718
- setSize({ width: nextW, height: nextH });
18719
- },
18720
- onEnd: () => {
18721
- emit("resizeEnd", size.value);
18722
- }
18723
- });
18724
- return vue.h("div", {
18725
- ref: (el) => {
18726
- handleRef.value = el ?? null;
18727
- },
18728
- "data-iris-resizable-handle": handle,
18729
- style: {
18730
- ...handlePosition2(handle),
18731
- touchAction: "none",
18732
- background: "transparent",
18733
- zIndex: "1"
18734
- }
18735
- });
18736
- };
18795
+ const emitResizeStart = (s) => emit("resizeStart", s);
18796
+ const emitResizeEnd = () => emit("resizeEnd", size.value);
18737
18797
  return () => vue.h(
18738
18798
  "div",
18739
18799
  {
@@ -18748,7 +18808,25 @@ var IrisResizable = vue.defineComponent({
18748
18808
  ...attrs.style ?? {}
18749
18809
  }
18750
18810
  },
18751
- [slots.default?.(), ...props.handles.map(renderHandle)]
18811
+ [
18812
+ slots.default?.(),
18813
+ ...props.handles.map(
18814
+ (handle) => vue.h(ResizableHandle, {
18815
+ key: handle,
18816
+ handle,
18817
+ disabled: props.disabled,
18818
+ keepAspect: props.keepAspect,
18819
+ minWidth: props.minWidth,
18820
+ minHeight: props.minHeight,
18821
+ maxWidth: props.maxWidth,
18822
+ maxHeight: props.maxHeight,
18823
+ sizeRef: size,
18824
+ onUpdate: setSize,
18825
+ onResizeStart: emitResizeStart,
18826
+ onResizeEnd: emitResizeEnd
18827
+ })
18828
+ )
18829
+ ]
18752
18830
  );
18753
18831
  }
18754
18832
  });