@iris-ui-kit/vue 0.2.28 → 0.2.29

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 (64) hide show
  1. package/dist/admin.cjs +3 -3
  2. package/dist/admin.cjs.map +1 -1
  3. package/dist/admin.js +3 -3
  4. package/dist/behaviors.d.cts +2 -2
  5. package/dist/behaviors.d.ts +2 -2
  6. package/dist/{chunk-IAG64CHL.js → chunk-BVKOOTXG.js} +3 -3
  7. package/dist/chunk-BVKOOTXG.js.map +1 -0
  8. package/dist/{chunk-AMNHDYFY.js → chunk-EZUDM4TM.js} +5 -5
  9. package/dist/chunk-EZUDM4TM.js.map +1 -0
  10. package/dist/{chunk-UPC4GN3F.js → chunk-KAOPZNZF.js} +3 -3
  11. package/dist/chunk-KAOPZNZF.js.map +1 -0
  12. package/dist/{chunk-MO7AAXA6.js → chunk-KSJZLFAT.js} +6 -6
  13. package/dist/chunk-KSJZLFAT.js.map +1 -0
  14. package/dist/{chunk-XCNCIG2I.js → chunk-LQ2YAOYR.js} +11 -10
  15. package/dist/chunk-LQ2YAOYR.js.map +1 -0
  16. package/dist/{chunk-ZTPIXL7Q.js → chunk-OH4YSGFK.js} +3 -3
  17. package/dist/chunk-OH4YSGFK.js.map +1 -0
  18. package/dist/{chunk-TCAXRWT6.js → chunk-W3JMJVEK.js} +3 -3
  19. package/dist/chunk-W3JMJVEK.js.map +1 -0
  20. package/dist/{chunk-LWABTQCB.js → chunk-X5UOY4IV.js} +13 -9
  21. package/dist/chunk-X5UOY4IV.js.map +1 -0
  22. package/dist/{chunk-3547J6IN.js → chunk-X7Y35UUL.js} +3 -3
  23. package/dist/{chunk-3547J6IN.js.map → chunk-X7Y35UUL.js.map} +1 -1
  24. package/dist/error-boundary.cjs +1 -1
  25. package/dist/error-boundary.cjs.map +1 -1
  26. package/dist/error-boundary.js +2 -2
  27. package/dist/form.cjs +1 -1
  28. package/dist/form.cjs.map +1 -1
  29. package/dist/form.js +1 -1
  30. package/dist/i18n.cjs +1 -1
  31. package/dist/i18n.cjs.map +1 -1
  32. package/dist/i18n.js +1 -1
  33. package/dist/index.cjs +3456 -861
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +723 -283
  36. package/dist/index.d.ts +723 -283
  37. package/dist/index.js +3432 -846
  38. package/dist/index.js.map +1 -1
  39. package/dist/modal-utils.cjs +9 -8
  40. package/dist/modal-utils.cjs.map +1 -1
  41. package/dist/modal-utils.js +1 -1
  42. package/dist/provider.cjs +4 -4
  43. package/dist/provider.cjs.map +1 -1
  44. package/dist/provider.js +4 -4
  45. package/dist/skeletons.cjs +11 -7
  46. package/dist/skeletons.cjs.map +1 -1
  47. package/dist/skeletons.d.cts +1 -1
  48. package/dist/skeletons.d.ts +1 -1
  49. package/dist/skeletons.js +2 -2
  50. package/dist/skins.cjs +1 -1
  51. package/dist/skins.cjs.map +1 -1
  52. package/dist/skins.js +1 -1
  53. package/dist/theme.cjs +1 -1
  54. package/dist/theme.cjs.map +1 -1
  55. package/dist/theme.js +1 -1
  56. package/package.json +11 -11
  57. package/dist/chunk-AMNHDYFY.js.map +0 -1
  58. package/dist/chunk-IAG64CHL.js.map +0 -1
  59. package/dist/chunk-LWABTQCB.js.map +0 -1
  60. package/dist/chunk-MO7AAXA6.js.map +0 -1
  61. package/dist/chunk-TCAXRWT6.js.map +0 -1
  62. package/dist/chunk-UPC4GN3F.js.map +0 -1
  63. package/dist/chunk-XCNCIG2I.js.map +0 -1
  64. package/dist/chunk-ZTPIXL7Q.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  export { createUndoStack, useUndoStack } from './chunk-I7PR4ILF.js';
2
2
  import './chunk-ROJV6RON.js';
3
- import { useBodyScrollLock, useFocusTrap } from './chunk-XCNCIG2I.js';
4
- export { __getBodyScrollLockCount, __resetBodyScrollLock, useBodyScrollLock, useFocusTrap } from './chunk-XCNCIG2I.js';
3
+ import { useBodyScrollLock, useFocusTrap } from './chunk-LQ2YAOYR.js';
4
+ export { __getBodyScrollLockCount, __resetBodyScrollLock, useBodyScrollLock, useFocusTrap } from './chunk-LQ2YAOYR.js';
5
5
  export { IrisGrid } from './chunk-IN3DQ55U.js';
6
6
  import './chunk-4FQ7XF52.js';
7
7
  import { useDataState } from './chunk-S43IHHBU.js';
8
8
  export { DATA_STATE_CLASS, __resetDataStateStyles, installDataStateStyles, useDataState, usePrefersReducedMotion } from './chunk-S43IHHBU.js';
9
- export { IrisProvider, PluginStoreContextKey, usePlugin, usePluginStore } from './chunk-MO7AAXA6.js';
9
+ export { IrisProvider, PluginStoreContextKey, usePlugin, usePluginStore } from './chunk-KSJZLFAT.js';
10
10
  export { createClientFetcher, createResourceController, useResourceController } from './chunk-25NYWSXP.js';
11
- import { IrisCheckbox } from './chunk-LWABTQCB.js';
12
- export { IrisAlert, IrisButton, IrisCheckbox, IrisDashboardTemplate, IrisDivider, IrisFormField, IrisInput, IrisLoginTemplate, IrisPasswordInput } from './chunk-LWABTQCB.js';
11
+ import { IrisCheckbox, IrisFormField, IrisInput, IrisButton } from './chunk-X5UOY4IV.js';
12
+ export { IrisAlert, IrisButton, IrisCheckbox, IrisDashboardTemplate, IrisDivider, IrisFormField, IrisInput, IrisLoginTemplate, IrisPasswordInput } from './chunk-X5UOY4IV.js';
13
13
  export { IrisContainer, IrisDashboardCard, IrisDashboardGrid, IrisStack } from './chunk-3R3QDLJC.js';
14
- export { IrisSkinKey, SkinProvider, SkinResolutionError, applySkin, builtinSkins, createSkinCatalog, createSkinEngine, createSkinRegistry, darkSkin, lightSkin, loadSkin, localStorageSkinStorage, memorySkinStorage, renderSkinStyle, resolveSkin, skinBootScript, skinError, skinToCssEntries, useSkin, useSkinContext, validateSkin } from './chunk-UPC4GN3F.js';
15
- export { DropdownContextKey, IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisBreadcrumb, IrisBreadcrumbItem, IrisDropdown, IrisDropdownItem, IrisDropdownMenu, IrisDropdownSeparator, IrisDropdownTrigger, IrisIcon, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-AMNHDYFY.js';
14
+ export { IrisSkinKey, SkinProvider, SkinResolutionError, applySkin, builtinSkins, createSkinCatalog, createSkinEngine, createSkinRegistry, darkSkin, lightSkin, loadSkin, localStorageSkinStorage, memorySkinStorage, renderSkinStyle, resolveSkin, skinBootScript, skinError, skinToCssEntries, useSkin, useSkinContext, validateSkin } from './chunk-KAOPZNZF.js';
15
+ export { DropdownContextKey, IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisBreadcrumb, IrisBreadcrumbItem, IrisDropdown, IrisDropdownItem, IrisDropdownMenu, IrisDropdownSeparator, IrisDropdownTrigger, IrisIcon, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-EZUDM4TM.js';
16
16
  import { useFloating, useDismiss } from './chunk-IPR5JAVF.js';
17
17
  export { useDismiss, useFloating } from './chunk-IPR5JAVF.js';
18
18
  import { useMachine } from './chunk-NNOF7KUH.js';
@@ -20,21 +20,21 @@ export { useMachine } from './chunk-NNOF7KUH.js';
20
20
  import { findFirstElement, mergeSlotProps, composeRefs } from './chunk-F77KIPOH.js';
21
21
  export { IrisSlot, composeRefs, findFirstElement, mergeSlotProps } from './chunk-F77KIPOH.js';
22
22
  export { IrisHeaderLayout, IrisSidebarLayout } from './chunk-3V46HGRC.js';
23
- export { IrisThemeKey, ThemeProvider, useColorScheme, useDirection, useTheme, useThemeContext } from './chunk-ZTPIXL7Q.js';
23
+ export { IrisThemeKey, ThemeProvider, useColorScheme, useDirection, useTheme, useThemeContext } from './chunk-OH4YSGFK.js';
24
24
  export { useAsyncResource, usePaginatedResource } from './chunk-SJB5DIIY.js';
25
25
  import { useDrag } from './chunk-IOMKC5OI.js';
26
26
  export { IrisClickOutside, IrisCompose, IrisHotkey, IrisLongPress, IrisMovable, IrisResizable, useDrag } from './chunk-IOMKC5OI.js';
27
27
  export { IrisSortable, SORTABLE_ITEM_ATTR } from './chunk-W3B6E5GN.js';
28
28
  export { createClientDataSource, createDataSource, createGroupedView, useDataSource, useGroupedView } from './chunk-QBFYIDY7.js';
29
- export { IrisErrorBoundary } from './chunk-3547J6IN.js';
30
- export { FormInjectionKey, IrisForm, useField, useFieldArray, useForm, useFormContext } from './chunk-IAG64CHL.js';
29
+ export { IrisErrorBoundary } from './chunk-X7Y35UUL.js';
30
+ export { FormInjectionKey, IrisForm, useField, useFieldArray, useForm, useFormContext } from './chunk-BVKOOTXG.js';
31
31
  import { useStore } from './chunk-75GSYEJS.js';
32
32
  export { useStore, useStoreSelector } from './chunk-75GSYEJS.js';
33
- import { useI18n } from './chunk-TCAXRWT6.js';
34
- export { I18nInjectionKey, IrisI18nProvider, useI18n } from './chunk-TCAXRWT6.js';
33
+ import { useI18n } from './chunk-W3JMJVEK.js';
34
+ export { I18nInjectionKey, IrisI18nProvider, useI18n } from './chunk-W3JMJVEK.js';
35
35
  import { defineComponent, computed, watch, ref, useId, provide, inject, h, nextTick, Teleport, onScopeDispose, Fragment, onMounted, watchEffect, onBeforeUnmount, onBeforeMount, shallowRef, toValue, Comment, onUnmounted } from 'vue';
36
- import { createFloatingMachine, createHoverIntent, createVirtualizer, createKeyboardNav, getPageRange, createSelectionModel, flattenLeafColumns, buildHeaderMatrix, createExpansion, flattenTree, withSortedChildren, createTreeSelection, createCellRange, computeVirtualRange, aggregate, createStore, firstEnabledIndex, createCalendarNav, clampDate, startOfMonth, isSameMonth, buildMonthMatrix, getWeekdayNames, formatMonthYear, startOfDay, endOfMonth, isSameDay, isOutOfRange, formatLocalISO, hexToRgba, rgbaToHsva, hsvaToRgba, rgbToHex, addMonths, compareValues, nextGridCell, nextEnabledIndex, copyText, clamp01, validateEditRulesAsync, createAutoDismiss, createReconnectingSource, createResilientFetcher, createDisposableScope, toCsv, downloadFile, toSpreadsheetXml, generateId } from '@iris-ui-kit/core';
37
- export { addDays, addMonths, buildMonthMatrix, clamp01, clampDate, composeEventHandlers, createFloatingMachine, createMachine, createStore, endOfMonth, formatLocalISO, formatMonthYear, generateId, getPageRange, getWeekdayNames, hexToRgba, hsvToRgb, hsvaToRgba, isOutOfRange, isSameDay, isSameMonth, mergeProps, rgbToHex, rgbToHsv, rgbaToHsva, startOfDay, startOfMonth } from '@iris-ui-kit/core';
36
+ import { createFloatingMachine, createHoverIntent, createVirtualizer, createKeyboardNav, getPageRange, createSelectionModel, flattenLeafColumns, buildHeaderMatrix, detectColumnType, seedFormValues, createExpansion, flattenTree, withSortedChildren, mergeFormFilters, createTreeSelection, createCellRange, createSortable, compareStates, createStore, firstEnabledIndex, createCalendarNav, clampDate, startOfMonth, isSameMonth, buildMonthMatrix, getWeekdayNames, formatMonthYear, startOfDay, endOfMonth, isSameDay, isOutOfRange, formatLocalISO, hexToRgba, rgbaToHsva, hsvaToRgba, rgbToHex, addMonths, RESPONSIVE_NARROW_WIDTH, computeResponsiveColumns, createRemoteTableSource, readTableViews, computeVirtualRange, toCsvRows, removeRowsFromList, tableDisplayText, aggregate, previewColumnsFromRows, nextEnabledIndex, copyText, clamp01, rowsFromCsv, compareValues, TABLE_VIEWS_SAVE_ITEM, uniqueTableTabs, leftPinnedCount, pinnedCountFromBudget, toCsv, createAutoDismiss, validateEditRulesAsync, writeClipboardText, serializeTableRange, nextGridCell, createReconnectingSource, createResilientFetcher, createDisposableScope, downloadFile, toSpreadsheetXml, generateId, buildFormValues, applyTableMask, writeTableViews } from '@iris-ui-kit/core';
37
+ export { addDays, addMonths, buildMonthMatrix, clamp01, clampDate, composeEventHandlers, createFloatingMachine, createMachine, createStore, endOfMonth, formatLocalISO, formatMonthYear, generateId, getPageRange, getWeekdayNames, hexToRgba, hsvToRgb, hsvaToRgba, isOutOfRange, isSameDay, isSameMonth, mergeProps, parseCsv, rgbToHex, rgbToHsv, rgbaToHsva, startOfDay, startOfMonth } from '@iris-ui-kit/core';
38
38
  import { getDirection } from '@iris-ui-kit/theme';
39
39
  export { applyTheme, createThemeStore, getCssVar, toCssVarName } from '@iris-ui-kit/theme';
40
40
  export { ALL_TOKEN_NAMES, COLOR_TOKENS, RADII_TOKENS, SPACING_TOKENS, darkTheme, lightTheme } from '@iris-ui-kit/tokens';
@@ -77,7 +77,7 @@ function useDisposableScope() {
77
77
  }
78
78
 
79
79
  // src/primitives/popover/context.ts
80
- var PopoverContextKey = /* @__PURE__ */ Symbol("IrisPopover");
80
+ var PopoverContextKey = Symbol("IrisPopover");
81
81
 
82
82
  // src/primitives/popover/Popover.ts
83
83
  var IrisPopover = defineComponent({
@@ -198,42 +198,18 @@ var ANIM_DIALOG = "var(--iris-anim-dialog)";
198
198
  var ANIM_POPOVER = "var(--iris-anim-popover)";
199
199
  var ANIM_TOAST = "var(--iris-anim-toast)";
200
200
  var ANIM_TOOLTIP = "var(--iris-anim-tooltip)";
201
- function installFloatingAnimations() {
202
- if (installed || typeof document === "undefined") return;
203
- installed = true;
204
- if (document.getElementById(STYLE_ID)) return;
205
- const style = document.createElement("style");
206
- style.id = STYLE_ID;
207
- style.textContent = `
201
+ var ANIMATION_CSS = `
208
202
  :root {
209
203
  --iris-anim-dialog: iris-dialog-in 150ms ease-out;
210
204
  --iris-anim-popover: iris-popover-in 150ms ease-out;
211
205
  --iris-anim-toast: iris-toast-in 200ms ease-out;
212
206
  --iris-anim-tooltip: iris-tooltip-in 120ms ease-out;
213
207
  }
214
- /*
215
- * NOTE: these keyframes animate OPACITY ONLY. The floating layers are
216
- * positioned via inline transform: translate3d(...) (Floating UI), and any
217
- * transform in the keyframes would override it for the animation duration \u2014
218
- * flashing the panel at the viewport origin (0,0) before it jumps into place.
219
- * Fade-only keeps the entrance motion without touching positioning.
220
- */
221
- @keyframes iris-dialog-in {
222
- from { opacity: 0; }
223
- to { opacity: 1; }
224
- }
225
- @keyframes iris-popover-in {
226
- from { opacity: 0; }
227
- to { opacity: 1; }
228
- }
229
- @keyframes iris-toast-in {
230
- from { opacity: 0; }
231
- to { opacity: 1; }
232
- }
233
- @keyframes iris-tooltip-in {
234
- from { opacity: 0; }
235
- to { opacity: 1; }
236
- }
208
+ /* Fade-only keyframes preserve Floating UI's inline translate3d position. */
209
+ @keyframes iris-dialog-in { from { opacity: 0; } to { opacity: 1; } }
210
+ @keyframes iris-popover-in { from { opacity: 0; } to { opacity: 1; } }
211
+ @keyframes iris-toast-in { from { opacity: 0; } to { opacity: 1; } }
212
+ @keyframes iris-tooltip-in { from { opacity: 0; } to { opacity: 1; } }
237
213
  @media (prefers-reduced-motion: reduce) {
238
214
  :root {
239
215
  --iris-anim-dialog: none;
@@ -242,7 +218,6 @@ function installFloatingAnimations() {
242
218
  --iris-anim-tooltip: none;
243
219
  }
244
220
  }
245
- /* P4: global focus-visible ring for interactive controls */
246
221
  [data-iris-tabs-trigger]:focus-visible,
247
222
  [data-iris-switch]:focus-visible,
248
223
  [data-iris-checkbox]:focus-visible,
@@ -254,6 +229,13 @@ function installFloatingAnimations() {
254
229
  outline-offset: 2px;
255
230
  }
256
231
  `;
232
+ function installFloatingAnimations() {
233
+ if (installed || typeof document === "undefined") return;
234
+ installed = true;
235
+ if (document.getElementById(STYLE_ID)) return;
236
+ const style = document.createElement("style");
237
+ style.id = STYLE_ID;
238
+ style.textContent = ANIMATION_CSS;
257
239
  document.head.appendChild(style);
258
240
  }
259
241
 
@@ -426,12 +408,12 @@ var IrisTooltip = defineComponent({
426
408
  const triggerListeners = {
427
409
  onPointerenter: handleEnter,
428
410
  onPointerleave: handleLeave,
429
- onFocus: (() => {
411
+ onFocus: () => {
430
412
  if (!props.disabled) hi.open();
431
- }),
432
- onBlur: (() => {
413
+ },
414
+ onBlur: () => {
433
415
  if (!props.disabled) hi.close();
434
- })
416
+ }
435
417
  };
436
418
  const captureTriggerRef = (el) => {
437
419
  triggerRef.value = el ?? null;
@@ -500,7 +482,7 @@ var IrisTooltip = defineComponent({
500
482
  });
501
483
 
502
484
  // src/primitives/dialog/context.ts
503
- var DialogContextKey = /* @__PURE__ */ Symbol("IrisDialog");
485
+ var DialogContextKey = Symbol("IrisDialog");
504
486
 
505
487
  // src/primitives/dialog/Dialog.ts
506
488
  var IrisDialog = defineComponent({
@@ -797,7 +779,7 @@ var IrisDialogClose = defineComponent({
797
779
  });
798
780
 
799
781
  // src/primitives/drawer/context.ts
800
- var DrawerContextKey = /* @__PURE__ */ Symbol("IrisDrawer");
782
+ var DrawerContextKey = Symbol("IrisDrawer");
801
783
 
802
784
  // src/primitives/drawer/Drawer.ts
803
785
  var IrisDrawer = defineComponent({
@@ -2354,7 +2336,7 @@ var IrisSwitch = defineComponent({
2354
2336
  );
2355
2337
  }
2356
2338
  });
2357
- var RadioGroupKey = /* @__PURE__ */ Symbol("IrisRadioGroup");
2339
+ var RadioGroupKey = Symbol("IrisRadioGroup");
2358
2340
  var IrisRadioGroup = defineComponent({
2359
2341
  name: "IrisRadioGroup",
2360
2342
  props: {
@@ -2728,6 +2710,152 @@ var IrisList = defineComponent({
2728
2710
  );
2729
2711
  }
2730
2712
  });
2713
+ function renderVirtualSelectListbox({
2714
+ items,
2715
+ translate,
2716
+ listboxRef,
2717
+ onListKeyDown,
2718
+ virtualizer,
2719
+ state,
2720
+ activeIndex,
2721
+ multiple,
2722
+ selectedValues,
2723
+ currentValue,
2724
+ onSelect,
2725
+ rowHeight
2726
+ }) {
2727
+ const optionStyle = (selected, active, disabled) => ({
2728
+ display: "flex",
2729
+ alignItems: "center",
2730
+ gap: "var(--iris-gap-sm)",
2731
+ padding: "var(--iris-padding-sm, 6px) var(--iris-padding-md)",
2732
+ borderRadius: "var(--iris-radius-sm)",
2733
+ cursor: disabled ? "not-allowed" : "pointer",
2734
+ opacity: disabled ? "0.5" : "1",
2735
+ fontSize: "var(--iris-font-size-md, 14px)",
2736
+ background: selected ? "var(--iris-surface-selected, rgba(99, 102, 241, 0.12))" : active ? "var(--iris-surface-hover)" : "transparent",
2737
+ color: "var(--iris-foreground)",
2738
+ fontWeight: selected ? "600" : "400",
2739
+ outline: "none"
2740
+ });
2741
+ const check = () => h(
2742
+ "svg",
2743
+ {
2744
+ "aria-hidden": "true",
2745
+ width: "14",
2746
+ height: "14",
2747
+ viewBox: "0 0 24 24",
2748
+ fill: "none",
2749
+ stroke: "var(--iris-primary)",
2750
+ "stroke-width": "2.5",
2751
+ "stroke-linecap": "round",
2752
+ "stroke-linejoin": "round"
2753
+ },
2754
+ [h("path", { d: "M20 6 9 17l-5-5" })]
2755
+ );
2756
+ return h(
2757
+ "ul",
2758
+ {
2759
+ role: "listbox",
2760
+ "aria-label": translate("select.options"),
2761
+ "data-iris-list": "",
2762
+ ref: (el) => {
2763
+ listboxRef.value = el ?? null;
2764
+ },
2765
+ onKeydown: onListKeyDown,
2766
+ onScroll: (event) => {
2767
+ virtualizer?.setScroll(event.currentTarget.scrollTop);
2768
+ },
2769
+ style: {
2770
+ listStyle: "none",
2771
+ margin: "0",
2772
+ padding: "var(--iris-padding-sm)",
2773
+ display: "flex",
2774
+ flexDirection: "column",
2775
+ outline: "none",
2776
+ maxHeight: "240px",
2777
+ overflowY: "auto"
2778
+ }
2779
+ },
2780
+ items.length === 0 ? [
2781
+ h(
2782
+ "li",
2783
+ {
2784
+ role: "presentation",
2785
+ "data-iris-list-state": "empty",
2786
+ "aria-live": "polite",
2787
+ style: {
2788
+ listStyle: "none",
2789
+ padding: "12px",
2790
+ textAlign: "center",
2791
+ color: "var(--iris-muted)",
2792
+ fontSize: "var(--iris-font-size-md, 14px)"
2793
+ }
2794
+ },
2795
+ translate("list.empty")
2796
+ )
2797
+ ] : [
2798
+ h("li", {
2799
+ role: "presentation",
2800
+ "aria-hidden": "true",
2801
+ "data-iris-select-spacer": "",
2802
+ "data-iris-select-spacer-type": "top",
2803
+ style: { height: `${state.value.offsetBefore}px` }
2804
+ }),
2805
+ ...state.value.items.map((item) => {
2806
+ const option = items[item.index];
2807
+ if (!option) return null;
2808
+ const active = item.index === activeIndex.value;
2809
+ const selected = multiple ? selectedValues.value.includes(option.value) : option.value === currentValue.value;
2810
+ return h(
2811
+ "li",
2812
+ {
2813
+ key: String(option.value ?? item.index),
2814
+ role: "option",
2815
+ tabindex: active ? 0 : -1,
2816
+ "aria-selected": selected ? "true" : "false",
2817
+ "aria-disabled": option.disabled ? "true" : void 0,
2818
+ "aria-setsize": items.length,
2819
+ "aria-posinset": item.index + 1,
2820
+ "data-iris-list-index": item.index,
2821
+ "data-iris-list-item": "",
2822
+ "data-state": selected ? "selected" : active ? "active" : "idle",
2823
+ onClick: option.disabled ? void 0 : () => {
2824
+ activeIndex.value = item.index;
2825
+ onSelect(option);
2826
+ },
2827
+ onFocus: () => {
2828
+ activeIndex.value = item.index;
2829
+ },
2830
+ onMouseenter: () => {
2831
+ activeIndex.value = item.index;
2832
+ },
2833
+ style: optionStyle(selected, active, !!option.disabled)
2834
+ },
2835
+ [
2836
+ h(
2837
+ "span",
2838
+ { style: { flex: "1", minWidth: "0" } },
2839
+ option.label ?? String(option.value)
2840
+ ),
2841
+ selected ? check() : null
2842
+ ]
2843
+ );
2844
+ }),
2845
+ h("li", {
2846
+ role: "presentation",
2847
+ "aria-hidden": "true",
2848
+ "data-iris-select-spacer": "",
2849
+ "data-iris-select-spacer-type": "bottom",
2850
+ style: {
2851
+ height: `${state.value.totalSize - state.value.offsetBefore - state.value.items.length * rowHeight}px`
2852
+ }
2853
+ })
2854
+ ]
2855
+ );
2856
+ }
2857
+
2858
+ // src/primitives/select/Select.ts
2731
2859
  var IrisSelect = defineComponent({
2732
2860
  name: "IrisSelect",
2733
2861
  inheritAttrs: false,
@@ -2965,138 +3093,6 @@ var IrisSelect = defineComponent({
2965
3093
  })
2966
3094
  ]
2967
3095
  );
2968
- const renderVirtualListbox = () => {
2969
- const list = props.items;
2970
- const optionStyle = (selected, active, disabled) => ({
2971
- display: "flex",
2972
- alignItems: "center",
2973
- gap: "var(--iris-gap-sm)",
2974
- padding: "var(--iris-padding-sm, 6px) var(--iris-padding-md)",
2975
- borderRadius: "var(--iris-radius-sm)",
2976
- cursor: disabled ? "not-allowed" : "pointer",
2977
- opacity: disabled ? "0.5" : "1",
2978
- fontSize: "var(--iris-font-size-md, 14px)",
2979
- background: selected ? "var(--iris-surface-selected, rgba(99, 102, 241, 0.12))" : active ? "var(--iris-surface-hover)" : "transparent",
2980
- color: "var(--iris-foreground)",
2981
- fontWeight: selected ? "600" : "400",
2982
- outline: "none"
2983
- });
2984
- const check = () => h(
2985
- "svg",
2986
- {
2987
- "aria-hidden": "true",
2988
- width: "14",
2989
- height: "14",
2990
- viewBox: "0 0 24 24",
2991
- fill: "none",
2992
- stroke: "var(--iris-primary)",
2993
- "stroke-width": "2.5",
2994
- "stroke-linecap": "round",
2995
- "stroke-linejoin": "round"
2996
- },
2997
- [h("path", { d: "M20 6 9 17l-5-5" })]
2998
- );
2999
- return h(
3000
- "ul",
3001
- {
3002
- role: "listbox",
3003
- "aria-label": t("select.options"),
3004
- "data-iris-list": "",
3005
- ref: (el) => {
3006
- listboxRef.value = el ?? null;
3007
- },
3008
- onKeydown: onListKeyDown,
3009
- onScroll: (e) => {
3010
- v?.setScroll(e.currentTarget.scrollTop);
3011
- },
3012
- style: {
3013
- listStyle: "none",
3014
- margin: "0",
3015
- padding: "var(--iris-padding-sm)",
3016
- display: "flex",
3017
- flexDirection: "column",
3018
- outline: "none",
3019
- maxHeight: "240px",
3020
- overflowY: "auto"
3021
- }
3022
- },
3023
- list.length === 0 ? [
3024
- h(
3025
- "li",
3026
- {
3027
- role: "presentation",
3028
- "data-iris-list-state": "empty",
3029
- "aria-live": "polite",
3030
- style: {
3031
- listStyle: "none",
3032
- padding: "12px",
3033
- textAlign: "center",
3034
- color: "var(--iris-muted)",
3035
- fontSize: "var(--iris-font-size-md, 14px)"
3036
- }
3037
- },
3038
- t("list.empty")
3039
- )
3040
- ] : [
3041
- h("li", {
3042
- role: "presentation",
3043
- "aria-hidden": "true",
3044
- "data-iris-select-spacer": "",
3045
- "data-iris-select-spacer-type": "top",
3046
- style: { height: `${vstate.value.offsetBefore}px` }
3047
- }),
3048
- ...vstate.value.items.map((item) => {
3049
- const opt = list[item.index];
3050
- if (!opt) return null;
3051
- const active = item.index === activeIndex.value;
3052
- const selected = props.multiple ? selectedValues.value.includes(opt.value) : opt.value === currentValue.value;
3053
- return h(
3054
- "li",
3055
- {
3056
- key: String(opt.value ?? item.index),
3057
- role: "option",
3058
- tabindex: active ? 0 : -1,
3059
- "aria-selected": selected ? "true" : "false",
3060
- "aria-disabled": opt.disabled ? "true" : void 0,
3061
- "aria-setsize": list.length,
3062
- "aria-posinset": item.index + 1,
3063
- "data-iris-list-index": item.index,
3064
- "data-iris-list-item": "",
3065
- "data-state": selected ? "selected" : active ? "active" : "idle",
3066
- onClick: opt.disabled ? void 0 : () => {
3067
- activeIndex.value = item.index;
3068
- onSelect(opt);
3069
- },
3070
- onFocus: () => {
3071
- activeIndex.value = item.index;
3072
- },
3073
- onMouseenter: () => {
3074
- activeIndex.value = item.index;
3075
- },
3076
- style: optionStyle(selected, active, !!opt.disabled)
3077
- },
3078
- [
3079
- h(
3080
- "span",
3081
- { style: { flex: "1", minWidth: "0" } },
3082
- opt.label ?? String(opt.value)
3083
- ),
3084
- selected ? check() : null
3085
- ]
3086
- );
3087
- }),
3088
- h("li", {
3089
- role: "presentation",
3090
- "aria-hidden": "true",
3091
- "data-iris-select-spacer": "",
3092
- "data-iris-select-spacer-type": "bottom",
3093
- style: {
3094
- height: `${vstate.value.totalSize - vstate.value.offsetBefore - vstate.value.items.length * ROW_HEIGHT3}px`
3095
- }
3096
- })
3097
- ]
3098
- );
3099
- };
3100
3096
  return () => h(
3101
3097
  IrisPopover,
3102
3098
  {
@@ -3143,7 +3139,20 @@ var IrisSelect = defineComponent({
3143
3139
  style: { padding: "var(--iris-padding-sm)", minWidth: "180px" },
3144
3140
  ...props.teleport !== void 0 ? { teleport: props.teleport } : {}
3145
3141
  },
3146
- () => props.virtual ? renderVirtualListbox() : h(IrisList, {
3142
+ () => props.virtual ? renderVirtualSelectListbox({
3143
+ items: props.items,
3144
+ translate: t,
3145
+ listboxRef,
3146
+ onListKeyDown,
3147
+ virtualizer: v,
3148
+ state: vstate,
3149
+ activeIndex,
3150
+ multiple: props.multiple,
3151
+ selectedValues,
3152
+ currentValue,
3153
+ onSelect,
3154
+ rowHeight: ROW_HEIGHT3
3155
+ }) : h(IrisList, {
3147
3156
  items: props.items,
3148
3157
  modelValue: currentValue.value,
3149
3158
  onSelect,
@@ -3530,8 +3539,8 @@ var IrisSkeleton = defineComponent({
3530
3539
  onMounted(installSkeletonStyles);
3531
3540
  const style = computed(() => {
3532
3541
  const w = props.width !== void 0 ? toCss(props.width) : defaultWidth(props.shape);
3533
- const h89 = props.height !== void 0 ? toCss(props.height) : defaultHeight(props.shape, props.width);
3534
- return { width: w, height: h89 };
3542
+ const h101 = props.height !== void 0 ? toCss(props.height) : defaultHeight(props.shape, props.width);
3543
+ return { width: w, height: h101 };
3535
3544
  });
3536
3545
  return () => h("div", {
3537
3546
  ...attrs,
@@ -4451,7 +4460,7 @@ var IrisChip = defineComponent({
4451
4460
  });
4452
4461
 
4453
4462
  // src/primitives/accordion/context.ts
4454
- var AccordionContextKey = /* @__PURE__ */ Symbol("IrisAccordion");
4463
+ var AccordionContextKey = Symbol("IrisAccordion");
4455
4464
 
4456
4465
  // src/primitives/accordion/Accordion.ts
4457
4466
  var IrisAccordion = defineComponent({
@@ -4689,7 +4698,7 @@ var IrisAccordionItem = defineComponent({
4689
4698
  });
4690
4699
 
4691
4700
  // src/primitives/stepper/context.ts
4692
- var StepperContextKey = /* @__PURE__ */ Symbol("IrisStepper");
4701
+ var StepperContextKey = Symbol("IrisStepper");
4693
4702
 
4694
4703
  // src/primitives/stepper/Stepper.ts
4695
4704
  var IrisStepper = defineComponent({
@@ -4937,7 +4946,7 @@ var IrisStepperStep = defineComponent({
4937
4946
  });
4938
4947
 
4939
4948
  // src/primitives/toggle-group/context.ts
4940
- var ToggleGroupContextKey = /* @__PURE__ */ Symbol("IrisToggleGroup");
4949
+ var ToggleGroupContextKey = Symbol("IrisToggleGroup");
4941
4950
  var SIZE_PADDING = {
4942
4951
  sm: "4px 10px",
4943
4952
  md: "6px 14px",
@@ -5867,12 +5876,250 @@ var tableControlProps = {
5867
5876
  default: void 0
5868
5877
  }
5869
5878
  };
5879
+
5880
+ // src/primitives/table/props.ts
5881
+ var tableProps = {
5882
+ columns: {
5883
+ type: Array,
5884
+ required: true
5885
+ },
5886
+ data: {
5887
+ type: Array,
5888
+ required: false,
5889
+ default: void 0
5890
+ },
5891
+ rowKey: { type: String, default: "id" },
5892
+ ...tableControlProps,
5893
+ /** Multi-column sort (vxe sort-config.multiple parity). */
5894
+ multiSort: { type: Boolean, default: false },
5895
+ multiSortState: {
5896
+ type: Array,
5897
+ default: void 0
5898
+ },
5899
+ defaultMultiSort: {
5900
+ type: Array,
5901
+ default: void 0
5902
+ },
5903
+ striped: { type: Boolean, default: false },
5904
+ editConfig: {
5905
+ type: Object,
5906
+ default: void 0
5907
+ },
5908
+ bordered: { type: Boolean, default: true },
5909
+ /** Row-density preset; invalid runtime values fail closed to comfortable. */
5910
+ density: {
5911
+ type: String,
5912
+ default: "comfortable"
5913
+ },
5914
+ /** Show the local density cycle button in the table toolbar. */
5915
+ densityToggle: { type: Boolean, default: false },
5916
+ /** Show a live formatter preview while an editor is open. */
5917
+ editPreview: { type: Boolean, default: false },
5918
+ /** Highlight committed cells matching the active inline draft. */
5919
+ pattern: { type: Boolean, default: false },
5920
+ /** Alias for pattern feedback. */
5921
+ patternFill: { type: Boolean, default: false },
5922
+ /** Named snapshots available in the table toolbar. */
5923
+ views: { type: Object, default: void 0 },
5924
+ /** Controlled active named view notification. */
5925
+ onActiveViewChange: {
5926
+ type: Function,
5927
+ default: void 0
5928
+ },
5929
+ /** Optional tab strip; tab clicks apply listed view names in order. */
5930
+ tableTabs: { type: Array, default: void 0 },
5931
+ /** Show a draggable boundary for the leading left-pinned columns. */
5932
+ pinnedDrag: { type: Boolean, default: false },
5933
+ /** Called for each column whose pin side changes. */
5934
+ onColumnPinnedChange: {
5935
+ type: Function,
5936
+ default: void 0
5937
+ },
5938
+ /** Called once after a pinned-boundary commit. */
5939
+ onPinnedCountChange: { type: Function, default: void 0 },
5940
+ /** Infer leaf-column value kinds from the first non-empty data arrival and
5941
+ * fill only missing alignment defaults. Disabled by default. */
5942
+ autoDetectTypes: { type: Boolean, default: false },
5943
+ /** Below 480px, greedily hide the lowest-priority top-level columns until
5944
+ * the natural width fits; pinned columns survive. */
5945
+ responsive: { type: Boolean, default: false },
5946
+ /** Extra bare row sets appended as named CSV segments by the imperative handle. */
5947
+ exportNames: {
5948
+ type: Array,
5949
+ default: void 0
5950
+ },
5951
+ resizableColumns: { type: Boolean, default: false },
5952
+ columnWidths: {
5953
+ type: Object,
5954
+ default: void 0
5955
+ },
5956
+ virtualScroll: {
5957
+ type: Object,
5958
+ default: void 0
5959
+ },
5960
+ loading: { type: Boolean, default: false },
5961
+ error: { type: Boolean, default: false },
5962
+ /** Print-friendly mode: marks the root so toolbar/form chrome is hidden by print CSS. */
5963
+ printable: { type: Boolean, default: false },
5964
+ /** Show a confirmation preview before the toolbar CSV import callback. */
5965
+ importPreview: { type: Boolean, default: false },
5966
+ onRetry: { type: Function, default: void 0 },
5967
+ columnVirtualization: { type: Boolean, default: false },
5968
+ keyboardNavigation: { type: Boolean, default: false },
5969
+ cellRange: { type: Boolean, default: false },
5970
+ /** Range clipboard copy; copyWithFormat uses column formatter output. */
5971
+ clipConfig: { type: Object, default: void 0 },
5972
+ renderDetail: {
5973
+ type: Function,
5974
+ default: void 0
5975
+ },
5976
+ rowExpandable: {
5977
+ type: Function,
5978
+ default: void 0
5979
+ },
5980
+ defaultExpandedRowKeys: {
5981
+ type: Array,
5982
+ default: void 0
5983
+ },
5984
+ getSubRows: {
5985
+ type: Function,
5986
+ default: void 0
5987
+ },
5988
+ treeSelectionCascade: { type: Boolean, default: false },
5989
+ proxyConfig: {
5990
+ type: Object,
5991
+ default: void 0
5992
+ },
5993
+ formConfig: {
5994
+ type: Object,
5995
+ default: void 0
5996
+ },
5997
+ toolbar: {
5998
+ type: Object,
5999
+ default: void 0
6000
+ },
6001
+ columnVisibility: {
6002
+ type: Object,
6003
+ default: void 0
6004
+ },
6005
+ filters: {
6006
+ type: Object,
6007
+ default: void 0
6008
+ },
6009
+ filterValues: {
6010
+ type: Object,
6011
+ default: void 0
6012
+ },
6013
+ onFilterValuesChange: {
6014
+ type: Function,
6015
+ default: void 0
6016
+ },
6017
+ contextMenu: {
6018
+ type: Object,
6019
+ default: void 0
6020
+ },
6021
+ lazyLoad: {
6022
+ type: Function,
6023
+ default: void 0
6024
+ },
6025
+ seq: { type: Boolean, default: false },
6026
+ seqStartIndex: { type: Number, default: 1 },
6027
+ spanMethod: {
6028
+ type: Function,
6029
+ default: void 0
6030
+ },
6031
+ columnDrag: {
6032
+ type: Object,
6033
+ default: void 0
6034
+ },
6035
+ rowDrag: {
6036
+ type: Object,
6037
+ default: void 0
6038
+ },
6039
+ onDataChange: {
6040
+ type: Function,
6041
+ default: void 0
6042
+ }
6043
+ };
6044
+ var SELECTION_COL_WIDTH = 40;
6045
+ var EXPAND_COL_WIDTH = 40;
6046
+ var SEQ_COL_WIDTH = 40;
6047
+ var DRAG_COL_WIDTH = 40;
6048
+ var DEFAULT_COL_WIDTH = 140;
6049
+ var DEFAULT_MIN_WIDTH = 60;
6050
+ var RESIZE_STEP = 16;
5870
6051
  function getCellValue(row, column) {
5871
6052
  const key = column.dataIndex ?? column.key;
5872
6053
  return row[key];
5873
6054
  }
6055
+ function resolveSpan(occupied, rowIndex, columnIndex, method) {
6056
+ if (!method) return { rowspan: 1, colspan: 1 };
6057
+ const key = `${rowIndex}:${columnIndex}`;
6058
+ if (occupied.has(key)) return null;
6059
+ const span = method({ rowIndex, columnIndex });
6060
+ const rowspan = span?.rowspan ?? 1;
6061
+ const colspan = span?.colspan ?? 1;
6062
+ if (rowspan > 1) {
6063
+ for (let row = 1; row < rowspan; row += 1) occupied.add(`${rowIndex + row}:${columnIndex}`);
6064
+ }
6065
+ if (colspan > 1) {
6066
+ for (let column = 1; column < colspan; column += 1) {
6067
+ occupied.add(`${rowIndex}:${columnIndex + column}`);
6068
+ }
6069
+ }
6070
+ return { rowspan, colspan };
6071
+ }
6072
+ function resolveInitialWidth(col) {
6073
+ if (typeof col.width === "number") return col.width;
6074
+ if (typeof col.width === "string") {
6075
+ const match = col.width.match(/^(\d+(?:\.\d+)?)px$/);
6076
+ if (match) return Number(match[1]);
6077
+ }
6078
+ return DEFAULT_COL_WIDTH;
6079
+ }
6080
+ function computeVisibleColSet(enabled, columns, scrollLeft, viewportWidth, widths) {
6081
+ if (!enabled) return null;
6082
+ const range = computeVirtualRange({
6083
+ itemCount: columns.length,
6084
+ scrollTop: scrollLeft,
6085
+ viewportSize: viewportWidth,
6086
+ itemSize: (index) => widths[columns[index].key] ?? resolveInitialWidth(columns[index]),
6087
+ buffer: 2
6088
+ });
6089
+ const visible = /* @__PURE__ */ new Set();
6090
+ for (let index = range.startIndex; index <= range.endIndex; index += 1) visible.add(index);
6091
+ columns.forEach((column, index) => {
6092
+ if (column.pinned) visible.add(index);
6093
+ });
6094
+ return visible;
6095
+ }
6096
+ function cellId(rowIdent, columnKey) {
6097
+ return `${rowIdent}::${columnKey}`;
6098
+ }
5874
6099
 
5875
6100
  // src/primitives/table/useTableSort.ts
6101
+ function buildSorter(col) {
6102
+ if (col.sorter) return col.sorter;
6103
+ return (a, b) => compareValues(getCellValue(a, col), getCellValue(b, col));
6104
+ }
6105
+ function buildMultiSortComparator(leafColumns, state) {
6106
+ if (state.length === 0) return null;
6107
+ const colMap = new Map(leafColumns.map((c) => [c.key, c]));
6108
+ const chain = [];
6109
+ for (const s of state) {
6110
+ const col = colMap.get(s.key);
6111
+ if (!col) continue;
6112
+ chain.push({ dir: s.direction === "asc" ? 1 : -1, sorter: buildSorter(col) });
6113
+ }
6114
+ if (chain.length === 0) return null;
6115
+ return (a, b) => {
6116
+ for (const step of chain) {
6117
+ const cmp = step.sorter(a, b);
6118
+ if (cmp !== 0) return cmp * step.dir;
6119
+ }
6120
+ return 0;
6121
+ };
6122
+ }
5876
6123
  function useTableSort(data, options) {
5877
6124
  const sortProp = computed(() => options.sort === void 0 ? void 0 : toValue(options.sort));
5878
6125
  const internalSortValue = ref(options.defaultSort ?? null);
@@ -5883,19 +6130,33 @@ function useTableSort(data, options) {
5883
6130
  options.onSortChange?.(val);
5884
6131
  }
5885
6132
  });
6133
+ const multiEnabled = computed(() => toValue(options.multiSort) === true);
6134
+ const multiSortProp = computed(
6135
+ () => options.multiSortState === void 0 ? void 0 : toValue(options.multiSortState)
6136
+ );
6137
+ const internalMultiSort = ref(options.defaultMultiSort ?? []);
6138
+ const multiSortState = computed({
6139
+ get: () => multiSortProp.value === void 0 ? internalMultiSort.value : multiSortProp.value,
6140
+ set: (val) => {
6141
+ if (multiSortProp.value === void 0) internalMultiSort.value = val;
6142
+ options.onMultiSortChange?.(val);
6143
+ }
6144
+ });
5886
6145
  const sortComparator = computed(() => {
5887
6146
  const s = sortState.value;
5888
6147
  if (!s) return null;
5889
6148
  const col = toValue(options.leafColumns).find((c) => c.key === s.key);
5890
6149
  if (!col) return null;
5891
6150
  const dir = s.direction === "asc" ? 1 : -1;
5892
- const sorter = col.sorter ?? ((a, b) => compareValues(getCellValue(a, col), getCellValue(b, col)));
5893
- return (a, b) => sorter(a, b) * dir;
6151
+ return (a, b) => buildSorter(col)(a, b) * dir;
5894
6152
  });
6153
+ const multiSortComparator = computed(
6154
+ () => buildMultiSortComparator(toValue(options.leafColumns), multiSortState.value)
6155
+ );
5895
6156
  const sortedData = computed(() => {
5896
- const compare = sortComparator.value;
5897
- if (!compare) return data.value;
5898
- return [...data.value].sort(compare);
6157
+ const comparator = multiEnabled.value ? multiSortComparator.value : sortComparator.value;
6158
+ if (!comparator) return data.value;
6159
+ return [...data.value].sort(comparator);
5899
6160
  });
5900
6161
  function setSort(next) {
5901
6162
  if (sortProp.value === void 0) internalSortValue.value = next;
@@ -5914,170 +6175,1989 @@ function useTableSort(data, options) {
5914
6175
  }
5915
6176
  setSort(null);
5916
6177
  }
6178
+ function setMultiSort(next) {
6179
+ if (multiSortProp.value === void 0) internalMultiSort.value = next;
6180
+ options.onMultiSortChange?.(next);
6181
+ }
6182
+ function cycleMultiSort(col) {
6183
+ if (!col.sortable) return;
6184
+ const idx = multiSortState.value.findIndex((s) => s.key === col.key);
6185
+ if (idx < 0) {
6186
+ setMultiSort([...multiSortState.value, { key: col.key, direction: "asc" }]);
6187
+ return;
6188
+ }
6189
+ const next = [...multiSortState.value];
6190
+ if (next[idx].direction === "asc") {
6191
+ next[idx] = { key: col.key, direction: "desc" };
6192
+ setMultiSort(next);
6193
+ return;
6194
+ }
6195
+ next.splice(idx, 1);
6196
+ setMultiSort(next);
6197
+ }
5917
6198
  return {
5918
6199
  sortState,
5919
6200
  cycleSort,
5920
6201
  setSort,
5921
6202
  sortComparator,
5922
- sortedData
6203
+ sortedData,
6204
+ multiSortState,
6205
+ cycleMultiSort,
6206
+ setMultiSort,
6207
+ multiSortComparator
5923
6208
  };
5924
6209
  }
5925
-
5926
- // src/primitives/table/Table.ts
5927
- function getCellValue2(row, column) {
5928
- const key = column.dataIndex ?? column.key;
5929
- return row[key];
6210
+ function mergeFilterValues(filters, filterValues) {
6211
+ const next = { ...filters };
6212
+ for (const [key, values] of Object.entries(filterValues)) {
6213
+ if (values.length > 0) next[key] = values.join(",");
6214
+ }
6215
+ return next;
5930
6216
  }
5931
- var SELECTION_COL_WIDTH = 40;
5932
- var EXPAND_COL_WIDTH = 40;
5933
- var DEFAULT_COL_WIDTH = 140;
5934
- var DEFAULT_MIN_WIDTH = 60;
5935
- var RESIZE_STEP = 16;
5936
- function resolveInitialWidth(col) {
5937
- if (typeof col.width === "number") return col.width;
5938
- if (typeof col.width === "string") {
5939
- const m = col.width.match(/^(\d+(?:\.\d+)?)px$/);
5940
- if (m) return Number(m[1]);
6217
+ function useTableProxy(options) {
6218
+ const cfg = computed(() => toValue(options.proxyConfig));
6219
+ const queryRef = ref(void 0);
6220
+ watch(
6221
+ () => cfg.value?.query,
6222
+ (query) => {
6223
+ queryRef.value = query;
6224
+ },
6225
+ { immediate: true }
6226
+ );
6227
+ const proxy = shallowRef(null);
6228
+ const state = shallowRef({
6229
+ data: [],
6230
+ total: 0,
6231
+ loading: false,
6232
+ error: null,
6233
+ params: { page: 1, pageSize: 10, sort: null, filters: {} }
6234
+ });
6235
+ let unsubscribe = null;
6236
+ let mounted = false;
6237
+ const attach = (ctrl) => {
6238
+ unsubscribe?.();
6239
+ proxy.value = ctrl;
6240
+ state.value = ctrl.getState();
6241
+ unsubscribe = ctrl.subscribe((snapshot) => {
6242
+ state.value = snapshot;
6243
+ });
6244
+ };
6245
+ const detach = () => {
6246
+ unsubscribe?.();
6247
+ unsubscribe = null;
6248
+ proxy.value?.destroy();
6249
+ proxy.value = null;
6250
+ state.value = {
6251
+ data: [],
6252
+ total: 0,
6253
+ loading: false,
6254
+ error: null,
6255
+ params: { page: 1, pageSize: 10, sort: null, filters: {} }
6256
+ };
6257
+ };
6258
+ watch(
6259
+ () => cfg.value === void 0,
6260
+ (absent) => {
6261
+ if (absent) {
6262
+ detach();
6263
+ return;
6264
+ }
6265
+ if (proxy.value) return;
6266
+ const config = cfg.value;
6267
+ const remoteSort = toValue(options.remoteSort) === true;
6268
+ const remoteFilter = toValue(options.remoteFilter) === true;
6269
+ const multiSort = toValue(options.multiSort) === true;
6270
+ const ctrl = createRemoteTableSource({
6271
+ query: (params) => queryRef.value(params),
6272
+ autoLoad: false,
6273
+ initialParams: {
6274
+ page: config.defaultPage ?? 1,
6275
+ pageSize: config.pageSize ?? 10,
6276
+ sort: remoteSort ? toValue(options.sort) ?? options.defaultSort ?? null : null,
6277
+ sorts: remoteSort && multiSort ? toValue(options.multiSortState) ?? options.defaultMultiSort ?? [] : void 0,
6278
+ filters: remoteFilter ? mergeFilterValues(toValue(options.filters) ?? {}, toValue(options.filterValues) ?? {}) : {}
6279
+ }
6280
+ });
6281
+ attach(ctrl);
6282
+ if (mounted && config.autoLoad !== false) void ctrl.request();
6283
+ },
6284
+ { immediate: true }
6285
+ );
6286
+ onScopeDispose(detach);
6287
+ onMounted(() => {
6288
+ mounted = true;
6289
+ const config = cfg.value;
6290
+ if (config && proxy.value && config.autoLoad !== false) void proxy.value.request();
6291
+ });
6292
+ const liveData = shallowRef([]);
6293
+ watch(
6294
+ state,
6295
+ (snapshot) => {
6296
+ liveData.value = snapshot.data;
6297
+ },
6298
+ { immediate: true }
6299
+ );
6300
+ return {
6301
+ proxy: computed(() => proxy.value),
6302
+ state: computed(() => state.value),
6303
+ liveData,
6304
+ setParams: (partial) => proxy.value ? proxy.value.setParams(partial) : false,
6305
+ refetch: () => proxy.value ? proxy.value.refetch() : Promise.resolve()
6306
+ };
6307
+ }
6308
+ function exportCsv(rows, columns) {
6309
+ return toCsv(
6310
+ rows,
6311
+ columns.map((column) => ({
6312
+ key: column.key,
6313
+ title: column.title,
6314
+ dataIndex: typeof column.dataIndex === "string" ? column.dataIndex : void 0
6315
+ }))
6316
+ );
6317
+ }
6318
+ async function downloadCsv(filename, csv) {
6319
+ const BOM = String.fromCharCode(65279);
6320
+ await downloadFile({
6321
+ filename,
6322
+ content: BOM + csv,
6323
+ mimeType: "text/csv;charset=utf-8;"
6324
+ });
6325
+ }
6326
+ function renderFormSection(ctx) {
6327
+ const fc = ctx.formConfig;
6328
+ if (!fc) return null;
6329
+ return h(
6330
+ "form",
6331
+ {
6332
+ "data-iris-table-form": "",
6333
+ onSubmit: (e) => {
6334
+ e.preventDefault();
6335
+ ctx.handleFormSubmit();
6336
+ },
6337
+ onReset: (e) => {
6338
+ e.preventDefault();
6339
+ ctx.handleFormReset();
6340
+ },
6341
+ style: {
6342
+ display: "flex",
6343
+ flexWrap: "wrap",
6344
+ alignItems: "flex-end",
6345
+ gap: "var(--iris-space-sm, 12px)",
6346
+ padding: "var(--iris-space-sm, 12px)",
6347
+ border: "1px solid var(--iris-border)",
6348
+ borderBottom: "none",
6349
+ background: "var(--iris-surface)",
6350
+ fontSize: "var(--iris-font-size-sm, 13px)"
6351
+ }
6352
+ },
6353
+ [
6354
+ ...fc.fields.map(
6355
+ (field) => h(
6356
+ "div",
6357
+ {
6358
+ key: field.key,
6359
+ "data-iris-table-form-field": field.key,
6360
+ style: { minWidth: 180 }
6361
+ },
6362
+ [
6363
+ h(
6364
+ IrisFormField,
6365
+ { label: field.label, size: "sm" },
6366
+ {
6367
+ default: () => field.type === "select" ? h(IrisSelect, {
6368
+ items: (field.options ?? []).map((o) => ({
6369
+ value: o.value,
6370
+ label: o.label
6371
+ })),
6372
+ modelValue: ctx.formDraft.value[field.key] ?? "",
6373
+ placeholder: field.placeholder ?? ctx.t("select.placeholder"),
6374
+ size: "sm",
6375
+ "onUpdate:modelValue": (v) => ctx.setFormValue(field.key, String(v ?? ""))
6376
+ }) : h(IrisInput, {
6377
+ modelValue: ctx.formDraft.value[field.key] ?? "",
6378
+ placeholder: field.placeholder,
6379
+ size: "sm",
6380
+ "onUpdate:modelValue": (v) => ctx.setFormValue(field.key, String(v ?? ""))
6381
+ })
6382
+ }
6383
+ )
6384
+ ]
6385
+ )
6386
+ ),
6387
+ h("div", { style: { display: "flex", gap: "var(--iris-space-xs, 8px)" } }, [
6388
+ h(
6389
+ IrisButton,
6390
+ { type: "submit", size: "sm", "data-iris-table-form-submit": "" },
6391
+ { default: () => fc.submitText ?? ctx.t("table.formSubmit") }
6392
+ ),
6393
+ h(
6394
+ IrisButton,
6395
+ {
6396
+ type: "reset",
6397
+ variant: "outline",
6398
+ size: "sm",
6399
+ "data-iris-table-form-reset": ""
6400
+ },
6401
+ { default: () => fc.resetText ?? ctx.t("table.formReset") }
6402
+ )
6403
+ ])
6404
+ ]
6405
+ );
6406
+ }
6407
+ var toolbarBtnStyle = {
6408
+ border: "none",
6409
+ background: "transparent",
6410
+ cursor: "pointer",
6411
+ color: "var(--iris-muted)",
6412
+ fontSize: "var(--iris-font-size-md, 14px)"
6413
+ };
6414
+ function renderToolbarSection(ctx) {
6415
+ const tb = ctx.toolbar;
6416
+ if (!tb && !ctx.densityToggle) return null;
6417
+ const toolChildren = [];
6418
+ if (tb?.title) {
6419
+ toolChildren.push(
6420
+ h("span", { style: { fontWeight: 600, color: "var(--iris-foreground)" } }, tb.title)
6421
+ );
6422
+ }
6423
+ toolChildren.push(h("div", { style: { flex: "1" } }));
6424
+ if (tb?.onRefresh) {
6425
+ toolChildren.push(
6426
+ h(
6427
+ "button",
6428
+ {
6429
+ type: "button",
6430
+ "data-iris-table-toolbar-refresh": "",
6431
+ "aria-label": ctx.t("table.refresh"),
6432
+ title: ctx.t("table.refresh"),
6433
+ onClick: () => {
6434
+ tb.onRefresh?.();
6435
+ void ctx.proxyCtrl.refetch();
6436
+ },
6437
+ style: toolbarBtnStyle
6438
+ },
6439
+ "\u21BB"
6440
+ )
6441
+ );
6442
+ }
6443
+ if (tb?.onExport) {
6444
+ toolChildren.push(
6445
+ h(
6446
+ "button",
6447
+ {
6448
+ type: "button",
6449
+ "data-iris-table-toolbar-export": "",
6450
+ "aria-label": ctx.t("table.export"),
6451
+ title: ctx.t("table.export"),
6452
+ onClick: () => tb?.onExport?.(),
6453
+ style: toolbarBtnStyle
6454
+ },
6455
+ "\u21E9"
6456
+ )
6457
+ );
6458
+ }
6459
+ if (tb?.onImport) {
6460
+ toolChildren.push(
6461
+ h("input", {
6462
+ ref: (el) => {
6463
+ ctx.importFileInput.value = el ?? null;
6464
+ },
6465
+ type: "file",
6466
+ accept: ".csv,text/csv",
6467
+ "data-iris-table-import-input": "",
6468
+ onChange: ctx.onImportFile,
6469
+ style: { display: "none" }
6470
+ }),
6471
+ h(
6472
+ "button",
6473
+ {
6474
+ type: "button",
6475
+ "data-iris-table-toolbar-import": "",
6476
+ "aria-label": ctx.t("table.import"),
6477
+ title: ctx.t("table.import"),
6478
+ onClick: () => ctx.importFileInput.value?.click(),
6479
+ style: toolbarBtnStyle
6480
+ },
6481
+ "\u21EA"
6482
+ )
6483
+ );
6484
+ }
6485
+ if (tb?.batch && ctx.selectable === "multi" && ctx.displaySelection.value.length > 0) {
6486
+ toolChildren.push(
6487
+ h(
6488
+ "button",
6489
+ {
6490
+ type: "button",
6491
+ "data-iris-table-toolbar-batch": "",
6492
+ "aria-label": tb.batch.label,
6493
+ title: tb.batch.label,
6494
+ onClick: () => tb.batch.onClick([...ctx.displaySelection.value]),
6495
+ style: {
6496
+ border: "none",
6497
+ cursor: "pointer",
6498
+ background: "var(--iris-primary)",
6499
+ color: "var(--iris-primary-foreground)",
6500
+ fontSize: "var(--iris-font-size-md, 14px)",
6501
+ display: "inline-flex",
6502
+ alignItems: "center",
6503
+ gap: "var(--iris-space-xxs, 4px)",
6504
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
6505
+ borderRadius: "var(--iris-radius-sm, 4px)"
6506
+ }
6507
+ },
6508
+ [
6509
+ tb.batch.icon ? h(
6510
+ "span",
6511
+ { "aria-hidden": "true", style: { fontSize: "var(--iris-font-size-sm, 13px)" } },
6512
+ tb.batch.icon
6513
+ ) : null,
6514
+ tb.batch.label
6515
+ ]
6516
+ )
6517
+ );
6518
+ }
6519
+ if (tb?.buttons && tb.buttons.length > 0) {
6520
+ for (const btn of tb.buttons) {
6521
+ toolChildren.push(
6522
+ h(
6523
+ "button",
6524
+ {
6525
+ key: btn.key,
6526
+ type: "button",
6527
+ "data-iris-table-toolbar-button": btn.key,
6528
+ [`data-iris-table-toolbar-button-${btn.key}`]: "",
6529
+ "aria-label": btn.label,
6530
+ title: btn.label,
6531
+ onClick: btn.onClick,
6532
+ style: {
6533
+ ...toolbarBtnStyle,
6534
+ color: "var(--iris-foreground)",
6535
+ display: "inline-flex",
6536
+ alignItems: "center",
6537
+ gap: "var(--iris-space-xxs, 4px)",
6538
+ padding: "0 var(--iris-space-xxs, 4px)"
6539
+ }
6540
+ },
6541
+ [
6542
+ btn.icon ? h(
6543
+ "span",
6544
+ { "aria-hidden": "true", style: { fontSize: "var(--iris-font-size-sm, 13px)" } },
6545
+ btn.icon
6546
+ ) : null,
6547
+ btn.label
6548
+ ]
6549
+ )
6550
+ );
6551
+ }
6552
+ }
6553
+ if (ctx.densityToggle) {
6554
+ toolChildren.push(
6555
+ h(
6556
+ "button",
6557
+ {
6558
+ type: "button",
6559
+ "data-iris-density-toggle": "",
6560
+ "data-iris-density": ctx.effectiveDensity,
6561
+ "aria-label": `${ctx.t("table.density")}: ${ctx.t(`table.density.${ctx.effectiveDensity}`)}`,
6562
+ title: `${ctx.t("table.density")}: ${ctx.t(`table.density.${ctx.effectiveDensity}`)}`,
6563
+ onClick: ctx.onDensityToggle,
6564
+ style: toolbarBtnStyle
6565
+ },
6566
+ ctx.t(`table.density.${ctx.effectiveDensity}`)
6567
+ )
6568
+ );
6569
+ }
6570
+ return h(
6571
+ "div",
6572
+ {
6573
+ "data-iris-table-toolbar": "",
6574
+ style: {
6575
+ display: "flex",
6576
+ alignItems: "center",
6577
+ gap: "var(--iris-space-sm, 12px)",
6578
+ padding: "var(--iris-space-xs, 8px) var(--iris-space-sm, 12px)",
6579
+ border: "1px solid var(--iris-border)",
6580
+ borderBottom: "none",
6581
+ borderTopLeftRadius: "var(--iris-radius-md, 6px)",
6582
+ borderTopRightRadius: "var(--iris-radius-md, 6px)",
6583
+ background: "var(--iris-surface)",
6584
+ fontSize: "var(--iris-font-size-sm, 13px)"
6585
+ }
6586
+ },
6587
+ toolChildren
6588
+ );
6589
+ }
6590
+ function renderPagerSection(ctx) {
6591
+ if (!ctx.proxyCtrl.proxy.value) return null;
6592
+ const st = ctx.proxyCtrl.state.value;
6593
+ return h(
6594
+ "div",
6595
+ {
6596
+ "data-iris-table-pager": "",
6597
+ style: {
6598
+ display: "flex",
6599
+ justifyContent: "flex-end",
6600
+ alignItems: "center",
6601
+ padding: "var(--iris-space-xs, 8px) var(--iris-space-sm, 12px)",
6602
+ borderTop: "1px solid var(--iris-border)",
6603
+ background: "var(--iris-surface)"
6604
+ }
6605
+ },
6606
+ [
6607
+ h(IrisPagination, {
6608
+ modelValue: st.params.page,
6609
+ total: st.total,
6610
+ pageSize: st.params.pageSize,
6611
+ size: "sm",
6612
+ "onUpdate:modelValue": (page) => {
6613
+ ctx.proxyCtrl.setParams({ page });
6614
+ ctx.proxyConfig?.onPageChange?.(page, ctx.proxyCtrl.state.value.params.pageSize);
6615
+ }
6616
+ })
6617
+ ]
6618
+ );
6619
+ }
6620
+ function renderContextMenuSection(ctx) {
6621
+ const st = ctx.state.value;
6622
+ if (!st || !st.open) return null;
6623
+ const node = h(
6624
+ "div",
6625
+ {
6626
+ ref: (el) => {
6627
+ ctx.menuRef.value = el ?? null;
6628
+ },
6629
+ role: "menu",
6630
+ "data-iris-table-context-menu": "",
6631
+ style: {
6632
+ ...ctx.styles.value,
6633
+ zIndex: "var(--iris-z-popover, 1000)",
6634
+ background: "var(--iris-surface-floating, var(--iris-surface))",
6635
+ color: "var(--iris-foreground)",
6636
+ border: "1px solid var(--iris-border)",
6637
+ borderRadius: "var(--iris-radius-md, 6px)",
6638
+ boxShadow: "var(--iris-shadow-lg)",
6639
+ padding: "var(--iris-space-xxs, 4px)",
6640
+ minWidth: "160px",
6641
+ display: "flex",
6642
+ flexDirection: "column"
6643
+ }
6644
+ },
6645
+ st.items.map(
6646
+ (item) => h(
6647
+ "button",
6648
+ {
6649
+ key: item.key,
6650
+ type: "button",
6651
+ role: "menuitem",
6652
+ "data-iris-table-context-menu-item": item.key,
6653
+ disabled: item.disabled,
6654
+ "aria-disabled": item.disabled ? "true" : void 0,
6655
+ onClick: () => {
6656
+ ctx.contextMenu.onSelect(item.key, st.params);
6657
+ ctx.close();
6658
+ },
6659
+ style: {
6660
+ border: "none",
6661
+ background: "transparent",
6662
+ cursor: item.disabled ? "default" : "pointer",
6663
+ color: item.disabled ? "var(--iris-muted)" : "var(--iris-foreground)",
6664
+ font: "inherit",
6665
+ textAlign: "start",
6666
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
6667
+ borderRadius: "var(--iris-radius-sm, 4px)"
6668
+ }
6669
+ },
6670
+ item.label
6671
+ )
6672
+ )
6673
+ );
6674
+ return h(Teleport, { to: "body" }, [node]);
6675
+ }
6676
+ function renderFilterPanelSection(ctx) {
6677
+ const st = ctx.state.value;
6678
+ if (!st || !st.open) return null;
6679
+ const col = ctx.columns.find((c) => c.key === st.colKey);
6680
+ if (!col || !col.filterable) return null;
6681
+ const options = col.filterOptions ?? [];
6682
+ const node = h(
6683
+ "div",
6684
+ {
6685
+ ref: (el) => {
6686
+ ctx.panelRef.value = el ?? null;
6687
+ },
6688
+ role: "dialog",
6689
+ "aria-label": ctx.t("table.filter"),
6690
+ "data-iris-table-filter-panel": "",
6691
+ "data-iris-table-filter-column": st.colKey,
6692
+ style: {
6693
+ ...ctx.styles.value,
6694
+ zIndex: "var(--iris-z-popover, 1000)",
6695
+ background: "var(--iris-surface-floating, var(--iris-surface))",
6696
+ color: "var(--iris-foreground)",
6697
+ border: "1px solid var(--iris-border)",
6698
+ borderRadius: "var(--iris-radius-md, 6px)",
6699
+ boxShadow: "var(--iris-shadow-lg)",
6700
+ padding: "var(--iris-space-sm, 12px)",
6701
+ minWidth: "180px",
6702
+ display: "flex",
6703
+ flexDirection: "column",
6704
+ gap: "var(--iris-space-xxs, 4px)"
6705
+ }
6706
+ },
6707
+ [
6708
+ ...options.map(
6709
+ (opt) => h(
6710
+ "div",
6711
+ {
6712
+ key: opt.value,
6713
+ "data-iris-filter-option": opt.value,
6714
+ style: { display: "flex", alignItems: "center" }
6715
+ },
6716
+ [
6717
+ h(
6718
+ IrisCheckbox,
6719
+ {
6720
+ modelValue: ctx.filterDraft.value.includes(opt.value),
6721
+ size: "sm",
6722
+ "onUpdate:modelValue": () => ctx.toggle(opt.value)
6723
+ },
6724
+ { default: () => opt.label }
6725
+ )
6726
+ ]
6727
+ )
6728
+ ),
6729
+ h(
6730
+ "div",
6731
+ {
6732
+ style: {
6733
+ display: "flex",
6734
+ justifyContent: "flex-end",
6735
+ gap: "var(--iris-space-xs, 8px)",
6736
+ marginTop: "var(--iris-space-xs, 8px)"
6737
+ }
6738
+ },
6739
+ [
6740
+ h(
6741
+ "button",
6742
+ {
6743
+ type: "button",
6744
+ "data-iris-filter-clear": "",
6745
+ onClick: () => {
6746
+ ctx.clear(st.colKey);
6747
+ ctx.close();
6748
+ },
6749
+ style: {
6750
+ border: "1px solid var(--iris-border)",
6751
+ background: "transparent",
6752
+ color: "var(--iris-foreground)",
6753
+ cursor: "pointer",
6754
+ font: "inherit",
6755
+ fontSize: "var(--iris-font-size-sm, 13px)",
6756
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
6757
+ borderRadius: "var(--iris-radius-sm, 4px)"
6758
+ }
6759
+ },
6760
+ ctx.t("table.filterClear")
6761
+ ),
6762
+ h(
6763
+ "button",
6764
+ {
6765
+ type: "button",
6766
+ "data-iris-filter-confirm": "",
6767
+ onClick: () => {
6768
+ ctx.apply(st.colKey, ctx.filterDraft.value);
6769
+ ctx.close();
6770
+ },
6771
+ style: {
6772
+ border: "1px solid var(--iris-primary)",
6773
+ background: "var(--iris-primary)",
6774
+ color: "var(--iris-primary-foreground, #fff)",
6775
+ cursor: "pointer",
6776
+ font: "inherit",
6777
+ fontSize: "var(--iris-font-size-sm, 13px)",
6778
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
6779
+ borderRadius: "var(--iris-radius-sm, 4px)"
6780
+ }
6781
+ },
6782
+ ctx.t("table.filterConfirm")
6783
+ )
6784
+ ]
6785
+ )
6786
+ ]
6787
+ );
6788
+ return h(Teleport, { to: "body" }, [node]);
6789
+ }
6790
+ function renderImportPreviewSection(ctx) {
6791
+ if (!ctx.rows) return null;
6792
+ const columns = previewColumnsFromRows(ctx.rows);
6793
+ const cellStyle = {
6794
+ border: "1px solid var(--iris-border)",
6795
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
6796
+ whiteSpace: "nowrap"
6797
+ };
6798
+ const table = columns.length ? h(
6799
+ "table",
6800
+ {
6801
+ "data-iris-import-preview-table": "",
6802
+ style: { borderCollapse: "collapse", fontSize: "var(--iris-font-size-sm, 13px)" }
6803
+ },
6804
+ [
6805
+ h("thead", [
6806
+ h(
6807
+ "tr",
6808
+ columns.map(
6809
+ (column) => h(
6810
+ "th",
6811
+ {
6812
+ key: column,
6813
+ "data-iris-import-preview-header": column,
6814
+ style: {
6815
+ ...cellStyle,
6816
+ background: "var(--iris-surface)",
6817
+ color: "var(--iris-foreground)",
6818
+ textAlign: "start",
6819
+ fontWeight: 600
6820
+ }
6821
+ },
6822
+ column
6823
+ )
6824
+ )
6825
+ )
6826
+ ]),
6827
+ h(
6828
+ "tbody",
6829
+ ctx.rows.slice(0, 5).map(
6830
+ (row, rowIndex) => h(
6831
+ "tr",
6832
+ { key: rowIndex },
6833
+ columns.map(
6834
+ (column) => h(
6835
+ "td",
6836
+ {
6837
+ key: column,
6838
+ "data-iris-import-preview-cell": `${rowIndex}:${column}`,
6839
+ style: { ...cellStyle, color: "var(--iris-foreground)" }
6840
+ },
6841
+ String(row[column] ?? "")
6842
+ )
6843
+ )
6844
+ )
6845
+ )
6846
+ )
6847
+ ]
6848
+ ) : null;
6849
+ const node = h(
6850
+ "div",
6851
+ {
6852
+ "data-iris-import-preview-backdrop": "",
6853
+ onPointerdown: (event) => {
6854
+ if (event.target === event.currentTarget) ctx.onCancel();
6855
+ },
6856
+ style: {
6857
+ position: "fixed",
6858
+ inset: 0,
6859
+ background: "var(--iris-backdrop, rgba(0, 0, 0, 0.5))",
6860
+ zIndex: "var(--iris-z-modal, 1200)",
6861
+ display: "flex",
6862
+ alignItems: "center",
6863
+ justifyContent: "center",
6864
+ padding: "var(--iris-space-lg, 24px)"
6865
+ }
6866
+ },
6867
+ [
6868
+ h(
6869
+ "div",
6870
+ {
6871
+ "data-iris-import-preview": "",
6872
+ role: "dialog",
6873
+ "aria-modal": "true",
6874
+ "aria-label": ctx.t("table.importPreview.title"),
6875
+ style: {
6876
+ background: "var(--iris-surface-floating, var(--iris-surface))",
6877
+ color: "var(--iris-foreground)",
6878
+ border: "1px solid var(--iris-border)",
6879
+ borderRadius: "var(--iris-radius-lg, 8px)",
6880
+ boxShadow: "var(--iris-shadow-xl)",
6881
+ padding: "var(--iris-space-lg, 24px)",
6882
+ maxWidth: "90vw",
6883
+ maxHeight: "85vh",
6884
+ overflow: "auto",
6885
+ display: "flex",
6886
+ flexDirection: "column",
6887
+ gap: "var(--iris-space-sm, 12px)",
6888
+ fontSize: "var(--iris-font-size-md, 14px)"
6889
+ }
6890
+ },
6891
+ [
6892
+ h("div", { style: { fontWeight: 600 } }, ctx.t("table.importPreview.title")),
6893
+ table,
6894
+ ctx.rows.length > 5 ? h(
6895
+ "div",
6896
+ {
6897
+ "data-iris-import-preview-total": "",
6898
+ style: { color: "var(--iris-muted)", fontSize: "var(--iris-font-size-xs, 12px)" }
6899
+ },
6900
+ ctx.t("table.total", { total: ctx.rows.length })
6901
+ ) : null,
6902
+ h(
6903
+ "div",
6904
+ {
6905
+ style: {
6906
+ display: "flex",
6907
+ justifyContent: "flex-end",
6908
+ gap: "var(--iris-space-xs, 8px)"
6909
+ }
6910
+ },
6911
+ [
6912
+ h(
6913
+ "button",
6914
+ {
6915
+ type: "button",
6916
+ "data-iris-import-preview-cancel": "",
6917
+ onClick: ctx.onCancel,
6918
+ style: {
6919
+ border: "1px solid var(--iris-border)",
6920
+ cursor: "pointer",
6921
+ background: "var(--iris-surface)",
6922
+ color: "var(--iris-foreground)",
6923
+ fontSize: "var(--iris-font-size-sm, 13px)",
6924
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
6925
+ borderRadius: "var(--iris-radius-sm, 4px)"
6926
+ }
6927
+ },
6928
+ ctx.t("table.importPreview.cancel")
6929
+ ),
6930
+ h(
6931
+ "button",
6932
+ {
6933
+ type: "button",
6934
+ "data-iris-import-preview-confirm": "",
6935
+ onClick: ctx.onConfirm,
6936
+ style: {
6937
+ border: "none",
6938
+ cursor: "pointer",
6939
+ background: "var(--iris-primary)",
6940
+ color: "var(--iris-primary-foreground)",
6941
+ fontSize: "var(--iris-font-size-sm, 13px)",
6942
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
6943
+ borderRadius: "var(--iris-radius-sm, 4px)"
6944
+ }
6945
+ },
6946
+ ctx.t("table.importPreview.confirm")
6947
+ )
6948
+ ]
6949
+ )
6950
+ ]
6951
+ )
6952
+ ]
6953
+ );
6954
+ return h(Teleport, { to: "body" }, [node]);
6955
+ }
6956
+ function renderTableSummaryRow(ctx) {
6957
+ if (ctx.bodyData.length === 0 || !ctx.leafColumns.some((column) => column.summary)) return null;
6958
+ const summaryCells = [...ctx.leadingCells];
6959
+ for (let columnIndex = 0; columnIndex < ctx.leafColumns.length; columnIndex += 1) {
6960
+ const column = ctx.leafColumns[columnIndex];
6961
+ if (ctx.visibleColSet && !ctx.visibleColSet.has(columnIndex)) continue;
6962
+ const align = column.align ?? "left";
6963
+ const operation = column.summary;
6964
+ const value = operation ? aggregate(ctx.bodyData, (row) => ctx.getCellValue(row, column), operation) : null;
6965
+ const content = operation != null && value != null ? column.renderSummary ? column.renderSummary(value, ctx.bodyData) : String(value) : "";
6966
+ summaryCells.push(
6967
+ h(
6968
+ "div",
6969
+ {
6970
+ key: column.key,
6971
+ role: "cell",
6972
+ "data-iris-table-cell": column.key,
6973
+ "data-iris-table-summary-cell": operation ? "" : void 0,
6974
+ "data-iris-table-pinned": column.pinned,
6975
+ style: {
6976
+ display: "flex",
6977
+ alignItems: "center",
6978
+ justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
6979
+ padding: "var(--iris-space-xs, 8px) var(--iris-padding-md)",
6980
+ fontSize: "var(--iris-font-size-md, 14px)",
6981
+ whiteSpace: "nowrap",
6982
+ overflow: "hidden",
6983
+ textOverflow: "ellipsis",
6984
+ ...ctx.visibleColSet ? { gridColumnStart: String(ctx.colTrack(columnIndex)) } : {},
6985
+ ...ctx.pinnedStyle(column.key)
6986
+ }
6987
+ },
6988
+ content
6989
+ )
6990
+ );
6991
+ }
6992
+ return h(
6993
+ "div",
6994
+ {
6995
+ role: "row",
6996
+ "data-iris-table-row": "summary",
6997
+ style: {
6998
+ display: "grid",
6999
+ gridTemplateColumns: ctx.gridTemplate,
7000
+ fontWeight: "600",
7001
+ borderTop: "2px solid var(--iris-border)",
7002
+ background: "var(--iris-surface)"
7003
+ }
7004
+ },
7005
+ summaryCells
7006
+ );
7007
+ }
7008
+ function renderTableStateRow(ctx) {
7009
+ if (ctx.error) {
7010
+ return h("div", { role: "row", "data-iris-table-row": "error", style: ctx.stateRowStyle }, [
7011
+ h(
7012
+ "span",
7013
+ { style: { marginInlineEnd: ctx.onRetry ? "var(--iris-space-sm, 12px)" : "0px" } },
7014
+ ctx.errorContent ?? ""
7015
+ ),
7016
+ ctx.retry ? h(
7017
+ "button",
7018
+ {
7019
+ type: "button",
7020
+ "data-iris-table-retry": "",
7021
+ onClick: ctx.retry,
7022
+ style: {
7023
+ border: "1px solid var(--iris-border)",
7024
+ background: "var(--iris-surface)",
7025
+ color: "var(--iris-foreground)",
7026
+ borderRadius: "var(--iris-radius-sm, 4px)",
7027
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
7028
+ fontSize: "var(--iris-font-size-sm, 13px)",
7029
+ cursor: "pointer"
7030
+ }
7031
+ },
7032
+ ctx.retryLabel
7033
+ ) : null
7034
+ ]);
7035
+ }
7036
+ if (ctx.loading) {
7037
+ return h(
7038
+ "div",
7039
+ {
7040
+ role: "row",
7041
+ "aria-busy": "true",
7042
+ "data-iris-table-row": "loading",
7043
+ style: ctx.stateRowStyle
7044
+ },
7045
+ ctx.loadingContent ?? ""
7046
+ );
7047
+ }
7048
+ if (ctx.rowCount === 0) {
7049
+ return h(
7050
+ "div",
7051
+ { role: "row", "data-iris-table-row": "empty", style: ctx.stateRowStyle },
7052
+ ctx.emptyContent ?? ""
7053
+ );
7054
+ }
7055
+ return null;
7056
+ }
7057
+ function createTableKeyboard(options) {
7058
+ const gridKeys = /* @__PURE__ */ new Set([
7059
+ "ArrowUp",
7060
+ "ArrowDown",
7061
+ "ArrowLeft",
7062
+ "ArrowRight",
7063
+ "Home",
7064
+ "End",
7065
+ "PageUp",
7066
+ "PageDown"
7067
+ ]);
7068
+ const handleGridKey = (event) => {
7069
+ if (!options.keyboardNavigation() || !gridKeys.has(event.key)) return;
7070
+ const target = event.target;
7071
+ if (target.dataset.gridRow === void 0) return;
7072
+ event.preventDefault();
7073
+ const next = nextGridCell(
7074
+ options.focused.value ?? { row: 0, col: 0 },
7075
+ event.key,
7076
+ { rowCount: options.rows().length, colCount: options.columns().length, pageSize: 10 }
7077
+ );
7078
+ options.focused.value = next;
7079
+ options.root()?.querySelector(`[data-grid-row="${next.row}"][data-grid-col="${next.col}"]`)?.focus();
7080
+ };
7081
+ const handleCellRangeKey = (event) => {
7082
+ if (!options.cellRange()) return;
7083
+ if (event.key === "Escape") {
7084
+ options.range.clearRange();
7085
+ return;
7086
+ }
7087
+ const arrows = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
7088
+ if (!event.shiftKey || !arrows.has(event.key)) return;
7089
+ const target = event.target;
7090
+ const row = target.dataset.irisCellRow;
7091
+ const col = target.dataset.irisCellCol;
7092
+ if (row === void 0 || col === void 0) return;
7093
+ event.preventDefault();
7094
+ const current = options.range.getState().active ?? { row: Number(row), col: Number(col) };
7095
+ const nextRow = event.key === "ArrowUp" ? Math.max(0, current.row - 1) : event.key === "ArrowDown" ? Math.min(options.rows().length - 1, current.row + 1) : current.row;
7096
+ const nextCol = event.key === "ArrowLeft" ? Math.max(0, current.col - 1) : event.key === "ArrowRight" ? Math.min(options.columns().length - 1, current.col + 1) : current.col;
7097
+ options.range.extendRange(nextRow, nextCol);
7098
+ };
7099
+ const isInRange = (row, col) => {
7100
+ const { anchor, active } = options.rangeState.value;
7101
+ return Boolean(
7102
+ anchor && active && row >= Math.min(anchor.row, active.row) && row <= Math.max(anchor.row, active.row) && col >= Math.min(anchor.col, active.col) && col <= Math.max(anchor.col, active.col)
7103
+ );
7104
+ };
7105
+ const activeCellRange = () => {
7106
+ const { anchor, active } = options.rangeState.value;
7107
+ if (!anchor || !active) return null;
7108
+ return {
7109
+ start: { row: Math.min(anchor.row, active.row), col: Math.min(anchor.col, active.col) },
7110
+ end: { row: Math.max(anchor.row, active.row), col: Math.max(anchor.col, active.col) }
7111
+ };
7112
+ };
7113
+ const copyActiveRange = () => {
7114
+ const range = activeCellRange();
7115
+ const clip = options.clipConfig();
7116
+ if (!range || clip?.copy === false) return;
7117
+ void writeClipboardText(
7118
+ serializeTableRange(
7119
+ options.rows(),
7120
+ options.columns(),
7121
+ range,
7122
+ clip?.copyFormat,
7123
+ clip?.copyWithFormat === true
7124
+ )
7125
+ );
7126
+ };
7127
+ const handleClipboardKey = (event) => {
7128
+ const clip = options.clipConfig();
7129
+ if (!options.cellRange() || !clip || event.defaultPrevented) return;
7130
+ if (event.key.toLowerCase() !== "c" || !event.ctrlKey && !event.metaKey) return;
7131
+ if (clip.copy === false || !activeCellRange()) return;
7132
+ event.preventDefault();
7133
+ copyActiveRange();
7134
+ };
7135
+ return {
7136
+ handleRootKeyDown: (event) => {
7137
+ handleGridKey(event);
7138
+ handleCellRangeKey(event);
7139
+ handleClipboardKey(event);
7140
+ },
7141
+ isInRange,
7142
+ activeCellRange,
7143
+ copyActiveRange
7144
+ };
7145
+ }
7146
+ function computeResponsiveTableColumns(columns, containerWidth, leadingWidth, widthOf) {
7147
+ if (containerWidth <= 0 || containerWidth >= RESPONSIVE_NARROW_WIDTH) {
7148
+ return { columns, overflow: false };
7149
+ }
7150
+ const isPinned = (column) => column.children && column.children.length > 0 ? column.children.some(isPinned) : column.pinned !== void 0;
7151
+ const fitted = computeResponsiveColumns(columns, Math.max(1, containerWidth - leadingWidth), {
7152
+ widthOf: (column) => widthOf(column),
7153
+ isPinned: (column) => isPinned(column),
7154
+ narrowWidth: RESPONSIVE_NARROW_WIDTH - leadingWidth
7155
+ });
7156
+ const natural = fitted.reduce(
7157
+ (sum, column) => sum + (column.children && column.children.length > 0 ? flattenLeafColumns([column]).reduce((nested, leaf) => nested + widthOf(leaf), 0) : widthOf(column)),
7158
+ leadingWidth
7159
+ );
7160
+ return { columns: fitted, overflow: natural > containerWidth };
7161
+ }
7162
+
7163
+ // src/primitives/table/table-styles.ts
7164
+ var TABLE_STYLE_ID = "iris-table-row-styles";
7165
+ var TABLE_STYLES = `
7166
+ [data-iris-table] [role="row"]:hover {
7167
+ --iris-cell-bg: var(--iris-surface-hover);
7168
+ }
7169
+ [data-iris-table-row-selected="true"] {
7170
+ --iris-cell-bg: var(--iris-surface-selected);
7171
+ }
7172
+ @media print { [data-iris-table-tabs], [data-iris-table-toolbar], [data-iris-table-form], [data-iris-scroll-hint] { display: none !important; } [data-iris-table][data-printable="true"] { border: none !important; box-shadow: var(--iris-shadow-none, none) !important; } }
7173
+ [data-iris-table][data-density="compact"] [data-iris-table-cell],
7174
+ [data-iris-table][data-density="compact"] [data-iris-table-header],
7175
+ [data-iris-table][data-density="compact"] [data-iris-table-summary-cell],
7176
+ [data-iris-table][data-density="compact"] [data-iris-table-footer-cell] { padding-block: 6px !important; }
7177
+ [data-iris-table][data-density="cozy"] [data-iris-table-cell],
7178
+ [data-iris-table][data-density="cozy"] [data-iris-table-header],
7179
+ [data-iris-table][data-density="cozy"] [data-iris-table-summary-cell],
7180
+ [data-iris-table][data-density="cozy"] [data-iris-table-footer-cell] { padding-block: 4px !important; }
7181
+ [data-iris-row-target="true"] {
7182
+ --iris-cell-bg: color-mix(in srgb, var(--iris-primary) 18%, var(--iris-background));
7183
+ background: color-mix(in srgb, var(--iris-primary) 18%, var(--iris-background));
7184
+ }
7185
+ `;
7186
+ function ensureTableStyles() {
7187
+ if (typeof document === "undefined" || document.getElementById(TABLE_STYLE_ID)) return;
7188
+ const style = document.createElement("style");
7189
+ style.id = TABLE_STYLE_ID;
7190
+ style.textContent = TABLE_STYLES;
7191
+ document.head.appendChild(style);
7192
+ }
7193
+
7194
+ // src/primitives/table/table-columns.ts
7195
+ function applyDetectedTableTypes(columns, detectedTypes) {
7196
+ const apply = (column) => {
7197
+ const kind = detectedTypes[column.key];
7198
+ const next = kind ? {
7199
+ ...column,
7200
+ ...kind === "number" ? column.align === void 0 ? { align: "right" } : null : column.align === void 0 ? { align: "left" } : null
7201
+ } : column;
7202
+ return next.children && next.children.length > 0 ? { ...next, children: next.children.map(apply) } : next;
7203
+ };
7204
+ return columns.map(apply);
7205
+ }
7206
+ function renderTableFilterTrigger(options) {
7207
+ const { column, leaf, active, open, label, onOpen } = options;
7208
+ if (!leaf || !column.filterable) return null;
7209
+ return h(
7210
+ "button",
7211
+ {
7212
+ type: "button",
7213
+ "data-iris-filter-trigger": column.key,
7214
+ "aria-label": label,
7215
+ "aria-haspopup": "true",
7216
+ "aria-expanded": open ? "true" : void 0,
7217
+ "data-iris-filter-active": active ? "true" : void 0,
7218
+ onClick: (event) => onOpen(event, column.key),
7219
+ onKeydown: (event) => event.stopPropagation(),
7220
+ style: {
7221
+ border: "none",
7222
+ background: "transparent",
7223
+ cursor: "pointer",
7224
+ padding: "0",
7225
+ marginInlineStart: "var(--iris-space-xxs, 4px)",
7226
+ fontSize: "var(--iris-font-size-xs, 12px)",
7227
+ lineHeight: "1",
7228
+ color: active ? "var(--iris-primary)" : "var(--iris-muted)"
7229
+ }
7230
+ },
7231
+ "\u23F7"
7232
+ );
7233
+ }
7234
+ function renderTableSortIndicator(column, options) {
7235
+ if (!column.sortable) return null;
7236
+ const state = options.multiSort ? options.multiSortState.find((item) => item.key === column.key) ?? null : options.sort;
7237
+ const isActive = state?.key === column.key;
7238
+ const direction = isActive ? state.direction : null;
7239
+ const color = isActive ? "var(--iris-primary)" : "var(--iris-muted)";
7240
+ return h(
7241
+ "span",
7242
+ {
7243
+ "aria-hidden": "true",
7244
+ style: {
7245
+ display: "inline-flex",
7246
+ flexDirection: "column",
7247
+ marginInlineStart: "var(--iris-space-xxs, 4px)",
7248
+ lineHeight: "0.6",
7249
+ fontSize: "var(--iris-font-size-xs, 12px)",
7250
+ color
7251
+ }
7252
+ },
7253
+ [
7254
+ h("span", { style: { opacity: direction === "asc" ? "1" : "0.45" } }, "\u25B2"),
7255
+ h("span", { style: { opacity: direction === "desc" ? "1" : "0.45" } }, "\u25BC")
7256
+ ]
7257
+ );
7258
+ }
7259
+
7260
+ // src/primitives/table/table-row-target.ts
7261
+ function createTableRowTarget(root) {
7262
+ let timer = null;
7263
+ const find = (key) => {
7264
+ const element = root();
7265
+ if (!element) return null;
7266
+ const keyString = String(key);
7267
+ return Array.from(element.querySelectorAll("[data-iris-table-row-key]")).find(
7268
+ (node) => node.getAttribute("data-iris-table-row-key") === keyString
7269
+ ) ?? null;
7270
+ };
7271
+ const scrollTo = (key) => {
7272
+ find(key)?.scrollIntoView?.({ block: "nearest" });
7273
+ };
7274
+ const goTo = (key) => {
7275
+ const row = find(key);
7276
+ if (!row) return;
7277
+ row.scrollIntoView?.({ block: "nearest" });
7278
+ root()?.querySelector('[data-iris-row-target="true"]')?.removeAttribute("data-iris-row-target");
7279
+ row.setAttribute("data-iris-row-target", "true");
7280
+ if (timer !== null) clearTimeout(timer);
7281
+ timer = setTimeout(() => {
7282
+ timer = null;
7283
+ row.removeAttribute("data-iris-row-target");
7284
+ }, 2e3);
7285
+ };
7286
+ const dispose = () => {
7287
+ if (timer !== null) clearTimeout(timer);
7288
+ timer = null;
7289
+ };
7290
+ return { find, scrollTo, goTo, dispose };
7291
+ }
7292
+ function createTablePinnedDrag(options) {
7293
+ const firstRightPinnedIndex = computed(() => {
7294
+ const index = options.columns().findIndex((column) => column.pinned === "right");
7295
+ return index < 0 ? options.columns().length : index;
7296
+ });
7297
+ const pinnedBoundaryColumn = computed(() => {
7298
+ if (!options.enabled()) return null;
7299
+ for (let index = firstRightPinnedIndex.value - 1; index >= 0; index -= 1) {
7300
+ const column = options.columns()[index];
7301
+ if (column?.pinned === "left") return column;
7302
+ }
7303
+ return null;
7304
+ });
7305
+ const handleRefs = /* @__PURE__ */ new Map();
7306
+ const wiredKeys = /* @__PURE__ */ new Set();
7307
+ const activeKey = ref(null);
7308
+ const dragDx = ref(0);
7309
+ const getHandleRef = (key) => {
7310
+ let handle = handleRefs.get(key);
7311
+ if (!handle) {
7312
+ handle = ref(null);
7313
+ handleRefs.set(key, handle);
7314
+ }
7315
+ return handle;
7316
+ };
7317
+ const resolvePinnedCount = (dx) => {
7318
+ const columns = options.columns();
7319
+ const cap = firstRightPinnedIndex.value;
7320
+ const current = leftPinnedCount(columns, cap);
7321
+ let currentWidth = 0;
7322
+ for (let index = 0; index < current; index += 1) {
7323
+ const column = columns[index];
7324
+ if (column) currentWidth += options.widthOf(column);
7325
+ }
7326
+ return pinnedCountFromBudget(
7327
+ columns,
7328
+ (column) => options.widthOf(column),
7329
+ currentWidth + dx,
7330
+ cap
7331
+ );
7332
+ };
7333
+ const commitPinnedCount = (count) => {
7334
+ if (!options.enabled()) return;
7335
+ const columns = options.columns();
7336
+ const cap = firstRightPinnedIndex.value;
7337
+ const clamped = Math.max(0, Math.min(cap, count));
7338
+ const current = leftPinnedCount(columns, cap);
7339
+ if (clamped === current) return;
7340
+ for (let index = 0; index < cap; index += 1) {
7341
+ const column = columns[index];
7342
+ if (!column) continue;
7343
+ const target = index < clamped ? "left" : null;
7344
+ if (column.pinned === target) continue;
7345
+ options.onColumnPinnedChange?.(column.key, target);
7346
+ }
7347
+ options.onPinnedCountChange?.(clamped);
7348
+ };
7349
+ const wire = (key) => {
7350
+ if (wiredKeys.has(key)) return;
7351
+ wiredKeys.add(key);
7352
+ const handle = getHandleRef(key);
7353
+ useDrag({
7354
+ handle,
7355
+ disabled: computed(() => !options.enabled()),
7356
+ onStart: () => {
7357
+ activeKey.value = key;
7358
+ dragDx.value = 0;
7359
+ },
7360
+ onDrag: ({ dx }) => {
7361
+ activeKey.value = key;
7362
+ dragDx.value = dx;
7363
+ },
7364
+ onEnd: ({ dx }) => {
7365
+ commitPinnedCount(resolvePinnedCount(dx));
7366
+ activeKey.value = null;
7367
+ dragDx.value = 0;
7368
+ }
7369
+ });
7370
+ };
7371
+ for (const column of options.columns()) wire(column.key);
7372
+ return (column) => {
7373
+ const boundary = pinnedBoundaryColumn.value;
7374
+ if (!options.enabled() || !boundary || boundary.key !== column.key) return null;
7375
+ const handle = getHandleRef(column.key);
7376
+ return h(
7377
+ "span",
7378
+ {
7379
+ ref: (element) => {
7380
+ handle.value = element ?? null;
7381
+ },
7382
+ role: "separator",
7383
+ "aria-orientation": "vertical",
7384
+ "aria-label": `Adjust pinned column count at ${column.title}`,
7385
+ tabindex: 0,
7386
+ "data-iris-pinned-drag-handle": "",
7387
+ "data-column-key": column.key,
7388
+ "data-iris-pinned-drag-active": activeKey.value === column.key && dragDx.value !== 0 ? "true" : void 0,
7389
+ onPointerdown: (event) => event.stopPropagation(),
7390
+ onKeydown: (event) => {
7391
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
7392
+ event.preventDefault();
7393
+ event.stopPropagation();
7394
+ commitPinnedCount(resolvePinnedCount(0) + (event.key === "ArrowRight" ? 1 : -1));
7395
+ },
7396
+ style: {
7397
+ position: "absolute",
7398
+ top: "0",
7399
+ right: "0",
7400
+ bottom: "0",
7401
+ width: "8px",
7402
+ cursor: "col-resize",
7403
+ touchAction: "none",
7404
+ userSelect: "none",
7405
+ zIndex: "2",
7406
+ transform: activeKey.value === column.key && dragDx.value !== 0 ? `translateX(${dragDx.value}px)` : void 0
7407
+ }
7408
+ },
7409
+ h("span", {
7410
+ "aria-hidden": "true",
7411
+ "data-iris-pinned-drag-line": "",
7412
+ style: {
7413
+ position: "absolute",
7414
+ top: "0",
7415
+ bottom: "0",
7416
+ insetInlineStart: "50%",
7417
+ width: "2px",
7418
+ background: "var(--iris-primary)",
7419
+ transform: "translateX(-50%)"
7420
+ }
7421
+ })
7422
+ );
7423
+ };
7424
+ }
7425
+ function useTableImport(isPreviewEnabled, getOnImport) {
7426
+ const importFileInput = ref(null);
7427
+ const importPreviewRows = ref(null);
7428
+ const handleImportFile = (event) => {
7429
+ const input = event.target;
7430
+ const file = input?.files?.[0];
7431
+ if (!file || !getOnImport() || typeof FileReader === "undefined") return;
7432
+ const reader = new FileReader();
7433
+ reader.onload = () => {
7434
+ const rows = rowsFromCsv(String(reader.result ?? ""));
7435
+ if (rows.length === 0) return;
7436
+ if (isPreviewEnabled()) importPreviewRows.value = rows;
7437
+ else getOnImport()?.(rows);
7438
+ };
7439
+ reader.readAsText(file);
7440
+ if (input) input.value = "";
7441
+ };
7442
+ const confirmImportPreview = () => {
7443
+ if (!importPreviewRows.value) return;
7444
+ getOnImport()?.(importPreviewRows.value);
7445
+ importPreviewRows.value = null;
7446
+ };
7447
+ const cancelImportPreview = () => {
7448
+ importPreviewRows.value = null;
7449
+ };
7450
+ watch(importPreviewRows, (rows, _previous, onCleanup) => {
7451
+ if (!rows || typeof window === "undefined") return;
7452
+ const closeOnEscape = (event) => {
7453
+ if (event.key === "Escape") cancelImportPreview();
7454
+ };
7455
+ window.addEventListener("keydown", closeOnEscape);
7456
+ onCleanup(() => window.removeEventListener("keydown", closeOnEscape));
7457
+ });
7458
+ return {
7459
+ importFileInput,
7460
+ importPreviewRows,
7461
+ handleImportFile,
7462
+ confirmImportPreview,
7463
+ cancelImportPreview
7464
+ };
7465
+ }
7466
+ var editorInputStyle = (error) => ({
7467
+ width: "100%",
7468
+ border: `1px solid ${error ? "var(--iris-danger)" : "var(--iris-primary)"}`,
7469
+ borderRadius: "var(--iris-radius-sm)",
7470
+ padding: "var(--iris-space-xxs, 4px) var(--iris-padding-sm, 6px)",
7471
+ font: "inherit",
7472
+ background: "var(--iris-background)",
7473
+ color: "var(--iris-foreground)",
7474
+ outline: "none",
7475
+ boxShadow: "0 0 0 3px color-mix(in srgb, var(--iris-primary) 18%, transparent)"
7476
+ });
7477
+ function editorErrorVNode(editCellId, error) {
7478
+ return h(
7479
+ "div",
7480
+ {
7481
+ id: `${editCellId}-error`,
7482
+ role: "alert",
7483
+ "data-iris-table-editor-error": "",
7484
+ style: {
7485
+ marginTop: "var(--iris-space-xxs, 4px)",
7486
+ fontSize: "var(--iris-font-size-xs, 12px)",
7487
+ color: "var(--iris-danger)"
7488
+ }
7489
+ },
7490
+ error
7491
+ );
7492
+ }
7493
+ var editPreviewStyle = {
7494
+ flexBasis: "100%",
7495
+ minWidth: "0",
7496
+ marginTop: "var(--iris-space-xxs, 4px)",
7497
+ fontSize: "var(--iris-font-size-xs, 12px)",
7498
+ color: "var(--iris-muted)"
7499
+ };
7500
+ function previewVNode(ctx, row, col, draft) {
7501
+ if (!ctx.editPreview || !col.formatter) return null;
7502
+ return h(
7503
+ "div",
7504
+ { "data-iris-edit-preview": "", style: editPreviewStyle },
7505
+ ctx.previewValue(row, col, draft)
7506
+ );
7507
+ }
7508
+ function renderRowSessionContent(ctx, row, col, index, key, editCellId, session) {
7509
+ const error = session.error;
7510
+ const input = h("input", {
7511
+ ref: (el) => {
7512
+ ctx.rowEditorRefs.set(col.key, el ?? null);
7513
+ },
7514
+ type: col.editor === "number" ? "number" : "text",
7515
+ value: session.draft,
7516
+ "data-iris-table-editor": "",
7517
+ "aria-invalid": error ? "true" : void 0,
7518
+ "aria-describedby": error ? `${editCellId}-error` : void 0,
7519
+ onInput: (e) => {
7520
+ session.draft = e.target.value;
7521
+ },
7522
+ onKeydown: (e) => {
7523
+ if (e.key === "Enter") {
7524
+ e.preventDefault();
7525
+ ctx.commitRowSession(key, col, index, editCellId);
7526
+ } else if (e.key === "Escape") {
7527
+ e.preventDefault();
7528
+ ctx.cancelRowEdit();
7529
+ }
7530
+ },
7531
+ onBlur: () => ctx.commitRowSession(key, col, index, editCellId),
7532
+ onClick: (e) => e.stopPropagation(),
7533
+ onDblclick: (e) => e.stopPropagation(),
7534
+ style: editorInputStyle(error)
7535
+ });
7536
+ const preview = previewVNode(ctx, row, col, session.draft);
7537
+ const children = [input, preview, error ? editorErrorVNode(editCellId, error) : null].filter(
7538
+ (node) => node !== null
7539
+ );
7540
+ return children.length === 1 ? children[0] : children;
7541
+ }
7542
+ function renderCellEditContent(ctx, row, col, index, editCellId) {
7543
+ const error = ctx.editError.value;
7544
+ const input = h("input", {
7545
+ ref: (el) => {
7546
+ ctx.editorInputRef.value = el ?? null;
7547
+ },
7548
+ type: col.editor === "number" ? "number" : "text",
7549
+ value: ctx.editingDraft.value,
7550
+ "data-iris-table-editor": "",
7551
+ "aria-invalid": error ? "true" : void 0,
7552
+ "aria-describedby": error ? `${editCellId}-error` : void 0,
7553
+ onInput: (e) => {
7554
+ ctx.editingDraft.value = e.target.value;
7555
+ },
7556
+ onKeydown: (e) => {
7557
+ if (e.key === "Enter") {
7558
+ e.preventDefault();
7559
+ ctx.commitEdit(row, col, index);
7560
+ } else if (e.key === "Escape") {
7561
+ e.preventDefault();
7562
+ ctx.cancelEdit();
7563
+ }
7564
+ },
7565
+ onBlur: () => ctx.commitEdit(row, col, index),
7566
+ onClick: (e) => e.stopPropagation(),
7567
+ onDblclick: (e) => e.stopPropagation(),
7568
+ style: editorInputStyle(error)
7569
+ });
7570
+ const preview = previewVNode(ctx, row, col, ctx.editingDraft.value);
7571
+ const children = [input, preview, error ? editorErrorVNode(editCellId, error) : null].filter(
7572
+ (node) => node !== null
7573
+ );
7574
+ return children.length === 1 ? children[0] : children;
7575
+ }
7576
+ function renderGroupedHeader(ctx, matrix) {
7577
+ const lead = (ctx.showDrag ? 1 : 0) + (ctx.showSeq ? 1 : 0) + (ctx.showDetail ? 1 : 0) + (ctx.showSelection ? 1 : 0);
7578
+ const cells = [];
7579
+ if (ctx.showDrag) {
7580
+ cells.push(
7581
+ h("div", {
7582
+ key: "__drag__",
7583
+ role: "columnheader",
7584
+ "data-iris-table-header": "__drag",
7585
+ style: { gridColumn: "1", gridRow: "1 / -1" }
7586
+ })
7587
+ );
7588
+ }
7589
+ if (ctx.showSeq) {
7590
+ cells.push(
7591
+ h("div", {
7592
+ key: "__seq__",
7593
+ role: "columnheader",
7594
+ "data-iris-table-header": "__seq",
7595
+ style: { gridColumn: ctx.showDrag ? "2" : "1", gridRow: "1 / -1" }
7596
+ })
7597
+ );
7598
+ }
7599
+ if (ctx.showDetail) {
7600
+ cells.push(
7601
+ h("div", {
7602
+ key: "__expand__",
7603
+ role: "columnheader",
7604
+ style: {
7605
+ gridColumn: `${(ctx.showDrag ? 1 : 0) + (ctx.showSeq ? 1 : 0) + 1}`,
7606
+ gridRow: "1 / -1"
7607
+ }
7608
+ })
7609
+ );
7610
+ }
7611
+ if (ctx.showSelection) {
7612
+ cells.push(
7613
+ h(
7614
+ "div",
7615
+ {
7616
+ key: "__select__",
7617
+ role: "columnheader",
7618
+ "data-iris-table-header": "",
7619
+ style: {
7620
+ gridColumn: `${(ctx.showDrag ? 1 : 0) + (ctx.showSeq ? 1 : 0) + (ctx.showDetail ? 1 : 0) + 1}`,
7621
+ gridRow: "1 / -1",
7622
+ display: "flex",
7623
+ alignItems: "center",
7624
+ justifyContent: "center",
7625
+ padding: "8px 12px",
7626
+ background: "var(--iris-surface)",
7627
+ borderBottom: "1px solid var(--iris-border)"
7628
+ }
7629
+ },
7630
+ ctx.selectable === "multi" ? [
7631
+ [
7632
+ h(IrisCheckbox, {
7633
+ modelValue: ctx.allSelected.value ? true : ctx.someSelected.value ? "indeterminate" : false,
7634
+ size: "sm",
7635
+ ariaLabel: ctx.t("table.selectAll"),
7636
+ "onUpdate:modelValue": ctx.toggleAll
7637
+ }),
7638
+ ctx.selection && ctx.selection.length > 0 ? h(
7639
+ "span",
7640
+ {
7641
+ "data-iris-table-selected-count": "",
7642
+ style: {
7643
+ marginInlineStart: "var(--iris-space-xs, 8px)",
7644
+ fontSize: "var(--iris-font-size-sm, 13px)",
7645
+ color: "var(--iris-muted)",
7646
+ whiteSpace: "nowrap"
7647
+ }
7648
+ },
7649
+ ctx.t("table.selectedCount", { count: String(ctx.selection.length) })
7650
+ ) : null
7651
+ ]
7652
+ ] : ""
7653
+ )
7654
+ );
5941
7655
  }
5942
- return DEFAULT_COL_WIDTH;
7656
+ for (const rowCells of matrix) {
7657
+ for (const cell of rowCells) {
7658
+ const col = cell.column;
7659
+ const isLeaf = !col.children || col.children.length === 0;
7660
+ const sortable = isLeaf && col.sortable;
7661
+ const align = col.align ?? "left";
7662
+ const headerSlot = ctx.slots[`header.${col.key}`];
7663
+ const title = headerSlot?.({ column: col }) ?? col.title;
7664
+ cells.push(
7665
+ h(
7666
+ "div",
7667
+ {
7668
+ key: `${col.key}-${cell.level}`,
7669
+ role: "columnheader",
7670
+ "data-iris-table-header": col.key,
7671
+ "data-iris-table-header-group": isLeaf ? void 0 : "",
7672
+ "aria-colspan": cell.colSpan,
7673
+ onClick: sortable ? () => ctx.onHeaderClick(col) : void 0,
7674
+ "aria-sort": sortable ? ctx.ariaSortFor(col) : void 0,
7675
+ style: {
7676
+ gridColumn: `${lead + cell.colStart} / span ${cell.colSpan}`,
7677
+ gridRow: `${cell.level + 1} / span ${cell.rowSpan}`,
7678
+ display: "flex",
7679
+ alignItems: "center",
7680
+ justifyContent: isLeaf ? align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start" : "center",
7681
+ padding: "8px var(--iris-padding-md)",
7682
+ cursor: sortable ? "pointer" : "default",
7683
+ userSelect: sortable ? "none" : "auto",
7684
+ background: "var(--iris-surface)",
7685
+ borderBottom: "1px solid var(--iris-border)",
7686
+ fontWeight: "600",
7687
+ fontSize: "var(--iris-font-size-md, 14px)",
7688
+ color: "var(--iris-foreground)",
7689
+ whiteSpace: "nowrap",
7690
+ overflow: "hidden",
7691
+ textOverflow: "ellipsis"
7692
+ }
7693
+ },
7694
+ [
7695
+ title,
7696
+ sortable ? ctx.sortIndicator(col) : null,
7697
+ ctx.multiSortSeq(col),
7698
+ ctx.renderFilterTrigger(col, isLeaf),
7699
+ isLeaf ? ctx.pinnedDragHandle?.(col) ?? null : null
7700
+ ]
7701
+ )
7702
+ );
7703
+ }
7704
+ }
7705
+ return h(
7706
+ "div",
7707
+ {
7708
+ role: "row",
7709
+ "data-iris-table-row": "header",
7710
+ "data-iris-table-header-row": "",
7711
+ "data-iris-table-header-grouped": "",
7712
+ style: {
7713
+ display: "grid",
7714
+ gridTemplateColumns: ctx.gridTemplate.value,
7715
+ gridTemplateRows: `repeat(${matrix.length}, auto)`
7716
+ }
7717
+ },
7718
+ cells
7719
+ );
7720
+ }
7721
+ function createTableViewsController(options) {
7722
+ const views = ref(readTableViews(options.config()));
7723
+ const internalActiveKey = ref(null);
7724
+ const activeKey = computed(() => {
7725
+ const config = options.config();
7726
+ return config?.activeKey !== void 0 ? config.activeKey ?? null : internalActiveKey.value;
7727
+ });
7728
+ const saveOpen = ref(false);
7729
+ const draft = ref("");
7730
+ const activeTab = ref(null);
7731
+ const persist = (next) => {
7732
+ views.value = next;
7733
+ writeTableViews(options.config(), next);
7734
+ };
7735
+ const applySnapshot = (snapshot) => {
7736
+ if (Object.prototype.hasOwnProperty.call(snapshot, "sort"))
7737
+ options.setSort(snapshot.sort ?? null);
7738
+ };
7739
+ const select = (key) => {
7740
+ const view = views.value.find((candidate) => candidate.name === key);
7741
+ if (!view) return;
7742
+ applySnapshot(view.snapshot);
7743
+ internalActiveKey.value = key;
7744
+ options.onActiveViewChange(key);
7745
+ };
7746
+ const save = (name) => {
7747
+ const trimmed = name.trim();
7748
+ if (!trimmed) return;
7749
+ const currentSort = options.sort.value;
7750
+ const entry = {
7751
+ name: trimmed,
7752
+ snapshot: { sort: currentSort ? { ...currentSort } : null }
7753
+ };
7754
+ const index = views.value.findIndex((view) => view.name === trimmed);
7755
+ persist(
7756
+ index < 0 ? [...views.value, entry] : views.value.map((view, i) => i === index ? entry : view)
7757
+ );
7758
+ internalActiveKey.value = trimmed;
7759
+ options.onActiveViewChange(trimmed);
7760
+ };
7761
+ const remove = (key) => {
7762
+ persist(views.value.filter((view) => view.name !== key));
7763
+ if (activeKey.value === key) {
7764
+ internalActiveKey.value = null;
7765
+ options.onActiveViewChange(null);
7766
+ }
7767
+ };
7768
+ const applyTab = (tab) => {
7769
+ activeTab.value = tab.key;
7770
+ for (const name of tab.views ?? []) select(name);
7771
+ };
7772
+ return {
7773
+ activeKey,
7774
+ views,
7775
+ saveOpen,
7776
+ draft,
7777
+ renderTabs: (tabs) => renderTableTabs(tabs, activeTab, applyTab),
7778
+ renderViews: () => renderTableViews({
7779
+ config: options.config(),
7780
+ views,
7781
+ activeKey,
7782
+ saveOpen,
7783
+ draft,
7784
+ onSelect: select,
7785
+ onSave: save,
7786
+ onDelete: remove
7787
+ })
7788
+ };
7789
+ }
7790
+ function renderTableViews(ctx) {
7791
+ if (!ctx.config) return null;
7792
+ return h(
7793
+ "div",
7794
+ {
7795
+ "data-iris-table-views-bar": "",
7796
+ style: {
7797
+ display: "flex",
7798
+ alignItems: "center",
7799
+ gap: "var(--iris-space-xxs, 4px)",
7800
+ padding: "var(--iris-space-xxs, 4px) 0"
7801
+ }
7802
+ },
7803
+ [
7804
+ h(
7805
+ "select",
7806
+ {
7807
+ "data-iris-table-views": "",
7808
+ value: ctx.saveOpen.value ? TABLE_VIEWS_SAVE_ITEM : ctx.activeKey.value ?? "",
7809
+ "aria-label": "Table views",
7810
+ onChange: (event) => {
7811
+ const value = event.target.value;
7812
+ if (value === TABLE_VIEWS_SAVE_ITEM) {
7813
+ ctx.draft.value = "";
7814
+ ctx.saveOpen.value = true;
7815
+ } else if (value !== "") {
7816
+ ctx.onSelect(value);
7817
+ }
7818
+ },
7819
+ style: {
7820
+ border: "1px solid var(--iris-border)",
7821
+ borderRadius: "var(--iris-radius-sm, 4px)",
7822
+ background: "var(--iris-surface)",
7823
+ color: "var(--iris-foreground)",
7824
+ font: "inherit",
7825
+ fontSize: "var(--iris-font-size-sm, 13px)",
7826
+ padding: "0 var(--iris-space-xxs, 4px)",
7827
+ maxWidth: "180px"
7828
+ }
7829
+ },
7830
+ [
7831
+ ctx.activeKey.value === null && !ctx.saveOpen.value ? h("option", { value: "", disabled: true }, "Select view") : null,
7832
+ ...ctx.views.value.map(
7833
+ (view) => h(
7834
+ "option",
7835
+ { key: view.name, value: view.name },
7836
+ ctx.config?.label?.(view.name) ?? view.name
7837
+ )
7838
+ ),
7839
+ h("option", { value: TABLE_VIEWS_SAVE_ITEM }, "\uFF0B Save view")
7840
+ ]
7841
+ ),
7842
+ ctx.saveOpen.value ? h("input", {
7843
+ "data-iris-views-save": "",
7844
+ type: "text",
7845
+ value: ctx.draft.value,
7846
+ placeholder: "View name",
7847
+ "aria-label": "Save view",
7848
+ onInput: (event) => {
7849
+ ctx.draft.value = event.target.value;
7850
+ },
7851
+ onKeydown: (event) => {
7852
+ if (event.key === "Enter") {
7853
+ event.preventDefault();
7854
+ if (ctx.draft.value.trim()) ctx.onSave(ctx.draft.value);
7855
+ ctx.saveOpen.value = false;
7856
+ } else if (event.key === "Escape") {
7857
+ ctx.saveOpen.value = false;
7858
+ }
7859
+ },
7860
+ onBlur: () => {
7861
+ ctx.saveOpen.value = false;
7862
+ },
7863
+ style: {
7864
+ border: "1px solid var(--iris-border)",
7865
+ borderRadius: "var(--iris-radius-sm, 4px)",
7866
+ padding: "var(--iris-space-xxs, 4px)",
7867
+ width: "120px",
7868
+ font: "inherit"
7869
+ }
7870
+ }) : null,
7871
+ ctx.activeKey.value !== null ? h(
7872
+ "button",
7873
+ {
7874
+ type: "button",
7875
+ "data-iris-table-views-delete": "",
7876
+ "aria-label": "Delete view",
7877
+ onClick: () => ctx.onDelete(ctx.activeKey.value),
7878
+ style: {
7879
+ border: "none",
7880
+ background: "transparent",
7881
+ cursor: "pointer",
7882
+ color: "var(--iris-muted)",
7883
+ font: "inherit",
7884
+ padding: "0 var(--iris-space-xxs, 4px)"
7885
+ }
7886
+ },
7887
+ "\xD7"
7888
+ ) : null
7889
+ ]
7890
+ );
7891
+ }
7892
+ function renderTableTabs(tabs, activeTab, onApply) {
7893
+ const unique = uniqueTableTabs(tabs ?? []);
7894
+ if (unique.length === 0) return null;
7895
+ return h(
7896
+ "div",
7897
+ {
7898
+ role: "tablist",
7899
+ "data-iris-table-tabs": "",
7900
+ style: {
7901
+ display: "flex",
7902
+ alignItems: "center",
7903
+ gap: "var(--iris-space-xs, 8px)",
7904
+ padding: "var(--iris-space-xs, 8px) var(--iris-space-sm, 12px)",
7905
+ border: "1px solid var(--iris-border)",
7906
+ borderBottom: "none",
7907
+ borderTopLeftRadius: "var(--iris-radius-md, 6px)",
7908
+ borderTopRightRadius: "var(--iris-radius-md, 6px)",
7909
+ background: "var(--iris-surface)"
7910
+ }
7911
+ },
7912
+ unique.map(
7913
+ (tab) => h(
7914
+ "button",
7915
+ {
7916
+ key: tab.key,
7917
+ type: "button",
7918
+ role: "tab",
7919
+ "data-iris-table-tab": tab.key,
7920
+ "aria-selected": activeTab.value === tab.key ? "true" : "false",
7921
+ onClick: () => onApply(tab),
7922
+ style: {
7923
+ border: "none",
7924
+ background: "transparent",
7925
+ color: activeTab.value === tab.key ? "var(--iris-primary)" : "var(--iris-foreground)",
7926
+ cursor: "pointer",
7927
+ font: "inherit",
7928
+ fontWeight: activeTab.value === tab.key ? "600" : "400",
7929
+ padding: "0 var(--iris-space-xxs, 4px)"
7930
+ }
7931
+ },
7932
+ tab.label
7933
+ )
7934
+ )
7935
+ );
5943
7936
  }
7937
+
7938
+ // src/primitives/table/Table.ts
5944
7939
  var IrisTable = defineComponent({
5945
7940
  name: "IrisTable",
5946
7941
  inheritAttrs: false,
5947
- props: {
5948
- columns: {
5949
- type: Array,
5950
- required: true
5951
- },
5952
- data: {
5953
- type: Array,
5954
- required: false,
5955
- default: void 0
5956
- },
5957
- rowKey: { type: String, default: "id" },
5958
- ...tableControlProps,
5959
- striped: { type: Boolean, default: false },
5960
- editConfig: {
5961
- type: Object,
5962
- default: void 0
5963
- },
5964
- bordered: { type: Boolean, default: true },
5965
- /** Enable per-column resize handles. Combine with `v-model:columnWidths` for persistence. */
5966
- resizableColumns: { type: Boolean, default: false },
5967
- /** Controlled column widths in px. */
5968
- columnWidths: {
5969
- type: Object,
5970
- default: void 0
5971
- },
5972
- /** Enable virtual scrolling for the body. */
5973
- virtualScroll: {
5974
- type: Object,
5975
- default: void 0
5976
- },
5977
- /** Show the loading state instead of rows. */
5978
- loading: { type: Boolean, default: false },
5979
- /** Show the error state instead of rows (takes precedence over loading). */
5980
- error: { type: Boolean, default: false },
5981
- onRetry: { type: Function, default: void 0 },
5982
- /** Render only the horizontally-visible columns (+ pinned + overscan) for wide tables. */
5983
- columnVirtualization: { type: Boolean, default: false },
5984
- /**
5985
- * Enable WAI-ARIA grid keyboard navigation: the table becomes `role="grid"`
5986
- * and Arrow / Home / End / Page Up·Down move a roving cell focus across the
5987
- * data cells. Off by default; opt-in and additive (no effect on mouse / Tab
5988
- * behavior). Pairs best without virtualization (the focused cell must be
5989
- * rendered) and does not hijack keystrokes while a cell is being edited.
5990
- */
5991
- keyboardNavigation: { type: Boolean, default: false },
5992
- /**
5993
- * Enable rectangular cell-range selection (Excel-style). Click starts a
5994
- * range; Shift+Click or Shift+Arrow extends it; Escape clears it.
5995
- * Cells within the range get `data-iris-cell-selected="true"`.
5996
- */
5997
- cellRange: { type: Boolean, default: false },
5998
- /**
5999
- * Render an expandable detail panel beneath a row. Providing this adds a
6000
- * leading expand-toggle column; clicking it reveals a full-width detail row.
6001
- * (Not applied in the virtual-scroll path.)
6002
- */
6003
- renderDetail: {
6004
- type: Function,
6005
- default: void 0
6006
- },
6007
- /** Which rows can expand a detail panel. Defaults to all rows when `renderDetail` is set. */
6008
- rowExpandable: {
6009
- type: Function,
6010
- default: void 0
6011
- },
6012
- /** Initially-expanded row keys (uncontrolled). */
6013
- defaultExpandedRowKeys: {
6014
- type: Array,
6015
- default: void 0
6016
- },
6017
- /**
6018
- * Read a row's child rows to render the table as a TREE. Providing this
6019
- * enables tree mode: `data` is treated as the root rows, each row's first
6020
- * cell gains a depth indent + an expand/collapse toggle (when it has
6021
- * children), and the expand state reuses `defaultExpandedRowKeys` /
6022
- * `expandedRowsChange`. Column sort reorders siblings hierarchically (each
6023
- * level sorted, structure kept), and tree rows virtualize like flat rows
6024
- * when `virtualScroll` is set (unless `renderDetail` is also used, since
6025
- * detail panels are variable-height).
6026
- */
6027
- getSubRows: {
6028
- type: Function,
6029
- default: void 0
6030
- },
6031
- /**
6032
- * Cascade multi-row selection through the complete tree supplied by
6033
- * `getSubRows`. Selecting a branch selects every descendant (including
6034
- * collapsed rows); partially selected branches render indeterminate.
6035
- * Ignored outside tree mode and for non-multi selection.
6036
- */
6037
- treeSelectionCascade: { type: Boolean, default: false }
6038
- },
7942
+ props: tableProps,
6039
7943
  emits: {
6040
7944
  "update:selection": (_value) => true,
6041
7945
  "update:sort": (_value) => true,
7946
+ "update:multiSortState": (_value) => true,
7947
+ multiSortChange: (_value) => true,
6042
7948
  "update:columnWidths": (_value) => true,
7949
+ /** Controlled columnVisibility channel (parent owns the map). */
7950
+ "update:columnVisibility": (_value) => true,
6043
7951
  rowClick: (_row, _index) => true,
6044
7952
  rowDblclick: (_row, _index) => true,
6045
7953
  cellEdit: (_payload) => true,
6046
7954
  expandedRowsChange: (_keys) => true
6047
7955
  },
6048
- setup(props, { slots, attrs, emit }) {
6049
- if (typeof document !== "undefined" && !document.getElementById("iris-table-row-styles")) {
6050
- const style = document.createElement("style");
6051
- style.id = "iris-table-row-styles";
6052
- style.textContent = `
6053
- [data-iris-table] [role="row"]:hover {
6054
- --iris-cell-bg: var(--iris-surface-hover);
6055
- }
6056
- [data-iris-table-row-selected="true"] {
6057
- --iris-cell-bg: var(--iris-surface-selected);
6058
- }
6059
- `;
6060
- document.head.appendChild(style);
6061
- }
7956
+ setup(props, { slots, attrs, emit, expose }) {
7957
+ ensureTableStyles();
6062
7958
  const { t } = useI18n();
6063
- const grouped = computed(() => props.columns.some((c) => c.children && c.children.length > 0));
7959
+ const densityState = ref("comfortable");
7960
+ const effectiveDensity = computed(() => {
7961
+ if (props.densityToggle) return densityState.value;
7962
+ return props.density === "compact" || props.density === "cozy" ? props.density : "comfortable";
7963
+ });
7964
+ const cycleDensity = () => {
7965
+ densityState.value = densityState.value === "comfortable" ? "compact" : densityState.value === "compact" ? "cozy" : "comfortable";
7966
+ };
7967
+ const importController = useTableImport(
7968
+ () => props.importPreview,
7969
+ () => props.toolbar?.onImport
7970
+ );
7971
+ const responsiveWidth = ref(0);
7972
+ const internalWidths = ref({});
7973
+ const effectiveWidths = computed(() => {
7974
+ if (props.columnWidths) return props.columnWidths;
7975
+ return internalWidths.value;
7976
+ });
7977
+ const sourceDisplayColumns = computed(() => {
7978
+ const vis = props.columnVisibility;
7979
+ if (!vis) return props.columns;
7980
+ return props.columns.filter((c) => vis[c.key] !== false);
7981
+ });
7982
+ const detectedTypes = ref({});
7983
+ const detectTypesDone = ref(false);
7984
+ const detectedDisplayColumns = computed(() => {
7985
+ if (!props.autoDetectTypes || Object.keys(detectedTypes.value).length === 0) {
7986
+ return sourceDisplayColumns.value;
7987
+ }
7988
+ return applyDetectedTableTypes(sourceDisplayColumns.value, detectedTypes.value);
7989
+ });
7990
+ const responsiveLeadingWidth = computed(
7991
+ () => (props.rowDrag ? DRAG_COL_WIDTH : 0) + (props.seq ? SEQ_COL_WIDTH : 0) + (props.renderDetail !== void 0 ? EXPAND_COL_WIDTH : 0) + (props.selectable !== "none" ? SELECTION_COL_WIDTH : 0)
7992
+ );
7993
+ const responsiveWidthOf = (column) => {
7994
+ const width = effectiveWidths.value[column.key] ?? resolveInitialWidth(column);
7995
+ return Number.isFinite(width) && width >= 0 ? width : resolveInitialWidth(column);
7996
+ };
7997
+ const responsiveResult = computed(
7998
+ () => props.responsive ? computeResponsiveTableColumns(
7999
+ detectedDisplayColumns.value,
8000
+ responsiveWidth.value,
8001
+ responsiveLeadingWidth.value,
8002
+ responsiveWidthOf
8003
+ ) : { columns: detectedDisplayColumns.value, overflow: false }
8004
+ );
8005
+ const responsiveDisplayColumns = computed(
8006
+ () => responsiveResult.value.columns
8007
+ );
8008
+ const responsiveOverflow = computed(() => responsiveResult.value.overflow);
8009
+ const displayColumns = computed(
8010
+ () => responsiveDisplayColumns.value
8011
+ );
8012
+ const grouped = computed(
8013
+ () => displayColumns.value.some((c) => c.children && c.children.length > 0)
8014
+ );
6064
8015
  const leafColumns = computed(
6065
- () => grouped.value ? flattenLeafColumns(props.columns) : props.columns
8016
+ () => grouped.value ? flattenLeafColumns(displayColumns.value) : displayColumns.value
8017
+ );
8018
+ const headerMatrix = computed(
8019
+ () => grouped.value ? buildHeaderMatrix(displayColumns.value) : null
8020
+ );
8021
+ const remoteSort = computed(() => props.proxyConfig?.remoteSort === true);
8022
+ const remoteFilter = computed(() => props.proxyConfig?.remoteFilter === true);
8023
+ const proxyCtrl = useTableProxy({
8024
+ proxyConfig: () => props.proxyConfig,
8025
+ remoteSort,
8026
+ remoteFilter,
8027
+ multiSort: () => props.multiSort,
8028
+ sort: () => props.sort,
8029
+ defaultSort: props.defaultSort,
8030
+ multiSortState: () => props.multiSortState,
8031
+ defaultMultiSort: props.defaultMultiSort,
8032
+ filters: () => props.filters,
8033
+ filterValues: () => props.filterValues
8034
+ });
8035
+ const tableLoading = computed(
8036
+ () => proxyCtrl.proxy.value ? proxyCtrl.state.value.loading : props.loading
8037
+ );
8038
+ const tableError = computed(
8039
+ () => proxyCtrl.proxy.value ? proxyCtrl.state.value.error !== null : props.error
8040
+ );
8041
+ const handleRetry = () => {
8042
+ if (proxyCtrl.proxy.value) void proxyCtrl.proxy.value.refetch();
8043
+ props.onRetry?.();
8044
+ };
8045
+ const retry = computed(() => proxyCtrl.proxy.value ? handleRetry : props.onRetry);
8046
+ const localRowsOverride = ref(null);
8047
+ watch(
8048
+ () => props.data,
8049
+ () => {
8050
+ if (localRowsOverride.value !== null) localRowsOverride.value = null;
8051
+ }
8052
+ );
8053
+ const tableData = computed(
8054
+ () => proxyCtrl.proxy.value ? proxyCtrl.liveData.value : localRowsOverride.value ?? props.data ?? []
8055
+ );
8056
+ watch(
8057
+ [() => props.autoDetectTypes, tableData, () => props.columns],
8058
+ () => {
8059
+ if (!props.autoDetectTypes || detectTypesDone.value || tableData.value.length === 0) return;
8060
+ detectTypesDone.value = true;
8061
+ const next = {};
8062
+ for (const column of flattenLeafColumns(props.columns)) {
8063
+ const candidate = column;
8064
+ if (candidate.formula) continue;
8065
+ next[column.key] = detectColumnType(
8066
+ tableData.value.map((row) => getCellValue(row, candidate))
8067
+ );
8068
+ }
8069
+ detectedTypes.value = next;
8070
+ },
8071
+ { immediate: true }
6066
8072
  );
6067
- const headerMatrix = computed(() => grouped.value ? buildHeaderMatrix(props.columns) : null);
6068
8073
  const {
6069
8074
  sortState: internalSort,
6070
8075
  cycleSort,
8076
+ setSort,
6071
8077
  sortComparator,
6072
- sortedData: sortedRows
6073
- } = useTableSort(
6074
- computed(() => props.data ?? []),
6075
- {
6076
- leafColumns,
6077
- sort: computed(() => props.sort),
6078
- defaultSort: props.defaultSort,
6079
- onSortChange: (next) => emit("update:sort", next)
8078
+ sortedData: sortedRows,
8079
+ multiSortState,
8080
+ cycleMultiSort
8081
+ } = useTableSort(tableData, {
8082
+ leafColumns,
8083
+ sort: computed(() => props.sort),
8084
+ defaultSort: props.defaultSort,
8085
+ onSortChange: (next) => {
8086
+ emit("update:sort", next);
8087
+ if (remoteSort.value) proxyCtrl.setParams({ sort: next });
8088
+ },
8089
+ multiSort: () => props.multiSort,
8090
+ multiSortState: () => props.multiSortState,
8091
+ defaultMultiSort: props.defaultMultiSort,
8092
+ onMultiSortChange: (next) => {
8093
+ emit("update:multiSortState", next);
8094
+ emit("multiSortChange", next);
8095
+ if (remoteSort.value) proxyCtrl.setParams({ sorts: next });
8096
+ }
8097
+ });
8098
+ const tableViews = createTableViewsController({
8099
+ config: () => props.views,
8100
+ sort: internalSort,
8101
+ setSort,
8102
+ onActiveViewChange: (key) => props.onActiveViewChange?.(key)
8103
+ });
8104
+ const sortedData = computed(() => remoteSort.value ? tableData.value : sortedRows.value);
8105
+ watch([internalSort, () => props.multiSort, remoteSort, () => proxyCtrl.proxy.value], () => {
8106
+ if (!proxyCtrl.proxy.value || !remoteSort.value || props.multiSort) return;
8107
+ proxyCtrl.setParams({ sort: internalSort.value });
8108
+ });
8109
+ watch([multiSortState, () => props.multiSort, remoteSort, () => proxyCtrl.proxy.value], () => {
8110
+ if (!proxyCtrl.proxy.value || !remoteSort.value || !props.multiSort) return;
8111
+ proxyCtrl.setParams({ sorts: multiSortState.value });
8112
+ });
8113
+ const formDraft = ref({});
8114
+ const formApplied = ref({});
8115
+ const formFieldSignature = computed(
8116
+ () => (props.formConfig?.fields ?? []).map((f) => `${f.key}=${f.defaultValue ?? ""}`).join("\0")
8117
+ );
8118
+ watch(
8119
+ formFieldSignature,
8120
+ () => {
8121
+ formDraft.value = seedFormValues(props.formConfig?.fields);
8122
+ formApplied.value = {};
8123
+ },
8124
+ { immediate: true }
8125
+ );
8126
+ const setFormValue = (key, value) => {
8127
+ if (formDraft.value[key] === value) return;
8128
+ formDraft.value = { ...formDraft.value, [key]: value };
8129
+ };
8130
+ const mergedProxyFilters = (form) => mergeFilterValues(mergeFormFilters(props.filters ?? {}, form), props.filterValues ?? {});
8131
+ const handleFormSubmit = () => {
8132
+ const values = buildFormValues(props.formConfig?.fields, formDraft.value);
8133
+ props.formConfig?.onSearch?.(values);
8134
+ formApplied.value = values;
8135
+ if (proxyCtrl.proxy.value) {
8136
+ proxyCtrl.setParams({ filters: mergedProxyFilters(values), page: 1 });
8137
+ }
8138
+ };
8139
+ const handleFormReset = () => {
8140
+ const defaults = seedFormValues(props.formConfig?.fields);
8141
+ formDraft.value = defaults;
8142
+ const values = buildFormValues(props.formConfig?.fields, defaults);
8143
+ formApplied.value = values;
8144
+ props.formConfig?.onReset?.(values);
8145
+ if (proxyCtrl.proxy.value) {
8146
+ if (!proxyCtrl.setParams({ filters: mergedProxyFilters(values), page: 1 })) {
8147
+ void proxyCtrl.refetch();
8148
+ }
6080
8149
  }
8150
+ };
8151
+ watch(
8152
+ [formApplied, () => props.filters, () => props.filterValues],
8153
+ () => {
8154
+ if (proxyCtrl.proxy.value && remoteFilter.value) {
8155
+ proxyCtrl.setParams({
8156
+ filters: mergedProxyFilters(formApplied.value)
8157
+ });
8158
+ }
8159
+ },
8160
+ { immediate: true }
6081
8161
  );
6082
8162
  const selControlled = computed(() => props.selection !== void 0);
6083
8163
  const selectionModel = createSelectionModel({
@@ -6120,18 +8200,56 @@ var IrisTable = defineComponent({
6120
8200
  if (typeof v === "string" || typeof v === "number") return v;
6121
8201
  return index;
6122
8202
  };
6123
- const treeMode = computed(() => props.getSubRows !== void 0);
8203
+ const treeMode = computed(() => props.getSubRows !== void 0 || props.lazyLoad !== void 0);
8204
+ const lazyChildren = ref(/* @__PURE__ */ new Map());
8205
+ const lazyLoading = ref(/* @__PURE__ */ new Set());
8206
+ let lazyEpoch = 0;
8207
+ watch(
8208
+ () => proxyCtrl.proxy.value ? proxyCtrl.state.value.data : props.data,
8209
+ () => {
8210
+ lazyChildren.value = /* @__PURE__ */ new Map();
8211
+ lazyLoading.value = /* @__PURE__ */ new Set();
8212
+ lazyEpoch += 1;
8213
+ }
8214
+ );
8215
+ const lazyChildrenOf = (row) => {
8216
+ const key = String(row[props.rowKey]);
8217
+ return lazyChildren.value.get(key) ?? props.getSubRows?.(row);
8218
+ };
6124
8219
  const flatTree = computed(
6125
- () => treeMode.value ? flattenTree(sortedRows.value, {
8220
+ () => treeMode.value ? flattenTree(sortedData.value, {
6126
8221
  getKey: (r) => String(r[props.rowKey]),
6127
8222
  // With an active sort, sort each level's children by the same
6128
- // comparator so the whole tree reorders hierarchically.
6129
- getChildren: sortComparator.value ? withSortedChildren((r) => props.getSubRows(r), sortComparator.value) : (r) => props.getSubRows(r),
8223
+ // comparator so the whole tree reorders hierarchically. Lazy-loaded
8224
+ // children win over `getSubRows` and still participate.
8225
+ getChildren: sortComparator.value ? withSortedChildren(lazyChildrenOf, sortComparator.value) : lazyChildrenOf,
6130
8226
  isExpanded: (k) => expandedKeys.value.includes(k)
6131
8227
  }) : null
6132
8228
  );
8229
+ const filteredData = computed(() => {
8230
+ if (remoteFilter.value) return sortedData.value;
8231
+ const merged = proxyCtrl.proxy.value ? props.filters ?? {} : mergeFormFilters(props.filters ?? {}, formApplied.value);
8232
+ const active = Object.entries(merged).filter(([, v]) => v != null && v !== "");
8233
+ const checkedEntries = Object.entries(props.filterValues ?? {}).filter(
8234
+ ([, values]) => values.length > 0
8235
+ );
8236
+ if (active.length === 0 && checkedEntries.length === 0) return sortedData.value;
8237
+ return sortedData.value.filter((row) => {
8238
+ const textOk = active.every(([key, value]) => {
8239
+ const col = displayColumns.value.find((c) => c.key === key);
8240
+ if (!col) return true;
8241
+ return String(getCellValue(row, col) ?? "").toLowerCase().includes(value.toLowerCase());
8242
+ });
8243
+ const setsOk = checkedEntries.every(([key, values]) => {
8244
+ const col = displayColumns.value.find((c) => c.key === key);
8245
+ if (!col) return true;
8246
+ return values.includes(String(getCellValue(row, col) ?? ""));
8247
+ });
8248
+ return textOk && setsOk;
8249
+ });
8250
+ });
6133
8251
  const bodyData = computed(
6134
- () => flatTree.value ? flatTree.value.map((t2) => t2.row) : sortedRows.value
8252
+ () => flatTree.value ? flatTree.value.map((t2) => t2.row) : filteredData.value
6135
8253
  );
6136
8254
  const cascadingTreeSelection = computed(
6137
8255
  () => props.treeSelectionCascade && props.selectable === "multi" && treeMode.value
@@ -6153,7 +8271,7 @@ var IrisTable = defineComponent({
6153
8271
  if (children && children.length > 0) walk(children, key);
6154
8272
  }
6155
8273
  };
6156
- walk(sortedRows.value);
8274
+ walk(sortedData.value);
6157
8275
  return nodes;
6158
8276
  });
6159
8277
  const compactTreeSelectionSeed = (keys) => {
@@ -6226,25 +8344,27 @@ var IrisTable = defineComponent({
6226
8344
  selectionModel.set([...allRowIds.value]);
6227
8345
  };
6228
8346
  const editingCellId = ref(null);
8347
+ const editingColumnKey = ref(null);
6229
8348
  const editingDraft = ref("");
6230
8349
  const editError = ref(null);
6231
8350
  const editorInputRef = ref(null);
6232
- const cellId = (rowIdent, colKey) => `${rowIdent}::${colKey}`;
6233
8351
  const beginEdit = (row, column, rowIdent) => {
6234
8352
  if (!column.editable) return;
6235
8353
  editingCellId.value = cellId(rowIdent, column.key);
6236
- const current = getCellValue2(row, column);
8354
+ editingColumnKey.value = column.key;
8355
+ const current = getCellValue(row, column);
6237
8356
  editingDraft.value = current == null ? "" : String(current);
6238
8357
  editError.value = null;
6239
8358
  void nextTick(() => editorInputRef.value?.focus());
6240
8359
  };
6241
8360
  const commitEdit = (row, column, rowIndex) => {
6242
8361
  if (editingCellId.value === null) return;
6243
- const oldValue = getCellValue2(row, column);
8362
+ const oldValue = getCellValue(row, column);
6244
8363
  const draft = editingDraft.value;
6245
8364
  const newValue = column.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? oldValue : Number(draft) : draft;
6246
8365
  if (column.editRules && column.editRules.length > 0) {
6247
- validateEditRulesAsync(column.editRules, draft, row).then((r) => {
8366
+ const context = { rows: tableData.value, columnKey: column.key };
8367
+ validateEditRulesAsync(column.editRules, draft, row, false, context).then((r) => {
6248
8368
  if (!r.valid) {
6249
8369
  editError.value = r.messages[0] ?? null;
6250
8370
  return;
@@ -6260,74 +8380,279 @@ var IrisTable = defineComponent({
6260
8380
  return;
6261
8381
  }
6262
8382
  }
6263
- finishCommit(row, column, rowIndex, oldValue, newValue);
6264
- editError.value = null;
6265
- editingCellId.value = null;
6266
- if (newValue !== oldValue) {
6267
- emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
8383
+ finishCommit(row, column, rowIndex, oldValue, newValue);
8384
+ editError.value = null;
8385
+ editingCellId.value = null;
8386
+ if (newValue !== oldValue) {
8387
+ emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
8388
+ }
8389
+ };
8390
+ const writeCellValue = (row, column, rowIndex, oldValue, newValue) => {
8391
+ if (newValue === oldValue) return;
8392
+ emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
8393
+ if (proxyCtrl.proxy.value) {
8394
+ const id = rowId(row, rowIndex);
8395
+ const idx = proxyCtrl.liveData.value.findIndex((r, i) => rowId(r, i) === id);
8396
+ if (idx >= 0) {
8397
+ const key = column.dataIndex ?? column.key;
8398
+ const next = { ...proxyCtrl.liveData.value[idx], [key]: newValue };
8399
+ proxyCtrl.liveData.value = [
8400
+ ...proxyCtrl.liveData.value.slice(0, idx),
8401
+ next,
8402
+ ...proxyCtrl.liveData.value.slice(idx + 1)
8403
+ ];
8404
+ }
8405
+ }
8406
+ };
8407
+ const finishCommit = (row, column, rowIndex, oldValue, newValue) => {
8408
+ editError.value = null;
8409
+ editingCellId.value = null;
8410
+ editingColumnKey.value = null;
8411
+ writeCellValue(row, column, rowIndex, oldValue, newValue);
8412
+ };
8413
+ const cancelEdit = () => {
8414
+ editError.value = null;
8415
+ editingCellId.value = null;
8416
+ editingColumnKey.value = null;
8417
+ };
8418
+ const rowMode = computed(() => props.editConfig?.mode === "row");
8419
+ const rowSessions = ref(/* @__PURE__ */ new Map());
8420
+ const rowEditing = ref(null);
8421
+ const rowEditorRefs = /* @__PURE__ */ new Map();
8422
+ const currentRowFor = (k) => bodyData.value.find((r, i) => rowId(r, i) === k);
8423
+ const beginRowEdit = (row, rowIndex, focusColKey) => {
8424
+ const k = rowId(row, rowIndex);
8425
+ const editableCols = leafColumns.value.filter((c) => c.editable);
8426
+ if (editableCols.length === 0) return;
8427
+ const sessions = /* @__PURE__ */ new Map();
8428
+ for (const col of editableCols) {
8429
+ const current = getCellValue(row, col);
8430
+ sessions.set(cellId(k, col.key), {
8431
+ draft: current == null ? "" : String(current),
8432
+ error: null
8433
+ });
8434
+ }
8435
+ rowSessions.value = sessions;
8436
+ rowEditing.value = { k, idx: rowIndex };
8437
+ const focusKey = focusColKey && editableCols.some((c) => c.key === focusColKey) ? focusColKey : editableCols[0].key;
8438
+ void nextTick(() => rowEditorRefs.get(focusKey)?.focus());
8439
+ };
8440
+ const commitRowSession = (k, column, rowIndex, id) => {
8441
+ const session = rowSessions.value.get(id);
8442
+ if (!session) return;
8443
+ const row = currentRowFor(k);
8444
+ if (!row) return;
8445
+ const oldValue = getCellValue(row, column);
8446
+ const draft = session.draft;
8447
+ const newValue = column.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? oldValue : Number(draft) : draft;
8448
+ if (column.editRules && column.editRules.length > 0) {
8449
+ const context = { rows: tableData.value, columnKey: column.key };
8450
+ validateEditRulesAsync(column.editRules, draft, row, false, context).then((r) => {
8451
+ if (!rowSessions.value.has(id)) return;
8452
+ if (!r.valid) {
8453
+ const s = rowSessions.value.get(id);
8454
+ if (s) s.error = r.messages[0] ?? null;
8455
+ return;
8456
+ }
8457
+ finishRowCommit(row, column, rowIndex, oldValue, newValue, id);
8458
+ });
8459
+ return;
8460
+ }
8461
+ if (column.validate) {
8462
+ const error = column.validate(newValue, row);
8463
+ if (error) {
8464
+ const s = rowSessions.value.get(id);
8465
+ if (s) s.error = error;
8466
+ return;
8467
+ }
8468
+ }
8469
+ finishRowCommit(row, column, rowIndex, oldValue, newValue, id);
8470
+ };
8471
+ const finishRowCommit = (row, column, rowIndex, oldValue, newValue, id) => {
8472
+ rowSessions.value.delete(id);
8473
+ if (rowSessions.value.size === 0) rowEditing.value = null;
8474
+ writeCellValue(row, column, rowIndex, oldValue, newValue);
8475
+ };
8476
+ const cancelRowEdit = () => {
8477
+ rowSessions.value = /* @__PURE__ */ new Map();
8478
+ rowEditing.value = null;
8479
+ };
8480
+ const switchRowEdit = (row, rowIndex, focusColKey) => {
8481
+ const editing = rowEditing.value;
8482
+ if (editing !== null) {
8483
+ for (const [id] of rowSessions.value) {
8484
+ const colKey = id.slice(id.indexOf("::") + 2);
8485
+ const col = leafColumns.value.find((c) => c.key === colKey);
8486
+ if (!col) continue;
8487
+ commitRowSession(editing.k, col, editing.idx, id);
8488
+ if (rowSessions.value.get(id)?.error != null) return;
8489
+ }
8490
+ }
8491
+ beginRowEdit(row, rowIndex, focusColKey);
8492
+ };
8493
+ const handleRowModeCellClick = (row, col, index, k) => {
8494
+ if (rowEditing.value?.k === k) {
8495
+ const id = cellId(k, col.key);
8496
+ if (col.editable && !rowSessions.value.has(id)) {
8497
+ const current = getCellValue(row, col);
8498
+ rowSessions.value.set(id, {
8499
+ draft: current == null ? "" : String(current),
8500
+ error: null
8501
+ });
8502
+ void nextTick(() => rowEditorRefs.get(col.key)?.focus());
8503
+ }
8504
+ } else {
8505
+ switchRowEdit(row, index, col.key);
6268
8506
  }
6269
8507
  };
6270
- const finishCommit = (row, column, rowIndex, oldValue, newValue) => {
6271
- editError.value = null;
6272
- editingCellId.value = null;
6273
- if (newValue !== oldValue) {
6274
- emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
8508
+ const editorRenderContext = {
8509
+ rowEditorRefs,
8510
+ editorInputRef,
8511
+ editingDraft,
8512
+ editError,
8513
+ commitEdit,
8514
+ cancelEdit,
8515
+ commitRowSession,
8516
+ cancelRowEdit,
8517
+ editPreview: props.editPreview,
8518
+ previewValue: (row, col, draft) => {
8519
+ const raw = col.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? getCellValue(row, col) : Number(draft) : draft;
8520
+ return col.formatter?.(applyTableMask(raw, col), row) ?? "";
6275
8521
  }
6276
8522
  };
6277
- const cancelEdit = () => {
6278
- editError.value = null;
6279
- editingCellId.value = null;
6280
- };
6281
- const internalWidths = ref({});
8523
+ const buildRowSessionContent = (row, col, index, k, editCellId, session) => renderRowSessionContent(editorRenderContext, row, col, index, k, editCellId, session);
8524
+ const buildCellEditContent = (row, col, index, editCellId) => renderCellEditContent(editorRenderContext, row, col, index, editCellId);
8525
+ const buildTreeIndent = (treeMeta, row) => h(
8526
+ "span",
8527
+ {
8528
+ "data-iris-table-tree-indent": "",
8529
+ style: {
8530
+ display: "inline-flex",
8531
+ alignItems: "center",
8532
+ flex: "none",
8533
+ paddingLeft: `${treeMeta.depth * 16}px`
8534
+ }
8535
+ },
8536
+ treeMeta.hasChildren || props.lazyLoad !== void 0 && !lazyChildren.value.has(treeMeta.key) ? [
8537
+ h(
8538
+ "button",
8539
+ {
8540
+ type: "button",
8541
+ "data-iris-table-tree-toggle": "",
8542
+ "data-iris-tree-loading": lazyLoading.value.has(treeMeta.key) ? "" : void 0,
8543
+ "aria-expanded": treeMeta.expanded ? "true" : "false",
8544
+ "aria-label": t(treeMeta.expanded ? "treeSelect.collapse" : "treeSelect.expand"),
8545
+ onClick: (e) => {
8546
+ e.stopPropagation();
8547
+ if (treeMeta.hasChildren) {
8548
+ expansion.toggle(treeMeta.key);
8549
+ return;
8550
+ }
8551
+ if (lazyLoading.value.has(treeMeta.key)) return;
8552
+ lazyLoading.value = new Set(lazyLoading.value).add(treeMeta.key);
8553
+ const clearLoading = () => {
8554
+ const next = new Set(lazyLoading.value);
8555
+ next.delete(treeMeta.key);
8556
+ lazyLoading.value = next;
8557
+ };
8558
+ try {
8559
+ const epoch = lazyEpoch;
8560
+ props.lazyLoad(row, (children) => {
8561
+ if (epoch !== lazyEpoch) return;
8562
+ lazyChildren.value.set(treeMeta.key, children);
8563
+ if (children && children.length > 0) {
8564
+ expansion.toggle(treeMeta.key);
8565
+ }
8566
+ clearLoading();
8567
+ });
8568
+ } catch {
8569
+ clearLoading();
8570
+ }
8571
+ },
8572
+ style: {
8573
+ border: "none",
8574
+ background: "transparent",
8575
+ cursor: "pointer",
8576
+ padding: "0",
8577
+ marginRight: "4px",
8578
+ font: "inherit",
8579
+ color: "var(--iris-foreground)",
8580
+ transform: treeMeta.expanded ? "rotate(90deg)" : "none",
8581
+ transition: "transform 150ms"
8582
+ }
8583
+ },
8584
+ "\u25B6"
8585
+ )
8586
+ ] : [
8587
+ h("span", {
8588
+ "aria-hidden": "true",
8589
+ style: { display: "inline-block", width: "16px" }
8590
+ })
8591
+ ]
8592
+ );
6282
8593
  watch(
6283
8594
  () => leafColumns.value,
6284
8595
  (cols) => {
6285
8596
  const seeded = { ...internalWidths.value };
8597
+ let changed = false;
6286
8598
  for (const col of cols) {
6287
- if (seeded[col.key] === void 0) seeded[col.key] = resolveInitialWidth(col);
8599
+ if (seeded[col.key] === void 0) {
8600
+ seeded[col.key] = resolveInitialWidth(col);
8601
+ changed = true;
8602
+ }
6288
8603
  }
6289
- internalWidths.value = seeded;
8604
+ if (changed) internalWidths.value = seeded;
6290
8605
  },
6291
8606
  { immediate: true, deep: false }
6292
8607
  );
6293
- const effectiveWidths = computed(() => {
6294
- if (props.columnWidths) return props.columnWidths;
6295
- return internalWidths.value;
6296
- });
6297
8608
  const setColumnWidths = (next) => {
6298
8609
  if (props.columnWidths === void 0) internalWidths.value = next;
6299
8610
  emit("update:columnWidths", next);
6300
8611
  };
6301
8612
  const onHeaderClick = (column) => {
6302
- cycleSort(column);
6303
- };
6304
- const sortIndicator = (col) => {
6305
- if (!col.sortable) return null;
6306
- const state = internalSort.value;
6307
- const isActive = state?.key === col.key;
6308
- const direction = isActive ? state.direction : null;
6309
- const color = isActive ? "var(--iris-primary)" : "var(--iris-muted)";
8613
+ if (props.multiSort) cycleMultiSort(column);
8614
+ else cycleSort(column);
8615
+ };
8616
+ const multiSortSeq = (col) => {
8617
+ if (!props.multiSort) return null;
8618
+ const idx = multiSortState.value.findIndex((s) => s.key === col.key);
8619
+ if (idx <= 0) return null;
6310
8620
  return h(
6311
8621
  "span",
6312
8622
  {
6313
- "aria-hidden": "true",
8623
+ "data-iris-sort-seq": "",
6314
8624
  style: {
6315
- display: "inline-flex",
6316
- flexDirection: "column",
6317
8625
  marginInlineStart: "var(--iris-space-xxs, 4px)",
6318
- lineHeight: "0.6",
6319
8626
  fontSize: "var(--iris-font-size-xs, 12px)",
6320
- color
8627
+ color: "var(--iris-muted)"
6321
8628
  }
6322
8629
  },
6323
- [
6324
- h("span", { style: { opacity: direction === "asc" ? "1" : "0.45" } }, "\u25B2"),
6325
- h("span", { style: { opacity: direction === "desc" ? "1" : "0.45" } }, "\u25BC")
6326
- ]
8630
+ String(idx + 1)
6327
8631
  );
6328
8632
  };
8633
+ const ariaSortFor = (col) => {
8634
+ const state = props.multiSort ? multiSortState.value.find((s) => s.key === col.key) ?? null : internalSort.value;
8635
+ if (state?.key === col.key) return state.direction === "asc" ? "ascending" : "descending";
8636
+ return col.sortable ? "none" : void 0;
8637
+ };
8638
+ const sortIndicator = (col) => renderTableSortIndicator(col, {
8639
+ multiSort: props.multiSort,
8640
+ multiSortState: multiSortState.value,
8641
+ sort: internalSort.value
8642
+ });
8643
+ const renderFilterTrigger = (col, leaf) => renderTableFilterTrigger({
8644
+ column: col,
8645
+ leaf,
8646
+ active: (props.filterValues?.[col.key]?.length ?? 0) > 0,
8647
+ open: filterPanelState.value?.open === true && filterPanelState.value.colKey === col.key,
8648
+ label: t("table.filter"),
8649
+ onOpen: openFilterPanel
8650
+ });
8651
+ const leadTrackCount = () => (props.rowDrag ? 1 : 0) + (props.seq ? 1 : 0) + (hasDetail.value ? 1 : 0) + (props.selectable !== "none" ? 1 : 0);
6329
8652
  const gridTemplate = computed(() => {
6330
8653
  const parts = [];
8654
+ if (props.rowDrag) parts.push(`${DRAG_COL_WIDTH}px`);
8655
+ if (props.seq) parts.push(`${SEQ_COL_WIDTH}px`);
6331
8656
  if (hasDetail.value) parts.push(`${EXPAND_COL_WIDTH}px`);
6332
8657
  if (props.selectable !== "none") parts.push(`${SELECTION_COL_WIDTH}px`);
6333
8658
  for (const col of leafColumns.value) {
@@ -6342,70 +8667,45 @@ var IrisTable = defineComponent({
6342
8667
  cellRangeState.value = s;
6343
8668
  })
6344
8669
  );
6345
- const isInRange = (row, col) => {
6346
- const { anchor, active } = cellRangeState.value;
6347
- if (!anchor || !active) return false;
6348
- const minRow = Math.min(anchor.row, active.row);
6349
- const maxRow = Math.max(anchor.row, active.row);
6350
- const minCol = Math.min(anchor.col, active.col);
6351
- const maxCol = Math.max(anchor.col, active.col);
6352
- return row >= minRow && row <= maxRow && col >= minCol && col <= maxCol;
6353
- };
6354
8670
  const rootRef = ref(null);
8671
+ let responsiveObserver = null;
8672
+ const measureResponsiveWidth = () => {
8673
+ responsiveWidth.value = props.responsive && rootRef.value ? rootRef.value.clientWidth : 0;
8674
+ };
8675
+ const updateResponsiveObserver = () => {
8676
+ responsiveObserver?.disconnect();
8677
+ responsiveObserver = null;
8678
+ responsiveWidth.value = 0;
8679
+ if (!props.responsive || !rootRef.value || typeof ResizeObserver === "undefined") return;
8680
+ measureResponsiveWidth();
8681
+ responsiveObserver = new ResizeObserver(measureResponsiveWidth);
8682
+ responsiveObserver.observe(rootRef.value);
8683
+ };
8684
+ onMounted(updateResponsiveObserver);
8685
+ watch(() => props.responsive, updateResponsiveObserver);
8686
+ onBeforeUnmount(() => {
8687
+ responsiveObserver?.disconnect();
8688
+ responsiveObserver = null;
8689
+ });
8690
+ const rowTarget = createTableRowTarget(() => rootRef.value);
8691
+ const { scrollTo: scrollToRow, goTo: goToRow } = rowTarget;
8692
+ onBeforeUnmount(rowTarget.dispose);
6355
8693
  const focusedCell = ref(null);
6356
- const GRID_NAV_KEYS = /* @__PURE__ */ new Set([
6357
- "ArrowUp",
6358
- "ArrowDown",
6359
- "ArrowLeft",
6360
- "ArrowRight",
6361
- "Home",
6362
- "End",
6363
- "PageUp",
6364
- "PageDown"
6365
- ]);
6366
- const handleGridKey = (e) => {
6367
- if (!props.keyboardNavigation || !GRID_NAV_KEYS.has(e.key)) return;
6368
- const target = e.target;
6369
- if (target.dataset.gridRow === void 0) return;
6370
- e.preventDefault();
6371
- const current = focusedCell.value ?? { row: 0, col: 0 };
6372
- const next = nextGridCell(current, e.key, {
6373
- rowCount: bodyData.value.length,
6374
- colCount: leafColumns.value.length,
6375
- pageSize: 10
6376
- });
6377
- focusedCell.value = next;
6378
- const cell = rootRef.value?.querySelector(
6379
- `[data-grid-row="${next.row}"][data-grid-col="${next.col}"]`
6380
- );
6381
- cell?.focus();
6382
- };
6383
- const CELL_RANGE_ARROW_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
6384
- const handleCellRangeKey = (e) => {
6385
- if (!props.cellRange) return;
6386
- if (e.key === "Escape") {
6387
- cellRangeCtrl.clearRange();
6388
- return;
6389
- }
6390
- if (!e.shiftKey || !CELL_RANGE_ARROW_KEYS.has(e.key)) return;
6391
- const target = e.target;
6392
- const rowAttr = target.dataset.irisCellRow;
6393
- const colAttr = target.dataset.irisCellCol;
6394
- if (rowAttr === void 0 || colAttr === void 0) return;
6395
- e.preventDefault();
6396
- const anchor = cellRangeCtrl.getState().anchor;
6397
- const active = anchor ? cellRangeCtrl.getState().active ?? { row: Number(rowAttr), col: Number(colAttr) } : { row: Number(rowAttr), col: Number(colAttr) };
6398
- let nextRow = active.row;
6399
- let nextCol = active.col;
6400
- if (e.key === "ArrowUp") nextRow = Math.max(0, nextRow - 1);
6401
- else if (e.key === "ArrowDown") nextRow = Math.min(bodyData.value.length - 1, nextRow + 1);
6402
- else if (e.key === "ArrowLeft") nextCol = Math.max(0, nextCol - 1);
6403
- else nextCol = Math.min(leafColumns.value.length - 1, nextCol + 1);
6404
- cellRangeCtrl.extendRange(nextRow, nextCol);
6405
- };
8694
+ const keyboard = createTableKeyboard({
8695
+ keyboardNavigation: () => props.keyboardNavigation,
8696
+ cellRange: () => props.cellRange,
8697
+ clipConfig: () => props.clipConfig,
8698
+ rows: () => bodyData.value,
8699
+ columns: () => leafColumns.value,
8700
+ root: () => rootRef.value,
8701
+ focused: focusedCell,
8702
+ range: cellRangeCtrl,
8703
+ rangeState: cellRangeState
8704
+ });
8705
+ const { handleRootKeyDown, isInRange, activeCellRange, copyActiveRange } = keyboard;
6406
8706
  const scrollLeft = ref(0);
6407
8707
  const viewportWidth = ref(0);
6408
- const colTrack = (i) => (hasDetail.value ? 1 : 0) + (props.selectable !== "none" ? 2 : 1) + i;
8708
+ const colTrack = (i) => leadTrackCount() + 1 + i;
6409
8709
  if (typeof ResizeObserver !== "undefined") {
6410
8710
  let ro = null;
6411
8711
  onMounted(() => {
@@ -6422,27 +8722,19 @@ var IrisTable = defineComponent({
6422
8722
  ro = null;
6423
8723
  });
6424
8724
  }
6425
- const visibleColSet = computed(() => {
6426
- if (!props.columnVirtualization) return null;
6427
- const cols = leafColumns.value;
6428
- const w = computeVirtualRange({
6429
- itemCount: cols.length,
6430
- scrollTop: scrollLeft.value,
6431
- viewportSize: viewportWidth.value,
6432
- itemSize: (i) => effectiveWidths.value[cols[i].key] ?? resolveInitialWidth(cols[i]),
6433
- buffer: 2
6434
- });
6435
- const set = /* @__PURE__ */ new Set();
6436
- for (let i = w.startIndex; i <= w.endIndex; i += 1) set.add(i);
6437
- cols.forEach((col, i) => {
6438
- if (col.pinned) set.add(i);
6439
- });
6440
- return set;
6441
- });
8725
+ const visibleColSet = computed(
8726
+ () => computeVisibleColSet(
8727
+ props.columnVirtualization,
8728
+ leafColumns.value,
8729
+ scrollLeft.value,
8730
+ viewportWidth.value,
8731
+ effectiveWidths.value
8732
+ )
8733
+ );
6442
8734
  const pinnedOffsets = computed(() => {
6443
8735
  const map = {};
6444
8736
  const widthOf = (col) => effectiveWidths.value[col.key] ?? resolveInitialWidth(col);
6445
- let left = (hasDetail.value ? EXPAND_COL_WIDTH : 0) + (props.selectable !== "none" ? SELECTION_COL_WIDTH : 0);
8737
+ let left = (props.rowDrag ? DRAG_COL_WIDTH : 0) + (props.seq ? SEQ_COL_WIDTH : 0) + (hasDetail.value ? EXPAND_COL_WIDTH : 0) + (props.selectable !== "none" ? SELECTION_COL_WIDTH : 0);
6446
8738
  for (const col of leafColumns.value) {
6447
8739
  if (col.pinned === "left") {
6448
8740
  map[col.key] = { side: "left", offset: left };
@@ -6498,126 +8790,449 @@ var IrisTable = defineComponent({
6498
8790
  }
6499
8791
  });
6500
8792
  };
8793
+ const pinnedDragHandle = createTablePinnedDrag({
8794
+ enabled: () => props.pinnedDrag === true,
8795
+ columns: () => leafColumns.value,
8796
+ widthOf: (column) => effectiveWidths.value[column.key] ?? resolveInitialWidth(column),
8797
+ onColumnPinnedChange: props.onColumnPinnedChange,
8798
+ onPinnedCountChange: props.onPinnedCountChange
8799
+ });
8800
+ const rowDragCtrl = createSortable();
8801
+ const rowDragState = shallowRef(rowDragCtrl.getState());
8802
+ onBeforeUnmount(
8803
+ rowDragCtrl.subscribe((s) => {
8804
+ rowDragState.value = s;
8805
+ })
8806
+ );
8807
+ const colDragCtrl = createSortable();
8808
+ const colDragState = shallowRef(colDragCtrl.getState());
8809
+ onBeforeUnmount(
8810
+ colDragCtrl.subscribe((s) => {
8811
+ colDragState.value = s;
8812
+ })
8813
+ );
8814
+ const rowRectsRef = ref([]);
8815
+ const colRectsRef = ref([]);
8816
+ const handleRowDragPointerDown = (e, rowId2) => {
8817
+ if (!props.rowDrag || e.button !== 0) return;
8818
+ e.preventDefault();
8819
+ rowDragCtrl.press(rowId2, e.clientX, e.clientY);
8820
+ };
8821
+ const handleRowDragPointerMove = (e) => {
8822
+ if (!props.rowDrag) return;
8823
+ if (rowDragCtrl.isPending()) {
8824
+ const started = rowDragCtrl.tryStart(e.clientX, e.clientY);
8825
+ if (started) {
8826
+ const rects = [];
8827
+ rootRef.value?.querySelectorAll("[data-iris-row-drag-handle]").forEach((el) => {
8828
+ const r = el.getBoundingClientRect();
8829
+ const id = el.getAttribute("data-iris-row-drag-handle");
8830
+ if (id) rects.push({ id, left: r.left, top: r.top, width: r.width, height: r.height });
8831
+ });
8832
+ rowRectsRef.value = rects;
8833
+ }
8834
+ }
8835
+ if (rowDragCtrl.getState().activeId !== null) {
8836
+ rowDragCtrl.moveOver({ x: e.clientX, y: e.clientY }, rowRectsRef.value);
8837
+ }
8838
+ };
8839
+ const handleRowDragPointerUp = () => {
8840
+ if (!props.rowDrag) return;
8841
+ if (rowDragCtrl.isPending()) {
8842
+ rowDragCtrl.cancel();
8843
+ return;
8844
+ }
8845
+ const { activeId, overId } = rowDragCtrl.end();
8846
+ if (activeId !== null && overId !== null && activeId !== overId) {
8847
+ const rows = [...bodyData.value];
8848
+ const from = rows.findIndex((r, i) => String(rowId(r, i)) === activeId);
8849
+ const to = rows.findIndex((r, i) => String(rowId(r, i)) === overId);
8850
+ if (from >= 0 && to >= 0 && from !== to) {
8851
+ const [moved] = rows.splice(from, 1);
8852
+ rows.splice(to, 0, moved);
8853
+ if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
8854
+ else localRowsOverride.value = rows;
8855
+ props.onDataChange?.(rows);
8856
+ props.rowDrag.onReorder(rows);
8857
+ }
8858
+ }
8859
+ rowRectsRef.value = [];
8860
+ };
8861
+ const handleRowDragPointerLeave = () => {
8862
+ if (props.rowDrag && rowDragCtrl.getState().activeId !== null) {
8863
+ rowDragCtrl.cancel();
8864
+ }
8865
+ };
8866
+ const handleColDragPointerDown = (e, colKey) => {
8867
+ if (!props.columnDrag || e.button !== 0) return;
8868
+ e.preventDefault();
8869
+ e.stopPropagation();
8870
+ colDragCtrl.press(colKey, e.clientX, e.clientY);
8871
+ };
8872
+ const handleColDragPointerMove = (e) => {
8873
+ if (!props.columnDrag) return;
8874
+ if (colDragCtrl.isPending()) {
8875
+ const started = colDragCtrl.tryStart(e.clientX, e.clientY);
8876
+ if (started) {
8877
+ const rects = [];
8878
+ rootRef.value?.querySelectorAll("[data-iris-table-header]").forEach((el) => {
8879
+ const r = el.getBoundingClientRect();
8880
+ const id = el.getAttribute("data-iris-table-header");
8881
+ if (id) rects.push({ id, left: r.left, top: r.top, width: r.width, height: r.height });
8882
+ });
8883
+ colRectsRef.value = rects;
8884
+ }
8885
+ }
8886
+ if (colDragCtrl.getState().activeId !== null) {
8887
+ colDragCtrl.moveOver({ x: e.clientX, y: e.clientY }, colRectsRef.value);
8888
+ }
8889
+ };
8890
+ const handleColDragPointerUp = () => {
8891
+ if (!props.columnDrag) return;
8892
+ if (colDragCtrl.isPending()) {
8893
+ colDragCtrl.cancel();
8894
+ return;
8895
+ }
8896
+ const { activeId, overId } = colDragCtrl.end();
8897
+ if (activeId !== null && overId !== null && activeId !== overId) {
8898
+ const next = [...leafColumns.value];
8899
+ const from = next.findIndex((c) => c.key === activeId);
8900
+ const to = next.findIndex((c) => c.key === overId);
8901
+ if (from >= 0 && to >= 0 && from !== to) {
8902
+ const [moved] = next.splice(from, 1);
8903
+ next.splice(to, 0, moved);
8904
+ props.columnDrag.onReorder(next);
8905
+ }
8906
+ }
8907
+ colRectsRef.value = [];
8908
+ };
8909
+ const spanOccupy = /* @__PURE__ */ new Set();
8910
+ const tableExpose = {
8911
+ loadData: (rows) => {
8912
+ if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
8913
+ else localRowsOverride.value = rows;
8914
+ props.onDataChange?.(rows);
8915
+ },
8916
+ reloadData: () => {
8917
+ void proxyCtrl.refetch();
8918
+ },
8919
+ commitProxy: (overrides) => {
8920
+ proxyCtrl.setParams(overrides);
8921
+ },
8922
+ getProxyInfo: () => {
8923
+ if (!proxyCtrl.proxy.value) return null;
8924
+ const s = proxyCtrl.state.value;
8925
+ return { page: s.params.page, pageSize: s.params.pageSize, total: s.total };
8926
+ },
8927
+ removeRows: (keys) => {
8928
+ const { rows, removedKeys } = removeRowsFromList(tableData.value, props.rowKey, keys);
8929
+ if (removedKeys.size === 0) return;
8930
+ if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
8931
+ else localRowsOverride.value = rows;
8932
+ const selected = displaySelection.value;
8933
+ const nextSelection = selected.filter((key) => !removedKeys.has(key));
8934
+ if (nextSelection.length !== selected.length) {
8935
+ rebaseToProp();
8936
+ selectionModel.set(nextSelection);
8937
+ }
8938
+ props.onDataChange?.(rows);
8939
+ },
8940
+ getFilteredData: () => [...bodyData.value],
8941
+ exportCurrentViewCsv: () => exportCsv(bodyData.value, leafColumns.value),
8942
+ exportMultiCsv: () => {
8943
+ const current = exportCsv(bodyData.value, leafColumns.value);
8944
+ const names = props.exportNames;
8945
+ if (!names || names.length === 0) return current;
8946
+ const segments = [`# current${current ? `
8947
+ ${current}` : ""}`];
8948
+ for (const entry of names) {
8949
+ if (!entry.key) continue;
8950
+ const refCsv = toCsvRows(entry.ref());
8951
+ segments.push(`# ${entry.key}${refCsv ? `
8952
+ ${refCsv}` : ""}`);
8953
+ }
8954
+ return segments.join("\n\n");
8955
+ },
8956
+ compareStates,
8957
+ scrollToRow,
8958
+ goToRow
8959
+ };
8960
+ expose(tableExpose);
8961
+ const buildFormSection = () => renderFormSection({
8962
+ formConfig: props.formConfig,
8963
+ t,
8964
+ formDraft,
8965
+ setFormValue,
8966
+ handleFormSubmit,
8967
+ handleFormReset
8968
+ });
8969
+ const buildToolbarSection = () => renderToolbarSection({
8970
+ toolbar: props.toolbar,
8971
+ selectable: props.selectable,
8972
+ displaySelection,
8973
+ proxyCtrl,
8974
+ t,
8975
+ importFileInput: importController.importFileInput,
8976
+ onImportFile: importController.handleImportFile,
8977
+ densityToggle: props.densityToggle,
8978
+ effectiveDensity: effectiveDensity.value,
8979
+ onDensityToggle: cycleDensity
8980
+ });
8981
+ const buildPagerSection = () => renderPagerSection({ proxyCtrl, proxyConfig: props.proxyConfig });
8982
+ const contextMenuState = ref(null);
8983
+ const contextAnchorRef = ref(null);
8984
+ const contextMenuRef = ref(null);
8985
+ const closeContextMenu = () => {
8986
+ if (contextMenuState.value) contextMenuState.value.open = false;
8987
+ };
8988
+ const handleContextMenu = (e, row, col, idx, ci) => {
8989
+ if (!props.contextMenu) return;
8990
+ e.preventDefault();
8991
+ contextAnchorRef.value = {
8992
+ getBoundingClientRect: () => ({
8993
+ left: e.clientX,
8994
+ top: e.clientY,
8995
+ right: e.clientX,
8996
+ bottom: e.clientY,
8997
+ width: 0,
8998
+ height: 0,
8999
+ x: e.clientX,
9000
+ y: e.clientY,
9001
+ toJSON() {
9002
+ }
9003
+ })
9004
+ };
9005
+ const params = {
9006
+ row,
9007
+ column: col,
9008
+ rowIndex: idx,
9009
+ columnIndex: ci
9010
+ };
9011
+ contextMenuState.value = { open: true, items: props.contextMenu.items(params), params };
9012
+ };
9013
+ const contextMenuOpen = computed(() => contextMenuState.value?.open === true);
9014
+ const { floatingStyles: contextMenuStyles } = useFloating({
9015
+ anchor: contextAnchorRef,
9016
+ floating: contextMenuRef,
9017
+ open: contextMenuOpen,
9018
+ placement: "bottom-start",
9019
+ // Cursor-anchored menu, vxe parity: with a zero-size anchor at the
9020
+ // viewport edge, flip/shift would clamp the coordinates away from the
9021
+ // cursor (React parity — deliberately disabled).
9022
+ flip: false,
9023
+ shift: false
9024
+ });
9025
+ useDismiss({
9026
+ enabled: contextMenuOpen,
9027
+ exclude: [contextMenuRef],
9028
+ onDismiss: closeContextMenu
9029
+ });
9030
+ watch(contextMenuOpen, (open) => {
9031
+ if (typeof document === "undefined") return;
9032
+ if (open) document.addEventListener("scroll", closeContextMenu, true);
9033
+ else document.removeEventListener("scroll", closeContextMenu, true);
9034
+ });
9035
+ onScopeDispose(() => {
9036
+ if (typeof document === "undefined") return;
9037
+ document.removeEventListener("scroll", closeContextMenu, true);
9038
+ });
9039
+ const buildContextMenuSection = () => renderContextMenuSection({
9040
+ state: contextMenuState,
9041
+ styles: contextMenuStyles,
9042
+ menuRef: contextMenuRef,
9043
+ close: closeContextMenu,
9044
+ contextMenu: props.contextMenu
9045
+ });
9046
+ const filterPanelState = ref(null);
9047
+ const filterAnchorRef = ref(null);
9048
+ const filterPanelRef = ref(null);
9049
+ const filterDraft = ref([]);
9050
+ const closeFilterPanel = () => {
9051
+ if (filterPanelState.value) filterPanelState.value.open = false;
9052
+ };
9053
+ const openFilterPanel = (e, colKey) => {
9054
+ e.stopPropagation();
9055
+ filterAnchorRef.value = e.currentTarget;
9056
+ filterDraft.value = [...props.filterValues?.[colKey] ?? []];
9057
+ filterPanelState.value = { open: true, colKey };
9058
+ };
9059
+ const filterPanelOpen = computed(() => filterPanelState.value?.open === true);
9060
+ const { floatingStyles: filterPanelStyles } = useFloating({
9061
+ anchor: filterAnchorRef,
9062
+ floating: filterPanelRef,
9063
+ open: filterPanelOpen,
9064
+ placement: "bottom-start"
9065
+ });
9066
+ useDismiss({
9067
+ enabled: filterPanelOpen,
9068
+ exclude: [filterPanelRef, filterAnchorRef],
9069
+ onDismiss: closeFilterPanel
9070
+ });
9071
+ watch(filterPanelOpen, (open) => {
9072
+ if (typeof document === "undefined") return;
9073
+ if (open) document.addEventListener("scroll", closeFilterPanel, true);
9074
+ else document.removeEventListener("scroll", closeFilterPanel, true);
9075
+ });
9076
+ onScopeDispose(() => {
9077
+ if (typeof document === "undefined") return;
9078
+ document.removeEventListener("scroll", closeFilterPanel, true);
9079
+ });
9080
+ const applyFilterValues = (colKey, values) => {
9081
+ props.onFilterValuesChange?.({ ...props.filterValues ?? {}, [colKey]: values });
9082
+ };
9083
+ const clearFilterValues = (colKey) => {
9084
+ const next = { ...props.filterValues ?? {} };
9085
+ delete next[colKey];
9086
+ props.onFilterValuesChange?.(next);
9087
+ };
9088
+ const toggleFilterDraft = (value) => {
9089
+ filterDraft.value = filterDraft.value.includes(value) ? filterDraft.value.filter((v) => v !== value) : [...filterDraft.value, value];
9090
+ };
9091
+ const buildFilterPanelSection = () => renderFilterPanelSection({
9092
+ state: filterPanelState,
9093
+ styles: filterPanelStyles,
9094
+ panelRef: filterPanelRef,
9095
+ filterDraft,
9096
+ columns: displayColumns.value,
9097
+ filterValues: props.filterValues,
9098
+ t,
9099
+ close: closeFilterPanel,
9100
+ toggle: toggleFilterDraft,
9101
+ apply: applyFilterValues,
9102
+ clear: clearFilterValues
9103
+ });
6501
9104
  return () => {
6502
9105
  const showSelection = props.selectable !== "none";
6503
9106
  const showDetail = hasDetail.value;
6504
- const buildGroupedHeader = (matrix) => {
6505
- const lead = (showDetail ? 1 : 0) + (showSelection ? 1 : 0);
9107
+ const showDrag = props.rowDrag !== void 0;
9108
+ const showSeq = props.seq === true;
9109
+ spanOccupy.clear();
9110
+ const leadFlatHeaderCells = () => {
6506
9111
  const cells = [];
6507
- if (showDetail) {
9112
+ if (showDrag) {
6508
9113
  cells.push(
6509
9114
  h("div", {
6510
- key: "__expand__",
6511
9115
  role: "columnheader",
6512
- style: { gridColumn: "1", gridRow: "1 / -1" }
9116
+ key: "__drag__",
9117
+ "data-iris-table-header": "__drag",
9118
+ style: {
9119
+ display: "flex",
9120
+ alignItems: "center",
9121
+ justifyContent: "center",
9122
+ padding: "8px",
9123
+ background: "var(--iris-surface)",
9124
+ borderBottom: "1px solid var(--iris-border)"
9125
+ }
6513
9126
  })
6514
9127
  );
6515
9128
  }
6516
- if (showSelection) {
9129
+ if (showSeq) {
6517
9130
  cells.push(
6518
- h(
6519
- "div",
6520
- {
6521
- key: "__select__",
6522
- role: "columnheader",
6523
- "data-iris-table-header": "",
6524
- style: {
6525
- gridColumn: showDetail ? "2" : "1",
6526
- gridRow: "1 / -1",
6527
- display: "flex",
6528
- alignItems: "center",
6529
- justifyContent: "center",
6530
- padding: "8px 12px",
6531
- background: "var(--iris-surface)",
6532
- borderBottom: "1px solid var(--iris-border)"
6533
- }
6534
- },
6535
- props.selectable === "multi" ? [
6536
- [
6537
- h(IrisCheckbox, {
6538
- modelValue: allSelected.value ? true : someSelected.value ? "indeterminate" : false,
6539
- size: "sm",
6540
- ariaLabel: t("table.selectAll"),
6541
- "onUpdate:modelValue": toggleAll
6542
- }),
6543
- props.selection && props.selection.length > 0 ? h(
6544
- "span",
6545
- {
6546
- "data-iris-table-selected-count": "",
6547
- style: {
6548
- marginInlineStart: "var(--iris-space-xs, 8px)",
6549
- fontSize: "var(--iris-font-size-sm, 13px)",
6550
- color: "var(--iris-muted)",
6551
- whiteSpace: "nowrap"
6552
- }
6553
- },
6554
- t("table.selectedCount", { count: String(props.selection.length) })
6555
- ) : null
6556
- ]
6557
- ] : ""
6558
- )
9131
+ h("div", {
9132
+ role: "columnheader",
9133
+ key: "__seq__",
9134
+ "data-iris-table-header": "__seq",
9135
+ style: {
9136
+ display: "flex",
9137
+ alignItems: "center",
9138
+ justifyContent: "center",
9139
+ padding: "8px",
9140
+ background: "var(--iris-surface)",
9141
+ borderBottom: "1px solid var(--iris-border)"
9142
+ }
9143
+ })
6559
9144
  );
6560
9145
  }
6561
- for (const rowCells of matrix) {
6562
- for (const cell of rowCells) {
6563
- const col = cell.column;
6564
- const isLeaf = !col.children || col.children.length === 0;
6565
- const sortable = isLeaf && col.sortable;
6566
- const align = col.align ?? "left";
6567
- const headerSlot = slots[`header.${col.key}`];
6568
- const title = headerSlot?.({ column: col }) ?? col.title;
6569
- cells.push(
6570
- h(
6571
- "div",
6572
- {
6573
- key: `${col.key}-${cell.level}`,
6574
- role: "columnheader",
6575
- "data-iris-table-header": col.key,
6576
- "data-iris-table-header-group": isLeaf ? void 0 : "",
6577
- "aria-colspan": cell.colSpan,
6578
- onClick: sortable ? () => onHeaderClick(col) : void 0,
6579
- "aria-sort": sortable ? internalSort.value?.key === col.key ? internalSort.value.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
6580
- style: {
6581
- gridColumn: `${lead + cell.colStart} / span ${cell.colSpan}`,
6582
- gridRow: `${cell.level + 1} / span ${cell.rowSpan}`,
6583
- display: "flex",
6584
- alignItems: "center",
6585
- justifyContent: isLeaf ? align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start" : "center",
6586
- padding: "8px var(--iris-padding-md)",
6587
- cursor: sortable ? "pointer" : "default",
6588
- userSelect: sortable ? "none" : "auto",
6589
- background: "var(--iris-surface)",
6590
- borderBottom: "1px solid var(--iris-border)",
6591
- fontWeight: "600",
6592
- fontSize: "var(--iris-font-size-md, 14px)",
6593
- color: "var(--iris-foreground)",
6594
- whiteSpace: "nowrap",
6595
- overflow: "hidden",
6596
- textOverflow: "ellipsis"
6597
- }
6598
- },
6599
- [title, sortable ? sortIndicator(col) : null]
6600
- )
6601
- );
6602
- }
9146
+ return cells;
9147
+ };
9148
+ const leadSummaryCells = () => {
9149
+ const cells = [];
9150
+ if (showDrag) {
9151
+ cells.push(
9152
+ h("div", {
9153
+ key: "__drag",
9154
+ role: "cell",
9155
+ "data-iris-table-cell": "__drag",
9156
+ style: {
9157
+ display: "flex",
9158
+ alignItems: "center",
9159
+ justifyContent: "center",
9160
+ padding: "8px 12px"
9161
+ }
9162
+ })
9163
+ );
6603
9164
  }
6604
- return h(
6605
- "div",
6606
- {
6607
- role: "row",
6608
- "data-iris-table-row": "header",
6609
- "data-iris-table-header-row": "",
6610
- "data-iris-table-header-grouped": "",
6611
- style: {
6612
- display: "grid",
6613
- gridTemplateColumns: gridTemplate.value,
6614
- gridTemplateRows: `repeat(${matrix.length}, auto)`
6615
- }
6616
- },
6617
- cells
6618
- );
9165
+ if (showSeq) {
9166
+ cells.push(
9167
+ h("div", {
9168
+ key: "__seq",
9169
+ role: "cell",
9170
+ "data-iris-table-cell": "__seq",
9171
+ style: {
9172
+ display: "flex",
9173
+ alignItems: "center",
9174
+ justifyContent: "center",
9175
+ padding: "8px 12px"
9176
+ }
9177
+ })
9178
+ );
9179
+ }
9180
+ if (showDetail) {
9181
+ cells.push(
9182
+ h("div", {
9183
+ key: "__expand",
9184
+ role: "cell",
9185
+ "data-iris-table-cell": "__expand",
9186
+ style: {
9187
+ display: "flex",
9188
+ alignItems: "center",
9189
+ justifyContent: "center",
9190
+ padding: "8px 12px"
9191
+ }
9192
+ })
9193
+ );
9194
+ }
9195
+ if (showSelection) {
9196
+ cells.push(
9197
+ h("div", {
9198
+ key: "__selection",
9199
+ role: "cell",
9200
+ "data-iris-table-cell": "__selection",
9201
+ style: {
9202
+ display: "flex",
9203
+ alignItems: "center",
9204
+ justifyContent: "center",
9205
+ padding: "8px 12px"
9206
+ }
9207
+ })
9208
+ );
9209
+ }
9210
+ return cells;
6619
9211
  };
6620
- const headerCells = [];
9212
+ const buildGroupedHeader = (matrix) => renderGroupedHeader(
9213
+ {
9214
+ showDrag,
9215
+ showSeq,
9216
+ showDetail,
9217
+ showSelection,
9218
+ selectable: props.selectable,
9219
+ selection: props.selection,
9220
+ allSelected,
9221
+ someSelected,
9222
+ toggleAll,
9223
+ t,
9224
+ slots,
9225
+ onHeaderClick,
9226
+ ariaSortFor,
9227
+ sortIndicator,
9228
+ multiSortSeq,
9229
+ renderFilterTrigger,
9230
+ pinnedDragHandle,
9231
+ gridTemplate
9232
+ },
9233
+ matrix
9234
+ );
9235
+ const headerCells = leadFlatHeaderCells();
6621
9236
  if (showDetail) {
6622
9237
  headerCells.push(
6623
9238
  h("div", {
@@ -6662,14 +9277,15 @@ var IrisTable = defineComponent({
6662
9277
  )
6663
9278
  );
6664
9279
  }
6665
- for (let ci = 0; ci < props.columns.length; ci += 1) {
6666
- const col = props.columns[ci];
9280
+ for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
9281
+ const col = leafColumns.value[ci];
6667
9282
  if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
6668
9283
  const align = col.align ?? "left";
6669
9284
  const headerSlot = slots[`header.${col.key}`];
6670
9285
  const title = headerSlot?.({ column: col }) ?? col.title;
6671
9286
  wireResize(col);
6672
- const handle = props.resizableColumns ? h("span", {
9287
+ const pinnedHandle = pinnedDragHandle(col);
9288
+ const handle = props.resizableColumns && !pinnedHandle ? h("span", {
6673
9289
  ref: (el) => {
6674
9290
  getHandleRef(col.key).value = el ?? null;
6675
9291
  },
@@ -6713,6 +9329,13 @@ var IrisTable = defineComponent({
6713
9329
  role: "columnheader",
6714
9330
  "data-iris-table-header": col.key,
6715
9331
  "data-iris-table-pinned": col.pinned,
9332
+ // Column drag-sort (vxe columnDragConfig parity, batch Y): the
9333
+ // header cell is the press target; grouped headers are NOT
9334
+ // supported (documented simplification — the reorder maps leaf
9335
+ // columns back to the parent, which a group column cannot do).
9336
+ "data-iris-col-drag-active": props.columnDrag && colDragState.value.activeId === col.key ? "true" : void 0,
9337
+ "data-iris-col-drag-over": props.columnDrag && colDragState.value.overId === col.key ? "true" : void 0,
9338
+ onPointerdown: props.columnDrag && !grouped.value ? (e) => handleColDragPointerDown(e, col.key) : void 0,
6716
9339
  onClick: () => onHeaderClick(col),
6717
9340
  style: {
6718
9341
  position: "relative",
@@ -6733,9 +9356,16 @@ var IrisTable = defineComponent({
6733
9356
  ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
6734
9357
  ...col.pinned ? { ...pinnedStyle(col.key), background: "var(--iris-surface)" } : {}
6735
9358
  },
6736
- "aria-sort": internalSort.value?.key === col.key ? internalSort.value.direction === "asc" ? "ascending" : "descending" : col.sortable ? "none" : void 0
9359
+ "aria-sort": ariaSortFor(col)
6737
9360
  },
6738
- [title, sortIndicator(col), handle]
9361
+ [
9362
+ title,
9363
+ sortIndicator(col),
9364
+ multiSortSeq(col),
9365
+ renderFilterTrigger(col, true),
9366
+ pinnedHandle,
9367
+ handle
9368
+ ]
6739
9369
  )
6740
9370
  );
6741
9371
  }
@@ -6755,6 +9385,69 @@ var IrisTable = defineComponent({
6755
9385
  const id = rowId(row, index);
6756
9386
  const selected = isSelected(id);
6757
9387
  const cells = [];
9388
+ if (showDrag) {
9389
+ const rowDragActive = rowDragState.value.activeId === String(id);
9390
+ const rowDragOver = rowDragState.value.overId === String(id);
9391
+ cells.push(
9392
+ h(
9393
+ "div",
9394
+ {
9395
+ key: "__drag",
9396
+ role: "cell",
9397
+ "data-iris-table-cell": "__drag",
9398
+ // The drag handle carries the row's id as its attribute VALUE
9399
+ // (rect collection for closestCenter reads it back).
9400
+ "data-iris-row-drag-handle": String(id),
9401
+ "data-iris-row-drag-active": rowDragActive ? "true" : void 0,
9402
+ "data-iris-row-drag-over": rowDragOver ? "true" : void 0,
9403
+ onPointerdown: (e) => handleRowDragPointerDown(e, String(id)),
9404
+ onClick: (e) => e.stopPropagation(),
9405
+ style: {
9406
+ display: "flex",
9407
+ alignItems: "center",
9408
+ justifyContent: "center",
9409
+ padding: "8px 12px",
9410
+ borderBottom: "1px solid var(--iris-border)",
9411
+ cursor: "grab",
9412
+ color: "var(--iris-muted)",
9413
+ background: rowDragActive ? "var(--iris-surface-hover)" : rowDragOver ? "var(--iris-surface-selected, rgba(99,102,241,0.12))" : "transparent"
9414
+ }
9415
+ },
9416
+ [
9417
+ h(
9418
+ "span",
9419
+ {
9420
+ "aria-hidden": "true",
9421
+ style: { fontSize: "var(--iris-font-size-sm, 13px)" }
9422
+ },
9423
+ "\u283F"
9424
+ )
9425
+ ]
9426
+ )
9427
+ );
9428
+ }
9429
+ if (showSeq) {
9430
+ cells.push(
9431
+ h(
9432
+ "div",
9433
+ {
9434
+ key: "__seq",
9435
+ role: "cell",
9436
+ "data-iris-table-cell": "__seq",
9437
+ style: {
9438
+ display: "flex",
9439
+ alignItems: "center",
9440
+ justifyContent: "center",
9441
+ padding: "8px 12px",
9442
+ borderBottom: "1px solid var(--iris-border)",
9443
+ color: "var(--iris-muted)",
9444
+ userSelect: "none"
9445
+ }
9446
+ },
9447
+ String(index + props.seqStartIndex)
9448
+ )
9449
+ );
9450
+ }
6758
9451
  if (showDetail) {
6759
9452
  const rowExpandable = isRowExpandable(row, index);
6760
9453
  const isExpanded = expandedKeys.value.includes(String(id));
@@ -6831,115 +9524,30 @@ var IrisTable = defineComponent({
6831
9524
  for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
6832
9525
  const col = leafColumns.value[ci];
6833
9526
  if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
9527
+ const span = resolveSpan(spanOccupy, index, ci, props.spanMethod);
9528
+ if (span === null) continue;
9529
+ const rowspan = span.rowspan;
9530
+ const colspan = span.colspan;
6834
9531
  const align = col.align ?? "left";
6835
9532
  const cellSlot = slots[`cell.${col.key}`];
6836
- const isEditing = editingCellId.value === cellId(id, col.key);
9533
+ const isRowEditing = rowMode.value && rowEditing.value !== null && rowEditing.value.k === id && rowSessions.value.has(cellId(id, col.key));
9534
+ const isEditing = isRowEditing || editingCellId.value === cellId(id, col.key);
9535
+ const patternHint = (props.pattern || props.patternFill) && !rowMode.value && editingColumnKey.value === col.key && !isEditing && editingDraft.value !== "" && String(getCellValue(row, col) ?? "") === editingDraft.value;
6837
9536
  let content;
6838
9537
  if (isEditing) {
6839
9538
  const editCellId = cellId(id, col.key);
6840
- const error = editError.value;
6841
- const input = h("input", {
6842
- ref: (el) => {
6843
- editorInputRef.value = el ?? null;
6844
- },
6845
- type: col.editor === "number" ? "number" : "text",
6846
- value: editingDraft.value,
6847
- "data-iris-table-editor": "",
6848
- "aria-invalid": error ? "true" : void 0,
6849
- "aria-describedby": error ? `${editCellId}-error` : void 0,
6850
- onInput: (e) => {
6851
- editingDraft.value = e.target.value;
6852
- },
6853
- onKeydown: (e) => {
6854
- if (e.key === "Enter") {
6855
- e.preventDefault();
6856
- commitEdit(row, col, index);
6857
- } else if (e.key === "Escape") {
6858
- e.preventDefault();
6859
- cancelEdit();
6860
- }
6861
- },
6862
- onBlur: () => commitEdit(row, col, index),
6863
- onClick: (e) => e.stopPropagation(),
6864
- onDblclick: (e) => e.stopPropagation(),
6865
- style: {
6866
- width: "100%",
6867
- border: `1px solid ${error ? "var(--iris-danger)" : "var(--iris-primary)"}`,
6868
- borderRadius: "var(--iris-radius-sm)",
6869
- padding: "var(--iris-space-xxs, 4px) var(--iris-padding-sm, 6px)",
6870
- font: "inherit",
6871
- background: "var(--iris-background)",
6872
- color: "var(--iris-foreground)",
6873
- outline: "none",
6874
- boxShadow: "0 0 0 3px color-mix(in srgb, var(--iris-primary) 18%, transparent)"
6875
- }
6876
- });
6877
- content = error ? [
6878
- input,
6879
- h(
6880
- "div",
6881
- {
6882
- id: `${editCellId}-error`,
6883
- role: "alert",
6884
- "data-iris-table-editor-error": "",
6885
- style: {
6886
- marginTop: "var(--iris-space-xxs, 4px)",
6887
- fontSize: "var(--iris-font-size-xs, 12px)",
6888
- color: "var(--iris-danger)"
6889
- }
6890
- },
6891
- error
6892
- )
6893
- ] : input;
9539
+ content = isRowEditing ? buildRowSessionContent(
9540
+ row,
9541
+ col,
9542
+ index,
9543
+ id,
9544
+ editCellId,
9545
+ rowSessions.value.get(editCellId)
9546
+ ) : buildCellEditContent(row, col, index, editCellId);
6894
9547
  } else {
6895
- content = cellSlot?.({ row, index, value: getCellValue2(row, col) }) ?? String(getCellValue2(row, col) ?? "");
9548
+ content = cellSlot?.({ row, index, value: getCellValue(row, col) }) ?? tableDisplayText(row, col);
6896
9549
  }
6897
- const treeIndent = treeMeta && ci === 0 ? h(
6898
- "span",
6899
- {
6900
- "data-iris-table-tree-indent": "",
6901
- style: {
6902
- display: "inline-flex",
6903
- alignItems: "center",
6904
- flex: "none",
6905
- paddingLeft: `${treeMeta.depth * 16}px`
6906
- }
6907
- },
6908
- treeMeta.hasChildren ? [
6909
- h(
6910
- "button",
6911
- {
6912
- type: "button",
6913
- "data-iris-table-tree-toggle": "",
6914
- "aria-expanded": treeMeta.expanded ? "true" : "false",
6915
- "aria-label": t(
6916
- treeMeta.expanded ? "treeSelect.collapse" : "treeSelect.expand"
6917
- ),
6918
- onClick: (e) => {
6919
- e.stopPropagation();
6920
- expansion.toggle(treeMeta.key);
6921
- },
6922
- style: {
6923
- border: "none",
6924
- background: "transparent",
6925
- cursor: "pointer",
6926
- padding: "0",
6927
- marginRight: "4px",
6928
- font: "inherit",
6929
- color: "var(--iris-foreground)",
6930
- transform: treeMeta.expanded ? "rotate(90deg)" : "none",
6931
- transition: "transform 150ms"
6932
- }
6933
- },
6934
- "\u25B6"
6935
- )
6936
- ] : [
6937
- h("span", {
6938
- "aria-hidden": "true",
6939
- style: { display: "inline-block", width: "16px" }
6940
- })
6941
- ]
6942
- ) : null;
9550
+ const treeIndent = treeMeta && ci === 0 ? buildTreeIndent(treeMeta, row) : null;
6943
9551
  const cellChildren = treeIndent ? [treeIndent, ...Array.isArray(content) ? content : [content]] : content;
6944
9552
  cells.push(
6945
9553
  h(
@@ -6951,6 +9559,7 @@ var IrisTable = defineComponent({
6951
9559
  "data-iris-table-pinned": col.pinned,
6952
9560
  "data-editable": col.editable ? "" : void 0,
6953
9561
  "data-editing": isEditing ? "" : void 0,
9562
+ "data-iris-input-hint": patternHint ? "true" : void 0,
6954
9563
  // Grid keyboard navigation (opt-in): grid coords + roving tabindex
6955
9564
  // (exactly one cell is 0) + an onFocus that syncs the focused cell.
6956
9565
  ...props.keyboardNavigation ? {
@@ -6974,14 +9583,19 @@ var IrisTable = defineComponent({
6974
9583
  }
6975
9584
  }
6976
9585
  } : {},
6977
- onDblclick: col.editable ? () => beginEdit(row, col, id) : void 0,
6978
- onClick: col.editable && props.editConfig?.trigger === "click" ? () => beginEdit(row, col, id) : void 0,
9586
+ onDblclick: rowMode.value ? () => switchRowEdit(row, index, col.key) : col.editable ? () => beginEdit(row, col, id) : void 0,
9587
+ onClick: rowMode.value ? () => handleRowModeCellClick(row, col, index, id) : props.cellRange ? (e) => {
9588
+ if (e.shiftKey) cellRangeCtrl.extendRange(index, ci);
9589
+ else cellRangeCtrl.startRange(index, ci);
9590
+ } : col.editable && props.editConfig?.trigger === "click" ? () => beginEdit(row, col, id) : void 0,
9591
+ onContextmenu: props.contextMenu ? (e) => handleContextMenu(e, row, col, index, ci) : void 0,
6979
9592
  style: {
6980
9593
  display: "flex",
6981
9594
  alignItems: "center",
6982
9595
  justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
6983
9596
  background: "var(--iris-cell-bg, transparent)",
6984
9597
  padding: isEditing ? "var(--iris-space-xxs, 4px)" : "var(--iris-space-xs, 8px) var(--iris-padding-md)",
9598
+ ...isEditing ? { flexWrap: "wrap" } : {},
6985
9599
  borderBottom: "1px solid var(--iris-border)",
6986
9600
  fontSize: "var(--iris-font-size-md, 14px)",
6987
9601
  whiteSpace: "nowrap",
@@ -6990,6 +9604,11 @@ var IrisTable = defineComponent({
6990
9604
  cursor: col.editable ? "cell" : "default",
6991
9605
  ...props.cellRange && isInRange(index, ci) ? { background: "var(--iris-surface-selected, rgba(99,102,241,0.12))" } : {},
6992
9606
  ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
9607
+ ...colspan > 1 ? { gridColumnEnd: `span ${colspan}` } : {},
9608
+ ...rowspan > 1 ? { gridRowEnd: `span ${rowspan}` } : {},
9609
+ ...patternHint ? {
9610
+ backgroundImage: "linear-gradient(var(--iris-input-hint, rgba(251, 191, 36, 0.16)), var(--iris-input-hint, rgba(251, 191, 36, 0.16)))"
9611
+ } : {},
6993
9612
  ...pinnedStyle(col.key)
6994
9613
  }
6995
9614
  },
@@ -7011,6 +9630,8 @@ var IrisTable = defineComponent({
7011
9630
  "aria-setsize": treeMeta ? treeMeta.setSize : void 0,
7012
9631
  "aria-posinset": treeMeta ? treeMeta.posInset : void 0,
7013
9632
  "data-iris-table-row": "",
9633
+ "data-iris-table-row-key": String(id),
9634
+ "data-iris-row-editing": rowMode.value && rowEditing.value?.k === id ? "true" : void 0,
7014
9635
  "data-state": selected ? "selected" : void 0,
7015
9636
  onClick: () => emit("rowClick", row, index),
7016
9637
  onDblclick: () => emit("rowDblclick", row, index),
@@ -7031,50 +9652,21 @@ var IrisTable = defineComponent({
7031
9652
  textAlign: "center",
7032
9653
  color: "var(--iris-muted)"
7033
9654
  };
9655
+ const stateNode = renderTableStateRow({
9656
+ error: tableError.value,
9657
+ loading: tableLoading.value,
9658
+ rowCount: bodyData.value.length,
9659
+ stateRowStyle,
9660
+ errorContent: slots.error ? slots.error() : t("table.error"),
9661
+ loadingContent: slots.loading ? slots.loading() : t("table.loading"),
9662
+ emptyContent: slots.empty ? slots.empty() : t("table.empty"),
9663
+ retry: retry.value,
9664
+ onRetry: props.onRetry,
9665
+ retryLabel: t("table.retry")
9666
+ });
7034
9667
  let bodyNode;
7035
- if (props.error) {
7036
- bodyNode = h("div", { role: "row", "data-iris-table-row": "error", style: stateRowStyle }, [
7037
- h(
7038
- "span",
7039
- { style: { marginInlineEnd: props.onRetry ? "var(--iris-space-sm, 12px)" : "0px" } },
7040
- slots.error ? slots.error() : t("table.error")
7041
- ),
7042
- props.onRetry ? h(
7043
- "button",
7044
- {
7045
- type: "button",
7046
- "data-iris-table-retry": "",
7047
- onClick: props.onRetry,
7048
- style: {
7049
- border: "1px solid var(--iris-border)",
7050
- background: "var(--iris-surface)",
7051
- color: "var(--iris-foreground)",
7052
- borderRadius: "var(--iris-radius-sm, 4px)",
7053
- padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
7054
- fontSize: "var(--iris-font-size-sm, 13px)",
7055
- cursor: "pointer"
7056
- }
7057
- },
7058
- t("table.retry")
7059
- ) : null
7060
- ]);
7061
- } else if (props.loading) {
7062
- bodyNode = h(
7063
- "div",
7064
- {
7065
- role: "row",
7066
- "aria-busy": "true",
7067
- "data-iris-table-row": "loading",
7068
- style: stateRowStyle
7069
- },
7070
- slots.loading ? slots.loading() : t("table.loading")
7071
- );
7072
- } else if (bodyData.value.length === 0) {
7073
- bodyNode = h(
7074
- "div",
7075
- { role: "row", "data-iris-table-row": "empty", style: stateRowStyle },
7076
- slots.empty ? slots.empty() : t("table.empty")
7077
- );
9668
+ if (stateNode) {
9669
+ bodyNode = stateNode;
7078
9670
  } else if (props.virtualScroll && (!treeMode.value || !hasDetail.value)) {
7079
9671
  bodyNode = h(
7080
9672
  IrisVirtualScroll,
@@ -7138,127 +9730,105 @@ var IrisTable = defineComponent({
7138
9730
  bodyChildren
7139
9731
  );
7140
9732
  }
7141
- let summaryRow = null;
7142
- if (!props.error && !props.loading && bodyData.value.length > 0 && leafColumns.value.some((c) => c.summary)) {
7143
- const summaryCells = [];
7144
- if (showSelection) {
7145
- summaryCells.push(
7146
- h("div", {
7147
- key: "__selection",
7148
- role: "cell",
7149
- "data-iris-table-cell": "__selection",
7150
- style: {
7151
- display: "flex",
7152
- alignItems: "center",
7153
- justifyContent: "center",
7154
- padding: "8px 12px"
7155
- }
7156
- })
7157
- );
7158
- }
7159
- for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
7160
- const col = leafColumns.value[ci];
7161
- if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
7162
- const align = col.align ?? "left";
7163
- const op = col.summary;
7164
- const value = op ? aggregate(bodyData.value, (r) => getCellValue2(r, col), op) : null;
7165
- const summaryContent = op != null && value != null ? col.renderSummary ? col.renderSummary(value, bodyData.value) : String(value) : "";
7166
- summaryCells.push(
7167
- h(
7168
- "div",
7169
- {
7170
- key: col.key,
7171
- role: "cell",
7172
- "data-iris-table-cell": col.key,
7173
- "data-iris-table-summary-cell": op ? "" : void 0,
7174
- "data-iris-table-pinned": col.pinned,
7175
- style: {
7176
- display: "flex",
7177
- alignItems: "center",
7178
- justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
7179
- padding: "var(--iris-space-xs, 8px) var(--iris-padding-md)",
7180
- fontSize: "var(--iris-font-size-md, 14px)",
7181
- whiteSpace: "nowrap",
7182
- overflow: "hidden",
7183
- textOverflow: "ellipsis",
7184
- ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
7185
- ...pinnedStyle(col.key)
7186
- }
7187
- },
7188
- summaryContent
7189
- )
7190
- );
7191
- }
7192
- summaryRow = h(
9733
+ const summaryRow = !tableError.value && !tableLoading.value ? renderTableSummaryRow({
9734
+ bodyData: bodyData.value,
9735
+ leafColumns: leafColumns.value,
9736
+ visibleColSet: visibleColSet.value,
9737
+ gridTemplate: gridTemplate.value,
9738
+ leadingCells: leadSummaryCells(),
9739
+ colTrack,
9740
+ getCellValue,
9741
+ pinnedStyle
9742
+ }) : null;
9743
+ const rootNodes = [
9744
+ tableViews.renderTabs(props.tableTabs),
9745
+ tableViews.renderViews(),
9746
+ buildFormSection(),
9747
+ buildToolbarSection(),
9748
+ props.clipConfig && props.clipConfig.copy !== false && activeCellRange() ? h(
9749
+ "button",
9750
+ { type: "button", "data-iris-table-range-copy": "", onClick: copyActiveRange },
9751
+ t("table.range.copy")
9752
+ ) : null,
9753
+ renderImportPreviewSection({
9754
+ rows: importController.importPreviewRows.value,
9755
+ t,
9756
+ onConfirm: importController.confirmImportPreview,
9757
+ onCancel: importController.cancelImportPreview
9758
+ }),
9759
+ h(
7193
9760
  "div",
7194
9761
  {
7195
- role: "row",
7196
- "data-iris-table-row": "summary",
9762
+ ...attrs,
9763
+ ref: (el) => {
9764
+ rootRef.value = el ?? null;
9765
+ },
9766
+ // A keyboard-navigable hierarchical table is a `treegrid`; otherwise the
9767
+ // grid/table role as before (treegrid implies managed cell focus).
9768
+ role: props.keyboardNavigation ? treeMode.value ? "treegrid" : "grid" : "table",
9769
+ "data-iris-table": "",
9770
+ "data-density": effectiveDensity.value,
9771
+ "data-printable": props.printable ? "true" : void 0,
9772
+ "data-virtual": props.virtualScroll ? "" : void 0,
9773
+ "data-column-virtualized": props.columnVirtualization ? "true" : void 0,
9774
+ onKeydown: props.keyboardNavigation || props.cellRange || props.clipConfig ? (e) => handleRootKeyDown(e) : void 0,
9775
+ onScroll: props.columnVirtualization ? (e) => {
9776
+ scrollLeft.value = e.currentTarget.scrollLeft;
9777
+ } : void 0,
9778
+ // Row/column drag-sort (batch Y): container-level pointer handling
9779
+ // for both reorders (React parity) — press happens on the row
9780
+ // handle / header cell, move/up resolve at the container.
9781
+ onPointermove: props.rowDrag || props.columnDrag ? (e) => {
9782
+ handleRowDragPointerMove(e);
9783
+ handleColDragPointerMove(e);
9784
+ } : void 0,
9785
+ onPointerup: props.rowDrag || props.columnDrag ? () => {
9786
+ handleRowDragPointerUp();
9787
+ handleColDragPointerUp();
9788
+ } : void 0,
9789
+ onPointerleave: props.rowDrag ? handleRowDragPointerLeave : void 0,
7197
9790
  style: {
7198
- display: "grid",
7199
- gridTemplateColumns: gridTemplate.value,
7200
- fontWeight: "600",
7201
- borderTop: "2px solid var(--iris-border)",
7202
- background: "var(--iris-surface)"
9791
+ background: "var(--iris-background)",
9792
+ color: "var(--iris-foreground)",
9793
+ fontSize: "var(--iris-font-size-md, 14px)",
9794
+ border: props.bordered ? "1px solid var(--iris-border)" : "none",
9795
+ borderRadius: "var(--iris-radius-md)",
9796
+ // Column virtualization and responsive overflow turn the table
9797
+ // into a horizontal scroll container.
9798
+ overflow: props.columnVirtualization || responsiveOverflow.value ? "auto" : "hidden",
9799
+ ...responsiveOverflow.value ? { overflowX: "auto" } : {},
9800
+ ...attrs.style ?? {}
7203
9801
  }
7204
9802
  },
7205
- summaryCells
7206
- );
7207
- }
7208
- return h(
7209
- "div",
7210
- {
7211
- ...attrs,
7212
- ref: (el) => {
7213
- rootRef.value = el ?? null;
9803
+ [headerRow, bodyNode, summaryRow, buildPagerSection()]
9804
+ ),
9805
+ props.responsive && responsiveOverflow.value && !props.printable ? h(
9806
+ "div",
9807
+ {
9808
+ "data-iris-scroll-hint": "",
9809
+ role: "status",
9810
+ "aria-live": "polite",
9811
+ style: {
9812
+ display: "flex",
9813
+ alignItems: "center",
9814
+ gap: "var(--iris-space-xxs, 4px)",
9815
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
9816
+ color: "var(--iris-muted)",
9817
+ background: "var(--iris-surface)",
9818
+ borderInline: "1px solid var(--iris-border)",
9819
+ borderBottom: "1px solid var(--iris-border)",
9820
+ fontSize: "var(--iris-font-size-sm, 13px)"
9821
+ }
7214
9822
  },
7215
- // A keyboard-navigable hierarchical table is a `treegrid`; otherwise the
7216
- // grid/table role as before (treegrid implies managed cell focus).
7217
- role: props.keyboardNavigation ? treeMode.value ? "treegrid" : "grid" : "table",
7218
- "data-iris-table": "",
7219
- "data-virtual": props.virtualScroll ? "" : void 0,
7220
- "data-column-virtualized": props.columnVirtualization ? "true" : void 0,
7221
- onKeydown: props.keyboardNavigation || props.cellRange ? (e) => {
7222
- if (props.keyboardNavigation) handleGridKey(e);
7223
- if (props.cellRange) handleCellRangeKey(e);
7224
- } : void 0,
7225
- onScroll: props.columnVirtualization ? (e) => {
7226
- scrollLeft.value = e.currentTarget.scrollLeft;
7227
- } : void 0,
7228
- style: {
7229
- background: "var(--iris-background)",
7230
- color: "var(--iris-foreground)",
7231
- fontSize: "var(--iris-font-size-md, 14px)",
7232
- border: props.bordered ? "1px solid var(--iris-border)" : "none",
7233
- borderRadius: "var(--iris-radius-md)",
7234
- // Column virtualization turns the table into a horizontal scroll container.
7235
- overflow: props.columnVirtualization ? "auto" : "hidden",
7236
- ...attrs.style ?? {}
7237
- }
7238
- },
7239
- [headerRow, bodyNode, summaryRow]
7240
- );
9823
+ [h("span", { "aria-hidden": "true" }, "\u21C6"), h("span", t("table.scrollHint"))]
9824
+ ) : null,
9825
+ buildContextMenuSection(),
9826
+ buildFilterPanelSection()
9827
+ ].filter((n) => n !== null);
9828
+ return rootNodes.length === 1 ? rootNodes[0] : rootNodes;
7241
9829
  };
7242
9830
  }
7243
9831
  });
7244
- function exportCsv(rows, columns) {
7245
- return toCsv(
7246
- rows,
7247
- columns.map((column) => ({
7248
- key: column.key,
7249
- title: column.title,
7250
- dataIndex: typeof column.dataIndex === "string" ? column.dataIndex : void 0
7251
- }))
7252
- );
7253
- }
7254
- async function downloadCsv(filename, csv) {
7255
- const BOM = String.fromCharCode(65279);
7256
- await downloadFile({
7257
- filename,
7258
- content: BOM + csv,
7259
- mimeType: "text/csv;charset=utf-8;"
7260
- });
7261
- }
7262
9832
  function exportExcel(rows, columns, options) {
7263
9833
  return toSpreadsheetXml(
7264
9834
  rows,
@@ -7279,7 +9849,7 @@ async function downloadExcel(filename, xml) {
7279
9849
  }
7280
9850
 
7281
9851
  // src/primitives/tabs/context.ts
7282
- var TabsContextKey = /* @__PURE__ */ Symbol("IrisTabs");
9852
+ var TabsContextKey = Symbol("IrisTabs");
7283
9853
 
7284
9854
  // src/primitives/tabs/Tabs.ts
7285
9855
  var IrisTabs = defineComponent({
@@ -7579,7 +10149,7 @@ var IrisTabsContent = defineComponent({
7579
10149
  });
7580
10150
 
7581
10151
  // src/primitives/menu/context.ts
7582
- var MenuContextKey = /* @__PURE__ */ Symbol("IrisMenu");
10152
+ var MenuContextKey = Symbol("IrisMenu");
7583
10153
 
7584
10154
  // src/primitives/menu/Menu.ts
7585
10155
  var IrisMenu = defineComponent({
@@ -8426,6 +10996,29 @@ var TREE_STATE_STYLE = {
8426
10996
  color: "var(--iris-muted)",
8427
10997
  fontSize: "var(--iris-font-size-md, 14px)"
8428
10998
  };
10999
+ function renderTreeState({
11000
+ state,
11001
+ stateKey,
11002
+ stateProps,
11003
+ slots,
11004
+ translate
11005
+ }) {
11006
+ const content = state === "error" ? slots.error ? slots.error() : translate("tree.error") : state === "loading" ? slots.loading ? slots.loading() : translate("tree.loading") : slots.empty ? slots.empty() : translate("tree.empty");
11007
+ return h(
11008
+ "div",
11009
+ {
11010
+ key: stateKey,
11011
+ role: "presentation",
11012
+ "data-iris-tree-state": state,
11013
+ "aria-live": "polite",
11014
+ ...stateProps,
11015
+ style: TREE_STATE_STYLE
11016
+ },
11017
+ content
11018
+ );
11019
+ }
11020
+
11021
+ // src/primitives/tree/Tree.ts
8429
11022
  var IrisTree = defineComponent({
8430
11023
  name: "IrisTree",
8431
11024
  inheritAttrs: false,
@@ -8655,21 +11248,6 @@ var IrisTree = defineComponent({
8655
11248
  break;
8656
11249
  }
8657
11250
  }
8658
- const renderStateNode = () => {
8659
- const content = state.value === "error" ? slots.error ? slots.error() : t("tree.error") : state.value === "loading" ? slots.loading ? slots.loading() : t("tree.loading") : slots.empty ? slots.empty() : t("tree.empty");
8660
- return h(
8661
- "div",
8662
- {
8663
- key: stateKey.value,
8664
- role: "presentation",
8665
- "data-iris-tree-state": state.value,
8666
- "aria-live": "polite",
8667
- ...stateProps.value,
8668
- style: TREE_STATE_STYLE
8669
- },
8670
- content
8671
- );
8672
- };
8673
11251
  const renderItems = () => {
8674
11252
  void checkedKeys.value;
8675
11253
  return flat.value.map((entry) => {
@@ -8802,7 +11380,15 @@ var IrisTree = defineComponent({
8802
11380
  ...attrs.style ?? {}
8803
11381
  }
8804
11382
  },
8805
- isContent.value ? renderItems() : [renderStateNode()]
11383
+ isContent.value ? renderItems() : [
11384
+ renderTreeState({
11385
+ state: state.value,
11386
+ stateKey: stateKey.value,
11387
+ stateProps: stateProps.value,
11388
+ slots,
11389
+ translate: t
11390
+ })
11391
+ ]
8806
11392
  );
8807
11393
  }
8808
11394
  });
@@ -14035,12 +16621,12 @@ function defaultFilter(query, item) {
14035
16621
  if (!q) return 0;
14036
16622
  const haystacks = [item.label, ...item.keywords ?? []].map((s) => s.toLowerCase());
14037
16623
  let best = null;
14038
- for (const h89 of haystacks) {
16624
+ for (const h101 of haystacks) {
14039
16625
  let qi = 0;
14040
16626
  let lastIdx = -1;
14041
16627
  let score = 0;
14042
- for (let i = 0; i < h89.length && qi < q.length; i += 1) {
14043
- if (h89[i] === q[qi]) {
16628
+ for (let i = 0; i < h101.length && qi < q.length; i += 1) {
16629
+ if (h101[i] === q[qi]) {
14044
16630
  score += lastIdx === -1 ? i : i - lastIdx - 1;
14045
16631
  lastIdx = i;
14046
16632
  qi += 1;