@iris-ui-kit/vue 0.2.27 → 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 +11 -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-ASFDGYIR.js → chunk-EZUDM4TM.js} +13 -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 +3462 -859
  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-ASFDGYIR.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.cjs CHANGED
@@ -55,7 +55,7 @@ function useDisposableScope() {
55
55
  });
56
56
  return scope;
57
57
  }
58
- var IrisThemeKey = /* @__PURE__ */ Symbol("IrisTheme");
58
+ var IrisThemeKey = Symbol("IrisTheme");
59
59
  var ThemeProvider = vue.defineComponent({
60
60
  name: "IrisThemeProvider",
61
61
  props: {
@@ -164,7 +164,7 @@ function useColorScheme() {
164
164
  vue.onBeforeUnmount(stop);
165
165
  return scheme;
166
166
  }
167
- var IrisSkinKey = /* @__PURE__ */ Symbol("IrisSkin");
167
+ var IrisSkinKey = Symbol("IrisSkin");
168
168
  var SkinProvider = vue.defineComponent({
169
169
  name: "IrisSkinProvider",
170
170
  props: {
@@ -243,7 +243,7 @@ function useMachine(machine, options = {}) {
243
243
  );
244
244
  return { state, send: machine.send };
245
245
  }
246
- var FormInjectionKey = /* @__PURE__ */ Symbol("IrisForm");
246
+ var FormInjectionKey = Symbol("IrisForm");
247
247
  function useFormContext() {
248
248
  const form = vue.inject(FormInjectionKey, null);
249
249
  if (!form) {
@@ -367,7 +367,7 @@ function useFieldArray(name) {
367
367
  }
368
368
 
369
369
  // src/i18n/context.ts
370
- var I18nInjectionKey = /* @__PURE__ */ Symbol("IrisI18n");
370
+ var I18nInjectionKey = Symbol("IrisI18n");
371
371
 
372
372
  // src/i18n/I18nProvider.ts
373
373
  var IrisI18nProvider = vue.defineComponent({
@@ -917,6 +917,7 @@ var CSS2 = `
917
917
  --iris-nav-item-padding-block: 8px;
918
918
  --iris-nav-item-border-radius: var(--iris-radius-md, 6px);
919
919
  --iris-nav-item-hover: var(--iris-surface-hover, var(--iris-surface));
920
+ --iris-nav-item-active-hover: color-mix(in srgb, var(--iris-primary) 88%, black);
920
921
  --iris-nav-item-height: 34px;
921
922
  display: flex;
922
923
  flex-direction: column;
@@ -1022,6 +1023,13 @@ var CSS2 = `
1022
1023
  font-weight: 600;
1023
1024
  }
1024
1025
 
1026
+ /* active \u9879\u7684 hover \u53CD\u9988\uFF1Aprimary \u52A0\u6DF1\u53D8\u4F53\uFF08\u76AE\u80A4\u53EF\u7528
1027
+ --iris-nav-item-active-hover \u8986\u76D6\uFF09\uFF1B\u7279\u5F02\u5EA6\u9AD8\u4E8E :hover/:where \u89C4\u5219 */
1028
+ .iris-nav-menu-item[data-active='true']:hover,
1029
+ .iris-nav-menu-item[data-active='true'].is-hovered:not([disabled], .is-disabled) {
1030
+ background: var(--iris-nav-item-active-hover, color-mix(in srgb, var(--iris-primary) 88%, black));
1031
+ }
1032
+
1025
1033
  :where(.iris-nav-menu-item[data-active-trail='true']) {
1026
1034
  color: var(--iris-primary);
1027
1035
  font-weight: 600;
@@ -2005,7 +2013,7 @@ function useTabsNav(nav) {
2005
2013
  }
2006
2014
 
2007
2015
  // src/primitives/dropdown/context.ts
2008
- var DropdownContextKey = /* @__PURE__ */ Symbol("IrisDropdown");
2016
+ var DropdownContextKey = Symbol("IrisDropdown");
2009
2017
 
2010
2018
  // src/primitives/dropdown/Dropdown.ts
2011
2019
  var IrisDropdown = vue.defineComponent({
@@ -3422,7 +3430,7 @@ function useUndoStack(options) {
3422
3430
  }
3423
3431
 
3424
3432
  // src/provider/context.ts
3425
- var PluginStoreContextKey = /* @__PURE__ */ Symbol("IrisPluginStore");
3433
+ var PluginStoreContextKey = Symbol("IrisPluginStore");
3426
3434
 
3427
3435
  // src/provider/IrisProvider.ts
3428
3436
  var DEFAULT_LOCALE = "en-US";
@@ -3683,15 +3691,18 @@ function installButtonStyles() {
3683
3691
  var SIZE_STYLES = {
3684
3692
  sm: {
3685
3693
  padding: "var(--iris-padding-sm) var(--iris-padding-md)",
3686
- fontSize: "var(--iris-font-size-xs, 12px)"
3694
+ fontSize: "var(--iris-font-size-xs, 12px)",
3695
+ minHeight: "var(--iris-control-height-sm, 28px)"
3687
3696
  },
3688
3697
  md: {
3689
3698
  padding: "var(--iris-padding-sm) var(--iris-padding-lg)",
3690
- fontSize: "var(--iris-font-size-md, 14px)"
3699
+ fontSize: "var(--iris-font-size-md, 14px)",
3700
+ minHeight: "var(--iris-control-height-md, 34px)"
3691
3701
  },
3692
3702
  lg: {
3693
3703
  padding: "var(--iris-padding-md) var(--iris-padding-lg)",
3694
- fontSize: "var(--iris-font-size-lg, 16px)"
3704
+ fontSize: "var(--iris-font-size-lg, 16px)",
3705
+ minHeight: "var(--iris-control-height-lg, 40px)"
3695
3706
  }
3696
3707
  };
3697
3708
  var VARIANT_STYLES = {
@@ -3726,6 +3737,7 @@ function buildInlineStyle(variant, size) {
3726
3737
  const base = { ...SIZE_STYLES[size], ...VARIANT_STYLES[variant] };
3727
3738
  if (variant === "link") {
3728
3739
  base.padding = "0";
3740
+ delete base.minHeight;
3729
3741
  }
3730
3742
  return base;
3731
3743
  }
@@ -3827,7 +3839,7 @@ var IrisButton = vue.defineComponent({
3827
3839
  });
3828
3840
 
3829
3841
  // src/primitives/popover/context.ts
3830
- var PopoverContextKey = /* @__PURE__ */ Symbol("IrisPopover");
3842
+ var PopoverContextKey = Symbol("IrisPopover");
3831
3843
 
3832
3844
  // src/primitives/popover/Popover.ts
3833
3845
  var IrisPopover = vue.defineComponent({
@@ -3948,42 +3960,18 @@ var ANIM_DIALOG = "var(--iris-anim-dialog)";
3948
3960
  var ANIM_POPOVER = "var(--iris-anim-popover)";
3949
3961
  var ANIM_TOAST = "var(--iris-anim-toast)";
3950
3962
  var ANIM_TOOLTIP = "var(--iris-anim-tooltip)";
3951
- function installFloatingAnimations() {
3952
- if (installed4 || typeof document === "undefined") return;
3953
- installed4 = true;
3954
- if (document.getElementById(STYLE_ID2)) return;
3955
- const style = document.createElement("style");
3956
- style.id = STYLE_ID2;
3957
- style.textContent = `
3963
+ var ANIMATION_CSS = `
3958
3964
  :root {
3959
3965
  --iris-anim-dialog: iris-dialog-in 150ms ease-out;
3960
3966
  --iris-anim-popover: iris-popover-in 150ms ease-out;
3961
3967
  --iris-anim-toast: iris-toast-in 200ms ease-out;
3962
3968
  --iris-anim-tooltip: iris-tooltip-in 120ms ease-out;
3963
3969
  }
3964
- /*
3965
- * NOTE: these keyframes animate OPACITY ONLY. The floating layers are
3966
- * positioned via inline transform: translate3d(...) (Floating UI), and any
3967
- * transform in the keyframes would override it for the animation duration \u2014
3968
- * flashing the panel at the viewport origin (0,0) before it jumps into place.
3969
- * Fade-only keeps the entrance motion without touching positioning.
3970
- */
3971
- @keyframes iris-dialog-in {
3972
- from { opacity: 0; }
3973
- to { opacity: 1; }
3974
- }
3975
- @keyframes iris-popover-in {
3976
- from { opacity: 0; }
3977
- to { opacity: 1; }
3978
- }
3979
- @keyframes iris-toast-in {
3980
- from { opacity: 0; }
3981
- to { opacity: 1; }
3982
- }
3983
- @keyframes iris-tooltip-in {
3984
- from { opacity: 0; }
3985
- to { opacity: 1; }
3986
- }
3970
+ /* Fade-only keyframes preserve Floating UI's inline translate3d position. */
3971
+ @keyframes iris-dialog-in { from { opacity: 0; } to { opacity: 1; } }
3972
+ @keyframes iris-popover-in { from { opacity: 0; } to { opacity: 1; } }
3973
+ @keyframes iris-toast-in { from { opacity: 0; } to { opacity: 1; } }
3974
+ @keyframes iris-tooltip-in { from { opacity: 0; } to { opacity: 1; } }
3987
3975
  @media (prefers-reduced-motion: reduce) {
3988
3976
  :root {
3989
3977
  --iris-anim-dialog: none;
@@ -3992,7 +3980,6 @@ function installFloatingAnimations() {
3992
3980
  --iris-anim-tooltip: none;
3993
3981
  }
3994
3982
  }
3995
- /* P4: global focus-visible ring for interactive controls */
3996
3983
  [data-iris-tabs-trigger]:focus-visible,
3997
3984
  [data-iris-switch]:focus-visible,
3998
3985
  [data-iris-checkbox]:focus-visible,
@@ -4004,6 +3991,13 @@ function installFloatingAnimations() {
4004
3991
  outline-offset: 2px;
4005
3992
  }
4006
3993
  `;
3994
+ function installFloatingAnimations() {
3995
+ if (installed4 || typeof document === "undefined") return;
3996
+ installed4 = true;
3997
+ if (document.getElementById(STYLE_ID2)) return;
3998
+ const style = document.createElement("style");
3999
+ style.id = STYLE_ID2;
4000
+ style.textContent = ANIMATION_CSS;
4007
4001
  document.head.appendChild(style);
4008
4002
  }
4009
4003
 
@@ -4176,12 +4170,12 @@ var IrisTooltip = vue.defineComponent({
4176
4170
  const triggerListeners = {
4177
4171
  onPointerenter: handleEnter,
4178
4172
  onPointerleave: handleLeave,
4179
- onFocus: (() => {
4173
+ onFocus: () => {
4180
4174
  if (!props.disabled) hi.open();
4181
- }),
4182
- onBlur: (() => {
4175
+ },
4176
+ onBlur: () => {
4183
4177
  if (!props.disabled) hi.close();
4184
- })
4178
+ }
4185
4179
  };
4186
4180
  const captureTriggerRef = (el) => {
4187
4181
  triggerRef.value = el ?? null;
@@ -4250,7 +4244,7 @@ var IrisTooltip = vue.defineComponent({
4250
4244
  });
4251
4245
 
4252
4246
  // src/primitives/dialog/context.ts
4253
- var DialogContextKey = /* @__PURE__ */ Symbol("IrisDialog");
4247
+ var DialogContextKey = Symbol("IrisDialog");
4254
4248
 
4255
4249
  // src/primitives/dialog/Dialog.ts
4256
4250
  var IrisDialog = vue.defineComponent({
@@ -4376,9 +4370,8 @@ function getFocusable(root) {
4376
4370
  (el) => !el.hasAttribute("disabled") && el.tabIndex !== -1
4377
4371
  );
4378
4372
  }
4379
- function useFocusTrap(options) {
4380
- let previouslyFocused = null;
4381
- const onKeyDown = (event) => {
4373
+ function createFocusTrapKeyHandler(options) {
4374
+ return (event) => {
4382
4375
  if (!options.active.value || event.key !== "Tab") return;
4383
4376
  const root = options.container.value;
4384
4377
  if (!root) return;
@@ -4396,13 +4389,15 @@ function useFocusTrap(options) {
4396
4389
  event.preventDefault();
4397
4390
  last.focus();
4398
4391
  }
4399
- } else {
4400
- if (active === last || !root.contains(active)) {
4401
- event.preventDefault();
4402
- first.focus();
4403
- }
4392
+ } else if (active === last || !root.contains(active)) {
4393
+ event.preventDefault();
4394
+ first.focus();
4404
4395
  }
4405
4396
  };
4397
+ }
4398
+ function useFocusTrap(options) {
4399
+ let previouslyFocused = null;
4400
+ const onKeyDown = createFocusTrapKeyHandler(options);
4406
4401
  const activate = () => {
4407
4402
  if (typeof document === "undefined") return;
4408
4403
  previouslyFocused = document.activeElement ?? null;
@@ -4707,7 +4702,7 @@ var IrisDialogClose = vue.defineComponent({
4707
4702
  });
4708
4703
 
4709
4704
  // src/primitives/drawer/context.ts
4710
- var DrawerContextKey = /* @__PURE__ */ Symbol("IrisDrawer");
4705
+ var DrawerContextKey = Symbol("IrisDrawer");
4711
4706
 
4712
4707
  // src/primitives/drawer/Drawer.ts
4713
4708
  var IrisDrawer = vue.defineComponent({
@@ -5091,11 +5086,11 @@ var IrisInput = vue.defineComponent({
5091
5086
  display: "inline-flex",
5092
5087
  alignItems: "center",
5093
5088
  gap: "var(--iris-gap-sm)",
5094
- background: "var(--iris-background)",
5089
+ background: props.readonly ? "var(--iris-surface)" : "var(--iris-background)",
5095
5090
  color: "var(--iris-foreground)",
5096
5091
  border: `1px solid ${borderColor}`,
5097
5092
  borderRadius: "var(--iris-radius-md)",
5098
- cursor: props.disabled ? "not-allowed" : "text",
5093
+ cursor: props.disabled ? "not-allowed" : props.readonly ? "default" : "text",
5099
5094
  opacity: props.disabled ? "0.6" : "1",
5100
5095
  transition: "border-color 120ms ease, box-shadow 120ms ease",
5101
5096
  boxShadow,
@@ -5135,7 +5130,7 @@ var IrisInput = vue.defineComponent({
5135
5130
  ...attrs,
5136
5131
  "data-iris-input": "",
5137
5132
  "data-iris-input-size": props.size,
5138
- "data-state": props.invalid ? "invalid" : focused.value ? "focused" : "idle",
5133
+ "data-state": props.invalid ? "invalid" : focused.value ? "focused" : props.readonly ? "readonly" : "idle",
5139
5134
  style: {
5140
5135
  ...wrapperStyle.value,
5141
5136
  ...attrs.style ?? {}
@@ -6717,7 +6712,7 @@ var IrisCheckbox = vue.defineComponent({
6717
6712
  );
6718
6713
  }
6719
6714
  });
6720
- var RadioGroupKey = /* @__PURE__ */ Symbol("IrisRadioGroup");
6715
+ var RadioGroupKey = Symbol("IrisRadioGroup");
6721
6716
  var IrisRadioGroup = vue.defineComponent({
6722
6717
  name: "IrisRadioGroup",
6723
6718
  props: {
@@ -7091,6 +7086,152 @@ var IrisList = vue.defineComponent({
7091
7086
  );
7092
7087
  }
7093
7088
  });
7089
+ function renderVirtualSelectListbox({
7090
+ items,
7091
+ translate,
7092
+ listboxRef,
7093
+ onListKeyDown,
7094
+ virtualizer,
7095
+ state,
7096
+ activeIndex,
7097
+ multiple,
7098
+ selectedValues,
7099
+ currentValue,
7100
+ onSelect,
7101
+ rowHeight
7102
+ }) {
7103
+ const optionStyle = (selected, active, disabled) => ({
7104
+ display: "flex",
7105
+ alignItems: "center",
7106
+ gap: "var(--iris-gap-sm)",
7107
+ padding: "var(--iris-padding-sm, 6px) var(--iris-padding-md)",
7108
+ borderRadius: "var(--iris-radius-sm)",
7109
+ cursor: disabled ? "not-allowed" : "pointer",
7110
+ opacity: disabled ? "0.5" : "1",
7111
+ fontSize: "var(--iris-font-size-md, 14px)",
7112
+ background: selected ? "var(--iris-surface-selected, rgba(99, 102, 241, 0.12))" : active ? "var(--iris-surface-hover)" : "transparent",
7113
+ color: "var(--iris-foreground)",
7114
+ fontWeight: selected ? "600" : "400",
7115
+ outline: "none"
7116
+ });
7117
+ const check = () => vue.h(
7118
+ "svg",
7119
+ {
7120
+ "aria-hidden": "true",
7121
+ width: "14",
7122
+ height: "14",
7123
+ viewBox: "0 0 24 24",
7124
+ fill: "none",
7125
+ stroke: "var(--iris-primary)",
7126
+ "stroke-width": "2.5",
7127
+ "stroke-linecap": "round",
7128
+ "stroke-linejoin": "round"
7129
+ },
7130
+ [vue.h("path", { d: "M20 6 9 17l-5-5" })]
7131
+ );
7132
+ return vue.h(
7133
+ "ul",
7134
+ {
7135
+ role: "listbox",
7136
+ "aria-label": translate("select.options"),
7137
+ "data-iris-list": "",
7138
+ ref: (el) => {
7139
+ listboxRef.value = el ?? null;
7140
+ },
7141
+ onKeydown: onListKeyDown,
7142
+ onScroll: (event) => {
7143
+ virtualizer?.setScroll(event.currentTarget.scrollTop);
7144
+ },
7145
+ style: {
7146
+ listStyle: "none",
7147
+ margin: "0",
7148
+ padding: "var(--iris-padding-sm)",
7149
+ display: "flex",
7150
+ flexDirection: "column",
7151
+ outline: "none",
7152
+ maxHeight: "240px",
7153
+ overflowY: "auto"
7154
+ }
7155
+ },
7156
+ items.length === 0 ? [
7157
+ vue.h(
7158
+ "li",
7159
+ {
7160
+ role: "presentation",
7161
+ "data-iris-list-state": "empty",
7162
+ "aria-live": "polite",
7163
+ style: {
7164
+ listStyle: "none",
7165
+ padding: "12px",
7166
+ textAlign: "center",
7167
+ color: "var(--iris-muted)",
7168
+ fontSize: "var(--iris-font-size-md, 14px)"
7169
+ }
7170
+ },
7171
+ translate("list.empty")
7172
+ )
7173
+ ] : [
7174
+ vue.h("li", {
7175
+ role: "presentation",
7176
+ "aria-hidden": "true",
7177
+ "data-iris-select-spacer": "",
7178
+ "data-iris-select-spacer-type": "top",
7179
+ style: { height: `${state.value.offsetBefore}px` }
7180
+ }),
7181
+ ...state.value.items.map((item) => {
7182
+ const option = items[item.index];
7183
+ if (!option) return null;
7184
+ const active = item.index === activeIndex.value;
7185
+ const selected = multiple ? selectedValues.value.includes(option.value) : option.value === currentValue.value;
7186
+ return vue.h(
7187
+ "li",
7188
+ {
7189
+ key: String(option.value ?? item.index),
7190
+ role: "option",
7191
+ tabindex: active ? 0 : -1,
7192
+ "aria-selected": selected ? "true" : "false",
7193
+ "aria-disabled": option.disabled ? "true" : void 0,
7194
+ "aria-setsize": items.length,
7195
+ "aria-posinset": item.index + 1,
7196
+ "data-iris-list-index": item.index,
7197
+ "data-iris-list-item": "",
7198
+ "data-state": selected ? "selected" : active ? "active" : "idle",
7199
+ onClick: option.disabled ? void 0 : () => {
7200
+ activeIndex.value = item.index;
7201
+ onSelect(option);
7202
+ },
7203
+ onFocus: () => {
7204
+ activeIndex.value = item.index;
7205
+ },
7206
+ onMouseenter: () => {
7207
+ activeIndex.value = item.index;
7208
+ },
7209
+ style: optionStyle(selected, active, !!option.disabled)
7210
+ },
7211
+ [
7212
+ vue.h(
7213
+ "span",
7214
+ { style: { flex: "1", minWidth: "0" } },
7215
+ option.label ?? String(option.value)
7216
+ ),
7217
+ selected ? check() : null
7218
+ ]
7219
+ );
7220
+ }),
7221
+ vue.h("li", {
7222
+ role: "presentation",
7223
+ "aria-hidden": "true",
7224
+ "data-iris-select-spacer": "",
7225
+ "data-iris-select-spacer-type": "bottom",
7226
+ style: {
7227
+ height: `${state.value.totalSize - state.value.offsetBefore - state.value.items.length * rowHeight}px`
7228
+ }
7229
+ })
7230
+ ]
7231
+ );
7232
+ }
7233
+
7234
+ // src/primitives/select/Select.ts
7094
7235
  var IrisSelect = vue.defineComponent({
7095
7236
  name: "IrisSelect",
7096
7237
  inheritAttrs: false,
@@ -7328,138 +7469,6 @@ var IrisSelect = vue.defineComponent({
7328
7469
  })
7329
7470
  ]
7330
7471
  );
7331
- const renderVirtualListbox = () => {
7332
- const list = props.items;
7333
- const optionStyle = (selected, active, disabled) => ({
7334
- display: "flex",
7335
- alignItems: "center",
7336
- gap: "var(--iris-gap-sm)",
7337
- padding: "var(--iris-padding-sm, 6px) var(--iris-padding-md)",
7338
- borderRadius: "var(--iris-radius-sm)",
7339
- cursor: disabled ? "not-allowed" : "pointer",
7340
- opacity: disabled ? "0.5" : "1",
7341
- fontSize: "var(--iris-font-size-md, 14px)",
7342
- background: selected ? "var(--iris-surface-selected, rgba(99, 102, 241, 0.12))" : active ? "var(--iris-surface-hover)" : "transparent",
7343
- color: "var(--iris-foreground)",
7344
- fontWeight: selected ? "600" : "400",
7345
- outline: "none"
7346
- });
7347
- const check = () => vue.h(
7348
- "svg",
7349
- {
7350
- "aria-hidden": "true",
7351
- width: "14",
7352
- height: "14",
7353
- viewBox: "0 0 24 24",
7354
- fill: "none",
7355
- stroke: "var(--iris-primary)",
7356
- "stroke-width": "2.5",
7357
- "stroke-linecap": "round",
7358
- "stroke-linejoin": "round"
7359
- },
7360
- [vue.h("path", { d: "M20 6 9 17l-5-5" })]
7361
- );
7362
- return vue.h(
7363
- "ul",
7364
- {
7365
- role: "listbox",
7366
- "aria-label": t("select.options"),
7367
- "data-iris-list": "",
7368
- ref: (el) => {
7369
- listboxRef.value = el ?? null;
7370
- },
7371
- onKeydown: onListKeyDown,
7372
- onScroll: (e) => {
7373
- v?.setScroll(e.currentTarget.scrollTop);
7374
- },
7375
- style: {
7376
- listStyle: "none",
7377
- margin: "0",
7378
- padding: "var(--iris-padding-sm)",
7379
- display: "flex",
7380
- flexDirection: "column",
7381
- outline: "none",
7382
- maxHeight: "240px",
7383
- overflowY: "auto"
7384
- }
7385
- },
7386
- list.length === 0 ? [
7387
- vue.h(
7388
- "li",
7389
- {
7390
- role: "presentation",
7391
- "data-iris-list-state": "empty",
7392
- "aria-live": "polite",
7393
- style: {
7394
- listStyle: "none",
7395
- padding: "12px",
7396
- textAlign: "center",
7397
- color: "var(--iris-muted)",
7398
- fontSize: "var(--iris-font-size-md, 14px)"
7399
- }
7400
- },
7401
- t("list.empty")
7402
- )
7403
- ] : [
7404
- vue.h("li", {
7405
- role: "presentation",
7406
- "aria-hidden": "true",
7407
- "data-iris-select-spacer": "",
7408
- "data-iris-select-spacer-type": "top",
7409
- style: { height: `${vstate.value.offsetBefore}px` }
7410
- }),
7411
- ...vstate.value.items.map((item) => {
7412
- const opt = list[item.index];
7413
- if (!opt) return null;
7414
- const active = item.index === activeIndex.value;
7415
- const selected = props.multiple ? selectedValues.value.includes(opt.value) : opt.value === currentValue.value;
7416
- return vue.h(
7417
- "li",
7418
- {
7419
- key: String(opt.value ?? item.index),
7420
- role: "option",
7421
- tabindex: active ? 0 : -1,
7422
- "aria-selected": selected ? "true" : "false",
7423
- "aria-disabled": opt.disabled ? "true" : void 0,
7424
- "aria-setsize": list.length,
7425
- "aria-posinset": item.index + 1,
7426
- "data-iris-list-index": item.index,
7427
- "data-iris-list-item": "",
7428
- "data-state": selected ? "selected" : active ? "active" : "idle",
7429
- onClick: opt.disabled ? void 0 : () => {
7430
- activeIndex.value = item.index;
7431
- onSelect(opt);
7432
- },
7433
- onFocus: () => {
7434
- activeIndex.value = item.index;
7435
- },
7436
- onMouseenter: () => {
7437
- activeIndex.value = item.index;
7438
- },
7439
- style: optionStyle(selected, active, !!opt.disabled)
7440
- },
7441
- [
7442
- vue.h(
7443
- "span",
7444
- { style: { flex: "1", minWidth: "0" } },
7445
- opt.label ?? String(opt.value)
7446
- ),
7447
- selected ? check() : null
7448
- ]
7449
- );
7450
- }),
7451
- vue.h("li", {
7452
- role: "presentation",
7453
- "aria-hidden": "true",
7454
- "data-iris-select-spacer": "",
7455
- "data-iris-select-spacer-type": "bottom",
7456
- style: {
7457
- height: `${vstate.value.totalSize - vstate.value.offsetBefore - vstate.value.items.length * ROW_HEIGHT3}px`
7458
- }
7459
- })
7460
- ]
7461
- );
7462
- };
7463
7472
  return () => vue.h(
7464
7473
  IrisPopover,
7465
7474
  {
@@ -7506,7 +7515,20 @@ var IrisSelect = vue.defineComponent({
7506
7515
  style: { padding: "var(--iris-padding-sm)", minWidth: "180px" },
7507
7516
  ...props.teleport !== void 0 ? { teleport: props.teleport } : {}
7508
7517
  },
7509
- () => props.virtual ? renderVirtualListbox() : vue.h(IrisList, {
7518
+ () => props.virtual ? renderVirtualSelectListbox({
7519
+ items: props.items,
7520
+ translate: t,
7521
+ listboxRef,
7522
+ onListKeyDown,
7523
+ virtualizer: v,
7524
+ state: vstate,
7525
+ activeIndex,
7526
+ multiple: props.multiple,
7527
+ selectedValues,
7528
+ currentValue,
7529
+ onSelect,
7530
+ rowHeight: ROW_HEIGHT3
7531
+ }) : vue.h(IrisList, {
7510
7532
  items: props.items,
7511
7533
  modelValue: currentValue.value,
7512
7534
  onSelect,
@@ -7968,8 +7990,8 @@ var IrisSkeleton = vue.defineComponent({
7968
7990
  vue.onMounted(installSkeletonStyles);
7969
7991
  const style = vue.computed(() => {
7970
7992
  const w = props.width !== void 0 ? toCss(props.width) : defaultWidth(props.shape);
7971
- const h124 = props.height !== void 0 ? toCss(props.height) : defaultHeight(props.shape, props.width);
7972
- return { width: w, height: h124 };
7993
+ const h136 = props.height !== void 0 ? toCss(props.height) : defaultHeight(props.shape, props.width);
7994
+ return { width: w, height: h136 };
7973
7995
  });
7974
7996
  return () => vue.h("div", {
7975
7997
  ...attrs,
@@ -9080,7 +9102,7 @@ var IrisChip = vue.defineComponent({
9080
9102
  });
9081
9103
 
9082
9104
  // src/primitives/accordion/context.ts
9083
- var AccordionContextKey = /* @__PURE__ */ Symbol("IrisAccordion");
9105
+ var AccordionContextKey = Symbol("IrisAccordion");
9084
9106
 
9085
9107
  // src/primitives/accordion/Accordion.ts
9086
9108
  var IrisAccordion = vue.defineComponent({
@@ -9318,7 +9340,7 @@ var IrisAccordionItem = vue.defineComponent({
9318
9340
  });
9319
9341
 
9320
9342
  // src/primitives/stepper/context.ts
9321
- var StepperContextKey = /* @__PURE__ */ Symbol("IrisStepper");
9343
+ var StepperContextKey = Symbol("IrisStepper");
9322
9344
 
9323
9345
  // src/primitives/stepper/Stepper.ts
9324
9346
  var IrisStepper = vue.defineComponent({
@@ -9566,7 +9588,7 @@ var IrisStepperStep = vue.defineComponent({
9566
9588
  });
9567
9589
 
9568
9590
  // src/primitives/toggle-group/context.ts
9569
- var ToggleGroupContextKey = /* @__PURE__ */ Symbol("IrisToggleGroup");
9591
+ var ToggleGroupContextKey = Symbol("IrisToggleGroup");
9570
9592
  var SIZE_PADDING = {
9571
9593
  sm: "4px 10px",
9572
9594
  md: "6px 14px",
@@ -10496,14 +10518,252 @@ var tableControlProps = {
10496
10518
  default: void 0
10497
10519
  }
10498
10520
  };
10521
+
10522
+ // src/primitives/table/props.ts
10523
+ var tableProps = {
10524
+ columns: {
10525
+ type: Array,
10526
+ required: true
10527
+ },
10528
+ data: {
10529
+ type: Array,
10530
+ required: false,
10531
+ default: void 0
10532
+ },
10533
+ rowKey: { type: String, default: "id" },
10534
+ ...tableControlProps,
10535
+ /** Multi-column sort (vxe sort-config.multiple parity). */
10536
+ multiSort: { type: Boolean, default: false },
10537
+ multiSortState: {
10538
+ type: Array,
10539
+ default: void 0
10540
+ },
10541
+ defaultMultiSort: {
10542
+ type: Array,
10543
+ default: void 0
10544
+ },
10545
+ striped: { type: Boolean, default: false },
10546
+ editConfig: {
10547
+ type: Object,
10548
+ default: void 0
10549
+ },
10550
+ bordered: { type: Boolean, default: true },
10551
+ /** Row-density preset; invalid runtime values fail closed to comfortable. */
10552
+ density: {
10553
+ type: String,
10554
+ default: "comfortable"
10555
+ },
10556
+ /** Show the local density cycle button in the table toolbar. */
10557
+ densityToggle: { type: Boolean, default: false },
10558
+ /** Show a live formatter preview while an editor is open. */
10559
+ editPreview: { type: Boolean, default: false },
10560
+ /** Highlight committed cells matching the active inline draft. */
10561
+ pattern: { type: Boolean, default: false },
10562
+ /** Alias for pattern feedback. */
10563
+ patternFill: { type: Boolean, default: false },
10564
+ /** Named snapshots available in the table toolbar. */
10565
+ views: { type: Object, default: void 0 },
10566
+ /** Controlled active named view notification. */
10567
+ onActiveViewChange: {
10568
+ type: Function,
10569
+ default: void 0
10570
+ },
10571
+ /** Optional tab strip; tab clicks apply listed view names in order. */
10572
+ tableTabs: { type: Array, default: void 0 },
10573
+ /** Show a draggable boundary for the leading left-pinned columns. */
10574
+ pinnedDrag: { type: Boolean, default: false },
10575
+ /** Called for each column whose pin side changes. */
10576
+ onColumnPinnedChange: {
10577
+ type: Function,
10578
+ default: void 0
10579
+ },
10580
+ /** Called once after a pinned-boundary commit. */
10581
+ onPinnedCountChange: { type: Function, default: void 0 },
10582
+ /** Infer leaf-column value kinds from the first non-empty data arrival and
10583
+ * fill only missing alignment defaults. Disabled by default. */
10584
+ autoDetectTypes: { type: Boolean, default: false },
10585
+ /** Below 480px, greedily hide the lowest-priority top-level columns until
10586
+ * the natural width fits; pinned columns survive. */
10587
+ responsive: { type: Boolean, default: false },
10588
+ /** Extra bare row sets appended as named CSV segments by the imperative handle. */
10589
+ exportNames: {
10590
+ type: Array,
10591
+ default: void 0
10592
+ },
10593
+ resizableColumns: { type: Boolean, default: false },
10594
+ columnWidths: {
10595
+ type: Object,
10596
+ default: void 0
10597
+ },
10598
+ virtualScroll: {
10599
+ type: Object,
10600
+ default: void 0
10601
+ },
10602
+ loading: { type: Boolean, default: false },
10603
+ error: { type: Boolean, default: false },
10604
+ /** Print-friendly mode: marks the root so toolbar/form chrome is hidden by print CSS. */
10605
+ printable: { type: Boolean, default: false },
10606
+ /** Show a confirmation preview before the toolbar CSV import callback. */
10607
+ importPreview: { type: Boolean, default: false },
10608
+ onRetry: { type: Function, default: void 0 },
10609
+ columnVirtualization: { type: Boolean, default: false },
10610
+ keyboardNavigation: { type: Boolean, default: false },
10611
+ cellRange: { type: Boolean, default: false },
10612
+ /** Range clipboard copy; copyWithFormat uses column formatter output. */
10613
+ clipConfig: { type: Object, default: void 0 },
10614
+ renderDetail: {
10615
+ type: Function,
10616
+ default: void 0
10617
+ },
10618
+ rowExpandable: {
10619
+ type: Function,
10620
+ default: void 0
10621
+ },
10622
+ defaultExpandedRowKeys: {
10623
+ type: Array,
10624
+ default: void 0
10625
+ },
10626
+ getSubRows: {
10627
+ type: Function,
10628
+ default: void 0
10629
+ },
10630
+ treeSelectionCascade: { type: Boolean, default: false },
10631
+ proxyConfig: {
10632
+ type: Object,
10633
+ default: void 0
10634
+ },
10635
+ formConfig: {
10636
+ type: Object,
10637
+ default: void 0
10638
+ },
10639
+ toolbar: {
10640
+ type: Object,
10641
+ default: void 0
10642
+ },
10643
+ columnVisibility: {
10644
+ type: Object,
10645
+ default: void 0
10646
+ },
10647
+ filters: {
10648
+ type: Object,
10649
+ default: void 0
10650
+ },
10651
+ filterValues: {
10652
+ type: Object,
10653
+ default: void 0
10654
+ },
10655
+ onFilterValuesChange: {
10656
+ type: Function,
10657
+ default: void 0
10658
+ },
10659
+ contextMenu: {
10660
+ type: Object,
10661
+ default: void 0
10662
+ },
10663
+ lazyLoad: {
10664
+ type: Function,
10665
+ default: void 0
10666
+ },
10667
+ seq: { type: Boolean, default: false },
10668
+ seqStartIndex: { type: Number, default: 1 },
10669
+ spanMethod: {
10670
+ type: Function,
10671
+ default: void 0
10672
+ },
10673
+ columnDrag: {
10674
+ type: Object,
10675
+ default: void 0
10676
+ },
10677
+ rowDrag: {
10678
+ type: Object,
10679
+ default: void 0
10680
+ },
10681
+ onDataChange: {
10682
+ type: Function,
10683
+ default: void 0
10684
+ }
10685
+ };
10686
+ var SELECTION_COL_WIDTH = 40;
10687
+ var EXPAND_COL_WIDTH = 40;
10688
+ var SEQ_COL_WIDTH = 40;
10689
+ var DRAG_COL_WIDTH = 40;
10690
+ var DEFAULT_COL_WIDTH = 140;
10691
+ var DEFAULT_MIN_WIDTH = 60;
10692
+ var RESIZE_STEP = 16;
10499
10693
  function getCellValue(row, column) {
10500
10694
  const key = column.dataIndex ?? column.key;
10501
10695
  return row[key];
10502
10696
  }
10503
-
10504
- // src/primitives/table/useTableSort.ts
10505
- function useTableSort(data, options) {
10506
- const sortProp = vue.computed(() => options.sort === void 0 ? void 0 : vue.toValue(options.sort));
10697
+ function resolveSpan(occupied, rowIndex, columnIndex, method) {
10698
+ if (!method) return { rowspan: 1, colspan: 1 };
10699
+ const key = `${rowIndex}:${columnIndex}`;
10700
+ if (occupied.has(key)) return null;
10701
+ const span = method({ rowIndex, columnIndex });
10702
+ const rowspan = span?.rowspan ?? 1;
10703
+ const colspan = span?.colspan ?? 1;
10704
+ if (rowspan > 1) {
10705
+ for (let row = 1; row < rowspan; row += 1) occupied.add(`${rowIndex + row}:${columnIndex}`);
10706
+ }
10707
+ if (colspan > 1) {
10708
+ for (let column = 1; column < colspan; column += 1) {
10709
+ occupied.add(`${rowIndex}:${columnIndex + column}`);
10710
+ }
10711
+ }
10712
+ return { rowspan, colspan };
10713
+ }
10714
+ function resolveInitialWidth(col) {
10715
+ if (typeof col.width === "number") return col.width;
10716
+ if (typeof col.width === "string") {
10717
+ const match = col.width.match(/^(\d+(?:\.\d+)?)px$/);
10718
+ if (match) return Number(match[1]);
10719
+ }
10720
+ return DEFAULT_COL_WIDTH;
10721
+ }
10722
+ function computeVisibleColSet(enabled, columns, scrollLeft, viewportWidth, widths) {
10723
+ if (!enabled) return null;
10724
+ const range = core.computeVirtualRange({
10725
+ itemCount: columns.length,
10726
+ scrollTop: scrollLeft,
10727
+ viewportSize: viewportWidth,
10728
+ itemSize: (index) => widths[columns[index].key] ?? resolveInitialWidth(columns[index]),
10729
+ buffer: 2
10730
+ });
10731
+ const visible = /* @__PURE__ */ new Set();
10732
+ for (let index = range.startIndex; index <= range.endIndex; index += 1) visible.add(index);
10733
+ columns.forEach((column, index) => {
10734
+ if (column.pinned) visible.add(index);
10735
+ });
10736
+ return visible;
10737
+ }
10738
+ function cellId(rowIdent, columnKey) {
10739
+ return `${rowIdent}::${columnKey}`;
10740
+ }
10741
+
10742
+ // src/primitives/table/useTableSort.ts
10743
+ function buildSorter(col) {
10744
+ if (col.sorter) return col.sorter;
10745
+ return (a, b) => core.compareValues(getCellValue(a, col), getCellValue(b, col));
10746
+ }
10747
+ function buildMultiSortComparator(leafColumns, state) {
10748
+ if (state.length === 0) return null;
10749
+ const colMap = new Map(leafColumns.map((c) => [c.key, c]));
10750
+ const chain = [];
10751
+ for (const s of state) {
10752
+ const col = colMap.get(s.key);
10753
+ if (!col) continue;
10754
+ chain.push({ dir: s.direction === "asc" ? 1 : -1, sorter: buildSorter(col) });
10755
+ }
10756
+ if (chain.length === 0) return null;
10757
+ return (a, b) => {
10758
+ for (const step of chain) {
10759
+ const cmp = step.sorter(a, b);
10760
+ if (cmp !== 0) return cmp * step.dir;
10761
+ }
10762
+ return 0;
10763
+ };
10764
+ }
10765
+ function useTableSort(data, options) {
10766
+ const sortProp = vue.computed(() => options.sort === void 0 ? void 0 : vue.toValue(options.sort));
10507
10767
  const internalSortValue = vue.ref(options.defaultSort ?? null);
10508
10768
  const sortState = vue.computed({
10509
10769
  get: () => sortProp.value === void 0 ? internalSortValue.value : sortProp.value ?? null,
@@ -10512,19 +10772,33 @@ function useTableSort(data, options) {
10512
10772
  options.onSortChange?.(val);
10513
10773
  }
10514
10774
  });
10775
+ const multiEnabled = vue.computed(() => vue.toValue(options.multiSort) === true);
10776
+ const multiSortProp = vue.computed(
10777
+ () => options.multiSortState === void 0 ? void 0 : vue.toValue(options.multiSortState)
10778
+ );
10779
+ const internalMultiSort = vue.ref(options.defaultMultiSort ?? []);
10780
+ const multiSortState = vue.computed({
10781
+ get: () => multiSortProp.value === void 0 ? internalMultiSort.value : multiSortProp.value,
10782
+ set: (val) => {
10783
+ if (multiSortProp.value === void 0) internalMultiSort.value = val;
10784
+ options.onMultiSortChange?.(val);
10785
+ }
10786
+ });
10515
10787
  const sortComparator = vue.computed(() => {
10516
10788
  const s = sortState.value;
10517
10789
  if (!s) return null;
10518
10790
  const col = vue.toValue(options.leafColumns).find((c) => c.key === s.key);
10519
10791
  if (!col) return null;
10520
10792
  const dir = s.direction === "asc" ? 1 : -1;
10521
- const sorter = col.sorter ?? ((a, b) => core.compareValues(getCellValue(a, col), getCellValue(b, col)));
10522
- return (a, b) => sorter(a, b) * dir;
10793
+ return (a, b) => buildSorter(col)(a, b) * dir;
10523
10794
  });
10795
+ const multiSortComparator = vue.computed(
10796
+ () => buildMultiSortComparator(vue.toValue(options.leafColumns), multiSortState.value)
10797
+ );
10524
10798
  const sortedData = vue.computed(() => {
10525
- const compare = sortComparator.value;
10526
- if (!compare) return data.value;
10527
- return [...data.value].sort(compare);
10799
+ const comparator = multiEnabled.value ? multiSortComparator.value : sortComparator.value;
10800
+ if (!comparator) return data.value;
10801
+ return [...data.value].sort(comparator);
10528
10802
  });
10529
10803
  function setSort(next) {
10530
10804
  if (sortProp.value === void 0) internalSortValue.value = next;
@@ -10541,172 +10815,1991 @@ function useTableSort(data, options) {
10541
10815
  setSort({ key: col.key, direction: "desc" });
10542
10816
  return;
10543
10817
  }
10544
- setSort(null);
10818
+ setSort(null);
10819
+ }
10820
+ function setMultiSort(next) {
10821
+ if (multiSortProp.value === void 0) internalMultiSort.value = next;
10822
+ options.onMultiSortChange?.(next);
10823
+ }
10824
+ function cycleMultiSort(col) {
10825
+ if (!col.sortable) return;
10826
+ const idx = multiSortState.value.findIndex((s) => s.key === col.key);
10827
+ if (idx < 0) {
10828
+ setMultiSort([...multiSortState.value, { key: col.key, direction: "asc" }]);
10829
+ return;
10830
+ }
10831
+ const next = [...multiSortState.value];
10832
+ if (next[idx].direction === "asc") {
10833
+ next[idx] = { key: col.key, direction: "desc" };
10834
+ setMultiSort(next);
10835
+ return;
10836
+ }
10837
+ next.splice(idx, 1);
10838
+ setMultiSort(next);
10839
+ }
10840
+ return {
10841
+ sortState,
10842
+ cycleSort,
10843
+ setSort,
10844
+ sortComparator,
10845
+ sortedData,
10846
+ multiSortState,
10847
+ cycleMultiSort,
10848
+ setMultiSort,
10849
+ multiSortComparator
10850
+ };
10851
+ }
10852
+ function mergeFilterValues(filters, filterValues) {
10853
+ const next = { ...filters };
10854
+ for (const [key, values] of Object.entries(filterValues)) {
10855
+ if (values.length > 0) next[key] = values.join(",");
10856
+ }
10857
+ return next;
10858
+ }
10859
+ function useTableProxy(options) {
10860
+ const cfg = vue.computed(() => vue.toValue(options.proxyConfig));
10861
+ const queryRef = vue.ref(void 0);
10862
+ vue.watch(
10863
+ () => cfg.value?.query,
10864
+ (query) => {
10865
+ queryRef.value = query;
10866
+ },
10867
+ { immediate: true }
10868
+ );
10869
+ const proxy = vue.shallowRef(null);
10870
+ const state = vue.shallowRef({
10871
+ data: [],
10872
+ total: 0,
10873
+ loading: false,
10874
+ error: null,
10875
+ params: { page: 1, pageSize: 10, sort: null, filters: {} }
10876
+ });
10877
+ let unsubscribe = null;
10878
+ let mounted = false;
10879
+ const attach = (ctrl) => {
10880
+ unsubscribe?.();
10881
+ proxy.value = ctrl;
10882
+ state.value = ctrl.getState();
10883
+ unsubscribe = ctrl.subscribe((snapshot) => {
10884
+ state.value = snapshot;
10885
+ });
10886
+ };
10887
+ const detach = () => {
10888
+ unsubscribe?.();
10889
+ unsubscribe = null;
10890
+ proxy.value?.destroy();
10891
+ proxy.value = null;
10892
+ state.value = {
10893
+ data: [],
10894
+ total: 0,
10895
+ loading: false,
10896
+ error: null,
10897
+ params: { page: 1, pageSize: 10, sort: null, filters: {} }
10898
+ };
10899
+ };
10900
+ vue.watch(
10901
+ () => cfg.value === void 0,
10902
+ (absent) => {
10903
+ if (absent) {
10904
+ detach();
10905
+ return;
10906
+ }
10907
+ if (proxy.value) return;
10908
+ const config = cfg.value;
10909
+ const remoteSort = vue.toValue(options.remoteSort) === true;
10910
+ const remoteFilter = vue.toValue(options.remoteFilter) === true;
10911
+ const multiSort = vue.toValue(options.multiSort) === true;
10912
+ const ctrl = core.createRemoteTableSource({
10913
+ query: (params) => queryRef.value(params),
10914
+ autoLoad: false,
10915
+ initialParams: {
10916
+ page: config.defaultPage ?? 1,
10917
+ pageSize: config.pageSize ?? 10,
10918
+ sort: remoteSort ? vue.toValue(options.sort) ?? options.defaultSort ?? null : null,
10919
+ sorts: remoteSort && multiSort ? vue.toValue(options.multiSortState) ?? options.defaultMultiSort ?? [] : void 0,
10920
+ filters: remoteFilter ? mergeFilterValues(vue.toValue(options.filters) ?? {}, vue.toValue(options.filterValues) ?? {}) : {}
10921
+ }
10922
+ });
10923
+ attach(ctrl);
10924
+ if (mounted && config.autoLoad !== false) void ctrl.request();
10925
+ },
10926
+ { immediate: true }
10927
+ );
10928
+ vue.onScopeDispose(detach);
10929
+ vue.onMounted(() => {
10930
+ mounted = true;
10931
+ const config = cfg.value;
10932
+ if (config && proxy.value && config.autoLoad !== false) void proxy.value.request();
10933
+ });
10934
+ const liveData = vue.shallowRef([]);
10935
+ vue.watch(
10936
+ state,
10937
+ (snapshot) => {
10938
+ liveData.value = snapshot.data;
10939
+ },
10940
+ { immediate: true }
10941
+ );
10942
+ return {
10943
+ proxy: vue.computed(() => proxy.value),
10944
+ state: vue.computed(() => state.value),
10945
+ liveData,
10946
+ setParams: (partial) => proxy.value ? proxy.value.setParams(partial) : false,
10947
+ refetch: () => proxy.value ? proxy.value.refetch() : Promise.resolve()
10948
+ };
10949
+ }
10950
+ function exportCsv(rows, columns) {
10951
+ return core.toCsv(
10952
+ rows,
10953
+ columns.map((column) => ({
10954
+ key: column.key,
10955
+ title: column.title,
10956
+ dataIndex: typeof column.dataIndex === "string" ? column.dataIndex : void 0
10957
+ }))
10958
+ );
10959
+ }
10960
+ async function downloadCsv(filename, csv) {
10961
+ const BOM = String.fromCharCode(65279);
10962
+ await core.downloadFile({
10963
+ filename,
10964
+ content: BOM + csv,
10965
+ mimeType: "text/csv;charset=utf-8;"
10966
+ });
10967
+ }
10968
+ function renderFormSection(ctx) {
10969
+ const fc = ctx.formConfig;
10970
+ if (!fc) return null;
10971
+ return vue.h(
10972
+ "form",
10973
+ {
10974
+ "data-iris-table-form": "",
10975
+ onSubmit: (e) => {
10976
+ e.preventDefault();
10977
+ ctx.handleFormSubmit();
10978
+ },
10979
+ onReset: (e) => {
10980
+ e.preventDefault();
10981
+ ctx.handleFormReset();
10982
+ },
10983
+ style: {
10984
+ display: "flex",
10985
+ flexWrap: "wrap",
10986
+ alignItems: "flex-end",
10987
+ gap: "var(--iris-space-sm, 12px)",
10988
+ padding: "var(--iris-space-sm, 12px)",
10989
+ border: "1px solid var(--iris-border)",
10990
+ borderBottom: "none",
10991
+ background: "var(--iris-surface)",
10992
+ fontSize: "var(--iris-font-size-sm, 13px)"
10993
+ }
10994
+ },
10995
+ [
10996
+ ...fc.fields.map(
10997
+ (field) => vue.h(
10998
+ "div",
10999
+ {
11000
+ key: field.key,
11001
+ "data-iris-table-form-field": field.key,
11002
+ style: { minWidth: 180 }
11003
+ },
11004
+ [
11005
+ vue.h(
11006
+ IrisFormField,
11007
+ { label: field.label, size: "sm" },
11008
+ {
11009
+ default: () => field.type === "select" ? vue.h(IrisSelect, {
11010
+ items: (field.options ?? []).map((o) => ({
11011
+ value: o.value,
11012
+ label: o.label
11013
+ })),
11014
+ modelValue: ctx.formDraft.value[field.key] ?? "",
11015
+ placeholder: field.placeholder ?? ctx.t("select.placeholder"),
11016
+ size: "sm",
11017
+ "onUpdate:modelValue": (v) => ctx.setFormValue(field.key, String(v ?? ""))
11018
+ }) : vue.h(IrisInput, {
11019
+ modelValue: ctx.formDraft.value[field.key] ?? "",
11020
+ placeholder: field.placeholder,
11021
+ size: "sm",
11022
+ "onUpdate:modelValue": (v) => ctx.setFormValue(field.key, String(v ?? ""))
11023
+ })
11024
+ }
11025
+ )
11026
+ ]
11027
+ )
11028
+ ),
11029
+ vue.h("div", { style: { display: "flex", gap: "var(--iris-space-xs, 8px)" } }, [
11030
+ vue.h(
11031
+ IrisButton,
11032
+ { type: "submit", size: "sm", "data-iris-table-form-submit": "" },
11033
+ { default: () => fc.submitText ?? ctx.t("table.formSubmit") }
11034
+ ),
11035
+ vue.h(
11036
+ IrisButton,
11037
+ {
11038
+ type: "reset",
11039
+ variant: "outline",
11040
+ size: "sm",
11041
+ "data-iris-table-form-reset": ""
11042
+ },
11043
+ { default: () => fc.resetText ?? ctx.t("table.formReset") }
11044
+ )
11045
+ ])
11046
+ ]
11047
+ );
11048
+ }
11049
+ var toolbarBtnStyle = {
11050
+ border: "none",
11051
+ background: "transparent",
11052
+ cursor: "pointer",
11053
+ color: "var(--iris-muted)",
11054
+ fontSize: "var(--iris-font-size-md, 14px)"
11055
+ };
11056
+ function renderToolbarSection(ctx) {
11057
+ const tb = ctx.toolbar;
11058
+ if (!tb && !ctx.densityToggle) return null;
11059
+ const toolChildren = [];
11060
+ if (tb?.title) {
11061
+ toolChildren.push(
11062
+ vue.h("span", { style: { fontWeight: 600, color: "var(--iris-foreground)" } }, tb.title)
11063
+ );
11064
+ }
11065
+ toolChildren.push(vue.h("div", { style: { flex: "1" } }));
11066
+ if (tb?.onRefresh) {
11067
+ toolChildren.push(
11068
+ vue.h(
11069
+ "button",
11070
+ {
11071
+ type: "button",
11072
+ "data-iris-table-toolbar-refresh": "",
11073
+ "aria-label": ctx.t("table.refresh"),
11074
+ title: ctx.t("table.refresh"),
11075
+ onClick: () => {
11076
+ tb.onRefresh?.();
11077
+ void ctx.proxyCtrl.refetch();
11078
+ },
11079
+ style: toolbarBtnStyle
11080
+ },
11081
+ "\u21BB"
11082
+ )
11083
+ );
11084
+ }
11085
+ if (tb?.onExport) {
11086
+ toolChildren.push(
11087
+ vue.h(
11088
+ "button",
11089
+ {
11090
+ type: "button",
11091
+ "data-iris-table-toolbar-export": "",
11092
+ "aria-label": ctx.t("table.export"),
11093
+ title: ctx.t("table.export"),
11094
+ onClick: () => tb?.onExport?.(),
11095
+ style: toolbarBtnStyle
11096
+ },
11097
+ "\u21E9"
11098
+ )
11099
+ );
11100
+ }
11101
+ if (tb?.onImport) {
11102
+ toolChildren.push(
11103
+ vue.h("input", {
11104
+ ref: (el) => {
11105
+ ctx.importFileInput.value = el ?? null;
11106
+ },
11107
+ type: "file",
11108
+ accept: ".csv,text/csv",
11109
+ "data-iris-table-import-input": "",
11110
+ onChange: ctx.onImportFile,
11111
+ style: { display: "none" }
11112
+ }),
11113
+ vue.h(
11114
+ "button",
11115
+ {
11116
+ type: "button",
11117
+ "data-iris-table-toolbar-import": "",
11118
+ "aria-label": ctx.t("table.import"),
11119
+ title: ctx.t("table.import"),
11120
+ onClick: () => ctx.importFileInput.value?.click(),
11121
+ style: toolbarBtnStyle
11122
+ },
11123
+ "\u21EA"
11124
+ )
11125
+ );
11126
+ }
11127
+ if (tb?.batch && ctx.selectable === "multi" && ctx.displaySelection.value.length > 0) {
11128
+ toolChildren.push(
11129
+ vue.h(
11130
+ "button",
11131
+ {
11132
+ type: "button",
11133
+ "data-iris-table-toolbar-batch": "",
11134
+ "aria-label": tb.batch.label,
11135
+ title: tb.batch.label,
11136
+ onClick: () => tb.batch.onClick([...ctx.displaySelection.value]),
11137
+ style: {
11138
+ border: "none",
11139
+ cursor: "pointer",
11140
+ background: "var(--iris-primary)",
11141
+ color: "var(--iris-primary-foreground)",
11142
+ fontSize: "var(--iris-font-size-md, 14px)",
11143
+ display: "inline-flex",
11144
+ alignItems: "center",
11145
+ gap: "var(--iris-space-xxs, 4px)",
11146
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
11147
+ borderRadius: "var(--iris-radius-sm, 4px)"
11148
+ }
11149
+ },
11150
+ [
11151
+ tb.batch.icon ? vue.h(
11152
+ "span",
11153
+ { "aria-hidden": "true", style: { fontSize: "var(--iris-font-size-sm, 13px)" } },
11154
+ tb.batch.icon
11155
+ ) : null,
11156
+ tb.batch.label
11157
+ ]
11158
+ )
11159
+ );
11160
+ }
11161
+ if (tb?.buttons && tb.buttons.length > 0) {
11162
+ for (const btn of tb.buttons) {
11163
+ toolChildren.push(
11164
+ vue.h(
11165
+ "button",
11166
+ {
11167
+ key: btn.key,
11168
+ type: "button",
11169
+ "data-iris-table-toolbar-button": btn.key,
11170
+ [`data-iris-table-toolbar-button-${btn.key}`]: "",
11171
+ "aria-label": btn.label,
11172
+ title: btn.label,
11173
+ onClick: btn.onClick,
11174
+ style: {
11175
+ ...toolbarBtnStyle,
11176
+ color: "var(--iris-foreground)",
11177
+ display: "inline-flex",
11178
+ alignItems: "center",
11179
+ gap: "var(--iris-space-xxs, 4px)",
11180
+ padding: "0 var(--iris-space-xxs, 4px)"
11181
+ }
11182
+ },
11183
+ [
11184
+ btn.icon ? vue.h(
11185
+ "span",
11186
+ { "aria-hidden": "true", style: { fontSize: "var(--iris-font-size-sm, 13px)" } },
11187
+ btn.icon
11188
+ ) : null,
11189
+ btn.label
11190
+ ]
11191
+ )
11192
+ );
11193
+ }
11194
+ }
11195
+ if (ctx.densityToggle) {
11196
+ toolChildren.push(
11197
+ vue.h(
11198
+ "button",
11199
+ {
11200
+ type: "button",
11201
+ "data-iris-density-toggle": "",
11202
+ "data-iris-density": ctx.effectiveDensity,
11203
+ "aria-label": `${ctx.t("table.density")}: ${ctx.t(`table.density.${ctx.effectiveDensity}`)}`,
11204
+ title: `${ctx.t("table.density")}: ${ctx.t(`table.density.${ctx.effectiveDensity}`)}`,
11205
+ onClick: ctx.onDensityToggle,
11206
+ style: toolbarBtnStyle
11207
+ },
11208
+ ctx.t(`table.density.${ctx.effectiveDensity}`)
11209
+ )
11210
+ );
11211
+ }
11212
+ return vue.h(
11213
+ "div",
11214
+ {
11215
+ "data-iris-table-toolbar": "",
11216
+ style: {
11217
+ display: "flex",
11218
+ alignItems: "center",
11219
+ gap: "var(--iris-space-sm, 12px)",
11220
+ padding: "var(--iris-space-xs, 8px) var(--iris-space-sm, 12px)",
11221
+ border: "1px solid var(--iris-border)",
11222
+ borderBottom: "none",
11223
+ borderTopLeftRadius: "var(--iris-radius-md, 6px)",
11224
+ borderTopRightRadius: "var(--iris-radius-md, 6px)",
11225
+ background: "var(--iris-surface)",
11226
+ fontSize: "var(--iris-font-size-sm, 13px)"
11227
+ }
11228
+ },
11229
+ toolChildren
11230
+ );
11231
+ }
11232
+ function renderPagerSection(ctx) {
11233
+ if (!ctx.proxyCtrl.proxy.value) return null;
11234
+ const st = ctx.proxyCtrl.state.value;
11235
+ return vue.h(
11236
+ "div",
11237
+ {
11238
+ "data-iris-table-pager": "",
11239
+ style: {
11240
+ display: "flex",
11241
+ justifyContent: "flex-end",
11242
+ alignItems: "center",
11243
+ padding: "var(--iris-space-xs, 8px) var(--iris-space-sm, 12px)",
11244
+ borderTop: "1px solid var(--iris-border)",
11245
+ background: "var(--iris-surface)"
11246
+ }
11247
+ },
11248
+ [
11249
+ vue.h(IrisPagination, {
11250
+ modelValue: st.params.page,
11251
+ total: st.total,
11252
+ pageSize: st.params.pageSize,
11253
+ size: "sm",
11254
+ "onUpdate:modelValue": (page) => {
11255
+ ctx.proxyCtrl.setParams({ page });
11256
+ ctx.proxyConfig?.onPageChange?.(page, ctx.proxyCtrl.state.value.params.pageSize);
11257
+ }
11258
+ })
11259
+ ]
11260
+ );
11261
+ }
11262
+ function renderContextMenuSection(ctx) {
11263
+ const st = ctx.state.value;
11264
+ if (!st || !st.open) return null;
11265
+ const node = vue.h(
11266
+ "div",
11267
+ {
11268
+ ref: (el) => {
11269
+ ctx.menuRef.value = el ?? null;
11270
+ },
11271
+ role: "menu",
11272
+ "data-iris-table-context-menu": "",
11273
+ style: {
11274
+ ...ctx.styles.value,
11275
+ zIndex: "var(--iris-z-popover, 1000)",
11276
+ background: "var(--iris-surface-floating, var(--iris-surface))",
11277
+ color: "var(--iris-foreground)",
11278
+ border: "1px solid var(--iris-border)",
11279
+ borderRadius: "var(--iris-radius-md, 6px)",
11280
+ boxShadow: "var(--iris-shadow-lg)",
11281
+ padding: "var(--iris-space-xxs, 4px)",
11282
+ minWidth: "160px",
11283
+ display: "flex",
11284
+ flexDirection: "column"
11285
+ }
11286
+ },
11287
+ st.items.map(
11288
+ (item) => vue.h(
11289
+ "button",
11290
+ {
11291
+ key: item.key,
11292
+ type: "button",
11293
+ role: "menuitem",
11294
+ "data-iris-table-context-menu-item": item.key,
11295
+ disabled: item.disabled,
11296
+ "aria-disabled": item.disabled ? "true" : void 0,
11297
+ onClick: () => {
11298
+ ctx.contextMenu.onSelect(item.key, st.params);
11299
+ ctx.close();
11300
+ },
11301
+ style: {
11302
+ border: "none",
11303
+ background: "transparent",
11304
+ cursor: item.disabled ? "default" : "pointer",
11305
+ color: item.disabled ? "var(--iris-muted)" : "var(--iris-foreground)",
11306
+ font: "inherit",
11307
+ textAlign: "start",
11308
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
11309
+ borderRadius: "var(--iris-radius-sm, 4px)"
11310
+ }
11311
+ },
11312
+ item.label
11313
+ )
11314
+ )
11315
+ );
11316
+ return vue.h(vue.Teleport, { to: "body" }, [node]);
11317
+ }
11318
+ function renderFilterPanelSection(ctx) {
11319
+ const st = ctx.state.value;
11320
+ if (!st || !st.open) return null;
11321
+ const col = ctx.columns.find((c) => c.key === st.colKey);
11322
+ if (!col || !col.filterable) return null;
11323
+ const options = col.filterOptions ?? [];
11324
+ const node = vue.h(
11325
+ "div",
11326
+ {
11327
+ ref: (el) => {
11328
+ ctx.panelRef.value = el ?? null;
11329
+ },
11330
+ role: "dialog",
11331
+ "aria-label": ctx.t("table.filter"),
11332
+ "data-iris-table-filter-panel": "",
11333
+ "data-iris-table-filter-column": st.colKey,
11334
+ style: {
11335
+ ...ctx.styles.value,
11336
+ zIndex: "var(--iris-z-popover, 1000)",
11337
+ background: "var(--iris-surface-floating, var(--iris-surface))",
11338
+ color: "var(--iris-foreground)",
11339
+ border: "1px solid var(--iris-border)",
11340
+ borderRadius: "var(--iris-radius-md, 6px)",
11341
+ boxShadow: "var(--iris-shadow-lg)",
11342
+ padding: "var(--iris-space-sm, 12px)",
11343
+ minWidth: "180px",
11344
+ display: "flex",
11345
+ flexDirection: "column",
11346
+ gap: "var(--iris-space-xxs, 4px)"
11347
+ }
11348
+ },
11349
+ [
11350
+ ...options.map(
11351
+ (opt) => vue.h(
11352
+ "div",
11353
+ {
11354
+ key: opt.value,
11355
+ "data-iris-filter-option": opt.value,
11356
+ style: { display: "flex", alignItems: "center" }
11357
+ },
11358
+ [
11359
+ vue.h(
11360
+ IrisCheckbox,
11361
+ {
11362
+ modelValue: ctx.filterDraft.value.includes(opt.value),
11363
+ size: "sm",
11364
+ "onUpdate:modelValue": () => ctx.toggle(opt.value)
11365
+ },
11366
+ { default: () => opt.label }
11367
+ )
11368
+ ]
11369
+ )
11370
+ ),
11371
+ vue.h(
11372
+ "div",
11373
+ {
11374
+ style: {
11375
+ display: "flex",
11376
+ justifyContent: "flex-end",
11377
+ gap: "var(--iris-space-xs, 8px)",
11378
+ marginTop: "var(--iris-space-xs, 8px)"
11379
+ }
11380
+ },
11381
+ [
11382
+ vue.h(
11383
+ "button",
11384
+ {
11385
+ type: "button",
11386
+ "data-iris-filter-clear": "",
11387
+ onClick: () => {
11388
+ ctx.clear(st.colKey);
11389
+ ctx.close();
11390
+ },
11391
+ style: {
11392
+ border: "1px solid var(--iris-border)",
11393
+ background: "transparent",
11394
+ color: "var(--iris-foreground)",
11395
+ cursor: "pointer",
11396
+ font: "inherit",
11397
+ fontSize: "var(--iris-font-size-sm, 13px)",
11398
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
11399
+ borderRadius: "var(--iris-radius-sm, 4px)"
11400
+ }
11401
+ },
11402
+ ctx.t("table.filterClear")
11403
+ ),
11404
+ vue.h(
11405
+ "button",
11406
+ {
11407
+ type: "button",
11408
+ "data-iris-filter-confirm": "",
11409
+ onClick: () => {
11410
+ ctx.apply(st.colKey, ctx.filterDraft.value);
11411
+ ctx.close();
11412
+ },
11413
+ style: {
11414
+ border: "1px solid var(--iris-primary)",
11415
+ background: "var(--iris-primary)",
11416
+ color: "var(--iris-primary-foreground, #fff)",
11417
+ cursor: "pointer",
11418
+ font: "inherit",
11419
+ fontSize: "var(--iris-font-size-sm, 13px)",
11420
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
11421
+ borderRadius: "var(--iris-radius-sm, 4px)"
11422
+ }
11423
+ },
11424
+ ctx.t("table.filterConfirm")
11425
+ )
11426
+ ]
11427
+ )
11428
+ ]
11429
+ );
11430
+ return vue.h(vue.Teleport, { to: "body" }, [node]);
11431
+ }
11432
+ function renderImportPreviewSection(ctx) {
11433
+ if (!ctx.rows) return null;
11434
+ const columns = core.previewColumnsFromRows(ctx.rows);
11435
+ const cellStyle = {
11436
+ border: "1px solid var(--iris-border)",
11437
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
11438
+ whiteSpace: "nowrap"
11439
+ };
11440
+ const table = columns.length ? vue.h(
11441
+ "table",
11442
+ {
11443
+ "data-iris-import-preview-table": "",
11444
+ style: { borderCollapse: "collapse", fontSize: "var(--iris-font-size-sm, 13px)" }
11445
+ },
11446
+ [
11447
+ vue.h("thead", [
11448
+ vue.h(
11449
+ "tr",
11450
+ columns.map(
11451
+ (column) => vue.h(
11452
+ "th",
11453
+ {
11454
+ key: column,
11455
+ "data-iris-import-preview-header": column,
11456
+ style: {
11457
+ ...cellStyle,
11458
+ background: "var(--iris-surface)",
11459
+ color: "var(--iris-foreground)",
11460
+ textAlign: "start",
11461
+ fontWeight: 600
11462
+ }
11463
+ },
11464
+ column
11465
+ )
11466
+ )
11467
+ )
11468
+ ]),
11469
+ vue.h(
11470
+ "tbody",
11471
+ ctx.rows.slice(0, 5).map(
11472
+ (row, rowIndex) => vue.h(
11473
+ "tr",
11474
+ { key: rowIndex },
11475
+ columns.map(
11476
+ (column) => vue.h(
11477
+ "td",
11478
+ {
11479
+ key: column,
11480
+ "data-iris-import-preview-cell": `${rowIndex}:${column}`,
11481
+ style: { ...cellStyle, color: "var(--iris-foreground)" }
11482
+ },
11483
+ String(row[column] ?? "")
11484
+ )
11485
+ )
11486
+ )
11487
+ )
11488
+ )
11489
+ ]
11490
+ ) : null;
11491
+ const node = vue.h(
11492
+ "div",
11493
+ {
11494
+ "data-iris-import-preview-backdrop": "",
11495
+ onPointerdown: (event) => {
11496
+ if (event.target === event.currentTarget) ctx.onCancel();
11497
+ },
11498
+ style: {
11499
+ position: "fixed",
11500
+ inset: 0,
11501
+ background: "var(--iris-backdrop, rgba(0, 0, 0, 0.5))",
11502
+ zIndex: "var(--iris-z-modal, 1200)",
11503
+ display: "flex",
11504
+ alignItems: "center",
11505
+ justifyContent: "center",
11506
+ padding: "var(--iris-space-lg, 24px)"
11507
+ }
11508
+ },
11509
+ [
11510
+ vue.h(
11511
+ "div",
11512
+ {
11513
+ "data-iris-import-preview": "",
11514
+ role: "dialog",
11515
+ "aria-modal": "true",
11516
+ "aria-label": ctx.t("table.importPreview.title"),
11517
+ style: {
11518
+ background: "var(--iris-surface-floating, var(--iris-surface))",
11519
+ color: "var(--iris-foreground)",
11520
+ border: "1px solid var(--iris-border)",
11521
+ borderRadius: "var(--iris-radius-lg, 8px)",
11522
+ boxShadow: "var(--iris-shadow-xl)",
11523
+ padding: "var(--iris-space-lg, 24px)",
11524
+ maxWidth: "90vw",
11525
+ maxHeight: "85vh",
11526
+ overflow: "auto",
11527
+ display: "flex",
11528
+ flexDirection: "column",
11529
+ gap: "var(--iris-space-sm, 12px)",
11530
+ fontSize: "var(--iris-font-size-md, 14px)"
11531
+ }
11532
+ },
11533
+ [
11534
+ vue.h("div", { style: { fontWeight: 600 } }, ctx.t("table.importPreview.title")),
11535
+ table,
11536
+ ctx.rows.length > 5 ? vue.h(
11537
+ "div",
11538
+ {
11539
+ "data-iris-import-preview-total": "",
11540
+ style: { color: "var(--iris-muted)", fontSize: "var(--iris-font-size-xs, 12px)" }
11541
+ },
11542
+ ctx.t("table.total", { total: ctx.rows.length })
11543
+ ) : null,
11544
+ vue.h(
11545
+ "div",
11546
+ {
11547
+ style: {
11548
+ display: "flex",
11549
+ justifyContent: "flex-end",
11550
+ gap: "var(--iris-space-xs, 8px)"
11551
+ }
11552
+ },
11553
+ [
11554
+ vue.h(
11555
+ "button",
11556
+ {
11557
+ type: "button",
11558
+ "data-iris-import-preview-cancel": "",
11559
+ onClick: ctx.onCancel,
11560
+ style: {
11561
+ border: "1px solid var(--iris-border)",
11562
+ cursor: "pointer",
11563
+ background: "var(--iris-surface)",
11564
+ color: "var(--iris-foreground)",
11565
+ fontSize: "var(--iris-font-size-sm, 13px)",
11566
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
11567
+ borderRadius: "var(--iris-radius-sm, 4px)"
11568
+ }
11569
+ },
11570
+ ctx.t("table.importPreview.cancel")
11571
+ ),
11572
+ vue.h(
11573
+ "button",
11574
+ {
11575
+ type: "button",
11576
+ "data-iris-import-preview-confirm": "",
11577
+ onClick: ctx.onConfirm,
11578
+ style: {
11579
+ border: "none",
11580
+ cursor: "pointer",
11581
+ background: "var(--iris-primary)",
11582
+ color: "var(--iris-primary-foreground)",
11583
+ fontSize: "var(--iris-font-size-sm, 13px)",
11584
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
11585
+ borderRadius: "var(--iris-radius-sm, 4px)"
11586
+ }
11587
+ },
11588
+ ctx.t("table.importPreview.confirm")
11589
+ )
11590
+ ]
11591
+ )
11592
+ ]
11593
+ )
11594
+ ]
11595
+ );
11596
+ return vue.h(vue.Teleport, { to: "body" }, [node]);
11597
+ }
11598
+ function renderTableSummaryRow(ctx) {
11599
+ if (ctx.bodyData.length === 0 || !ctx.leafColumns.some((column) => column.summary)) return null;
11600
+ const summaryCells = [...ctx.leadingCells];
11601
+ for (let columnIndex = 0; columnIndex < ctx.leafColumns.length; columnIndex += 1) {
11602
+ const column = ctx.leafColumns[columnIndex];
11603
+ if (ctx.visibleColSet && !ctx.visibleColSet.has(columnIndex)) continue;
11604
+ const align = column.align ?? "left";
11605
+ const operation = column.summary;
11606
+ const value = operation ? core.aggregate(ctx.bodyData, (row) => ctx.getCellValue(row, column), operation) : null;
11607
+ const content = operation != null && value != null ? column.renderSummary ? column.renderSummary(value, ctx.bodyData) : String(value) : "";
11608
+ summaryCells.push(
11609
+ vue.h(
11610
+ "div",
11611
+ {
11612
+ key: column.key,
11613
+ role: "cell",
11614
+ "data-iris-table-cell": column.key,
11615
+ "data-iris-table-summary-cell": operation ? "" : void 0,
11616
+ "data-iris-table-pinned": column.pinned,
11617
+ style: {
11618
+ display: "flex",
11619
+ alignItems: "center",
11620
+ justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
11621
+ padding: "var(--iris-space-xs, 8px) var(--iris-padding-md)",
11622
+ fontSize: "var(--iris-font-size-md, 14px)",
11623
+ whiteSpace: "nowrap",
11624
+ overflow: "hidden",
11625
+ textOverflow: "ellipsis",
11626
+ ...ctx.visibleColSet ? { gridColumnStart: String(ctx.colTrack(columnIndex)) } : {},
11627
+ ...ctx.pinnedStyle(column.key)
11628
+ }
11629
+ },
11630
+ content
11631
+ )
11632
+ );
11633
+ }
11634
+ return vue.h(
11635
+ "div",
11636
+ {
11637
+ role: "row",
11638
+ "data-iris-table-row": "summary",
11639
+ style: {
11640
+ display: "grid",
11641
+ gridTemplateColumns: ctx.gridTemplate,
11642
+ fontWeight: "600",
11643
+ borderTop: "2px solid var(--iris-border)",
11644
+ background: "var(--iris-surface)"
11645
+ }
11646
+ },
11647
+ summaryCells
11648
+ );
11649
+ }
11650
+ function renderTableStateRow(ctx) {
11651
+ if (ctx.error) {
11652
+ return vue.h("div", { role: "row", "data-iris-table-row": "error", style: ctx.stateRowStyle }, [
11653
+ vue.h(
11654
+ "span",
11655
+ { style: { marginInlineEnd: ctx.onRetry ? "var(--iris-space-sm, 12px)" : "0px" } },
11656
+ ctx.errorContent ?? ""
11657
+ ),
11658
+ ctx.retry ? vue.h(
11659
+ "button",
11660
+ {
11661
+ type: "button",
11662
+ "data-iris-table-retry": "",
11663
+ onClick: ctx.retry,
11664
+ style: {
11665
+ border: "1px solid var(--iris-border)",
11666
+ background: "var(--iris-surface)",
11667
+ color: "var(--iris-foreground)",
11668
+ borderRadius: "var(--iris-radius-sm, 4px)",
11669
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
11670
+ fontSize: "var(--iris-font-size-sm, 13px)",
11671
+ cursor: "pointer"
11672
+ }
11673
+ },
11674
+ ctx.retryLabel
11675
+ ) : null
11676
+ ]);
11677
+ }
11678
+ if (ctx.loading) {
11679
+ return vue.h(
11680
+ "div",
11681
+ {
11682
+ role: "row",
11683
+ "aria-busy": "true",
11684
+ "data-iris-table-row": "loading",
11685
+ style: ctx.stateRowStyle
11686
+ },
11687
+ ctx.loadingContent ?? ""
11688
+ );
11689
+ }
11690
+ if (ctx.rowCount === 0) {
11691
+ return vue.h(
11692
+ "div",
11693
+ { role: "row", "data-iris-table-row": "empty", style: ctx.stateRowStyle },
11694
+ ctx.emptyContent ?? ""
11695
+ );
11696
+ }
11697
+ return null;
11698
+ }
11699
+ function createTableKeyboard(options) {
11700
+ const gridKeys = /* @__PURE__ */ new Set([
11701
+ "ArrowUp",
11702
+ "ArrowDown",
11703
+ "ArrowLeft",
11704
+ "ArrowRight",
11705
+ "Home",
11706
+ "End",
11707
+ "PageUp",
11708
+ "PageDown"
11709
+ ]);
11710
+ const handleGridKey = (event) => {
11711
+ if (!options.keyboardNavigation() || !gridKeys.has(event.key)) return;
11712
+ const target = event.target;
11713
+ if (target.dataset.gridRow === void 0) return;
11714
+ event.preventDefault();
11715
+ const next = core.nextGridCell(
11716
+ options.focused.value ?? { row: 0, col: 0 },
11717
+ event.key,
11718
+ { rowCount: options.rows().length, colCount: options.columns().length, pageSize: 10 }
11719
+ );
11720
+ options.focused.value = next;
11721
+ options.root()?.querySelector(`[data-grid-row="${next.row}"][data-grid-col="${next.col}"]`)?.focus();
11722
+ };
11723
+ const handleCellRangeKey = (event) => {
11724
+ if (!options.cellRange()) return;
11725
+ if (event.key === "Escape") {
11726
+ options.range.clearRange();
11727
+ return;
11728
+ }
11729
+ const arrows = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
11730
+ if (!event.shiftKey || !arrows.has(event.key)) return;
11731
+ const target = event.target;
11732
+ const row = target.dataset.irisCellRow;
11733
+ const col = target.dataset.irisCellCol;
11734
+ if (row === void 0 || col === void 0) return;
11735
+ event.preventDefault();
11736
+ const current = options.range.getState().active ?? { row: Number(row), col: Number(col) };
11737
+ 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;
11738
+ 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;
11739
+ options.range.extendRange(nextRow, nextCol);
11740
+ };
11741
+ const isInRange = (row, col) => {
11742
+ const { anchor, active } = options.rangeState.value;
11743
+ return Boolean(
11744
+ 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)
11745
+ );
11746
+ };
11747
+ const activeCellRange = () => {
11748
+ const { anchor, active } = options.rangeState.value;
11749
+ if (!anchor || !active) return null;
11750
+ return {
11751
+ start: { row: Math.min(anchor.row, active.row), col: Math.min(anchor.col, active.col) },
11752
+ end: { row: Math.max(anchor.row, active.row), col: Math.max(anchor.col, active.col) }
11753
+ };
11754
+ };
11755
+ const copyActiveRange = () => {
11756
+ const range = activeCellRange();
11757
+ const clip = options.clipConfig();
11758
+ if (!range || clip?.copy === false) return;
11759
+ void core.writeClipboardText(
11760
+ core.serializeTableRange(
11761
+ options.rows(),
11762
+ options.columns(),
11763
+ range,
11764
+ clip?.copyFormat,
11765
+ clip?.copyWithFormat === true
11766
+ )
11767
+ );
11768
+ };
11769
+ const handleClipboardKey = (event) => {
11770
+ const clip = options.clipConfig();
11771
+ if (!options.cellRange() || !clip || event.defaultPrevented) return;
11772
+ if (event.key.toLowerCase() !== "c" || !event.ctrlKey && !event.metaKey) return;
11773
+ if (clip.copy === false || !activeCellRange()) return;
11774
+ event.preventDefault();
11775
+ copyActiveRange();
11776
+ };
11777
+ return {
11778
+ handleRootKeyDown: (event) => {
11779
+ handleGridKey(event);
11780
+ handleCellRangeKey(event);
11781
+ handleClipboardKey(event);
11782
+ },
11783
+ isInRange,
11784
+ activeCellRange,
11785
+ copyActiveRange
11786
+ };
11787
+ }
11788
+ function computeResponsiveTableColumns(columns, containerWidth, leadingWidth, widthOf) {
11789
+ if (containerWidth <= 0 || containerWidth >= core.RESPONSIVE_NARROW_WIDTH) {
11790
+ return { columns, overflow: false };
11791
+ }
11792
+ const isPinned = (column) => column.children && column.children.length > 0 ? column.children.some(isPinned) : column.pinned !== void 0;
11793
+ const fitted = core.computeResponsiveColumns(columns, Math.max(1, containerWidth - leadingWidth), {
11794
+ widthOf: (column) => widthOf(column),
11795
+ isPinned: (column) => isPinned(column),
11796
+ narrowWidth: core.RESPONSIVE_NARROW_WIDTH - leadingWidth
11797
+ });
11798
+ const natural = fitted.reduce(
11799
+ (sum, column) => sum + (column.children && column.children.length > 0 ? core.flattenLeafColumns([column]).reduce((nested, leaf) => nested + widthOf(leaf), 0) : widthOf(column)),
11800
+ leadingWidth
11801
+ );
11802
+ return { columns: fitted, overflow: natural > containerWidth };
11803
+ }
11804
+
11805
+ // src/primitives/table/table-styles.ts
11806
+ var TABLE_STYLE_ID = "iris-table-row-styles";
11807
+ var TABLE_STYLES = `
11808
+ [data-iris-table] [role="row"]:hover {
11809
+ --iris-cell-bg: var(--iris-surface-hover);
11810
+ }
11811
+ [data-iris-table-row-selected="true"] {
11812
+ --iris-cell-bg: var(--iris-surface-selected);
11813
+ }
11814
+ @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; } }
11815
+ [data-iris-table][data-density="compact"] [data-iris-table-cell],
11816
+ [data-iris-table][data-density="compact"] [data-iris-table-header],
11817
+ [data-iris-table][data-density="compact"] [data-iris-table-summary-cell],
11818
+ [data-iris-table][data-density="compact"] [data-iris-table-footer-cell] { padding-block: 6px !important; }
11819
+ [data-iris-table][data-density="cozy"] [data-iris-table-cell],
11820
+ [data-iris-table][data-density="cozy"] [data-iris-table-header],
11821
+ [data-iris-table][data-density="cozy"] [data-iris-table-summary-cell],
11822
+ [data-iris-table][data-density="cozy"] [data-iris-table-footer-cell] { padding-block: 4px !important; }
11823
+ [data-iris-row-target="true"] {
11824
+ --iris-cell-bg: color-mix(in srgb, var(--iris-primary) 18%, var(--iris-background));
11825
+ background: color-mix(in srgb, var(--iris-primary) 18%, var(--iris-background));
11826
+ }
11827
+ `;
11828
+ function ensureTableStyles() {
11829
+ if (typeof document === "undefined" || document.getElementById(TABLE_STYLE_ID)) return;
11830
+ const style = document.createElement("style");
11831
+ style.id = TABLE_STYLE_ID;
11832
+ style.textContent = TABLE_STYLES;
11833
+ document.head.appendChild(style);
11834
+ }
11835
+
11836
+ // src/primitives/table/table-columns.ts
11837
+ function applyDetectedTableTypes(columns, detectedTypes) {
11838
+ const apply = (column) => {
11839
+ const kind = detectedTypes[column.key];
11840
+ const next = kind ? {
11841
+ ...column,
11842
+ ...kind === "number" ? column.align === void 0 ? { align: "right" } : null : column.align === void 0 ? { align: "left" } : null
11843
+ } : column;
11844
+ return next.children && next.children.length > 0 ? { ...next, children: next.children.map(apply) } : next;
11845
+ };
11846
+ return columns.map(apply);
11847
+ }
11848
+ function renderTableFilterTrigger(options) {
11849
+ const { column, leaf, active, open, label, onOpen } = options;
11850
+ if (!leaf || !column.filterable) return null;
11851
+ return vue.h(
11852
+ "button",
11853
+ {
11854
+ type: "button",
11855
+ "data-iris-filter-trigger": column.key,
11856
+ "aria-label": label,
11857
+ "aria-haspopup": "true",
11858
+ "aria-expanded": open ? "true" : void 0,
11859
+ "data-iris-filter-active": active ? "true" : void 0,
11860
+ onClick: (event) => onOpen(event, column.key),
11861
+ onKeydown: (event) => event.stopPropagation(),
11862
+ style: {
11863
+ border: "none",
11864
+ background: "transparent",
11865
+ cursor: "pointer",
11866
+ padding: "0",
11867
+ marginInlineStart: "var(--iris-space-xxs, 4px)",
11868
+ fontSize: "var(--iris-font-size-xs, 12px)",
11869
+ lineHeight: "1",
11870
+ color: active ? "var(--iris-primary)" : "var(--iris-muted)"
11871
+ }
11872
+ },
11873
+ "\u23F7"
11874
+ );
11875
+ }
11876
+ function renderTableSortIndicator(column, options) {
11877
+ if (!column.sortable) return null;
11878
+ const state = options.multiSort ? options.multiSortState.find((item) => item.key === column.key) ?? null : options.sort;
11879
+ const isActive = state?.key === column.key;
11880
+ const direction = isActive ? state.direction : null;
11881
+ const color = isActive ? "var(--iris-primary)" : "var(--iris-muted)";
11882
+ return vue.h(
11883
+ "span",
11884
+ {
11885
+ "aria-hidden": "true",
11886
+ style: {
11887
+ display: "inline-flex",
11888
+ flexDirection: "column",
11889
+ marginInlineStart: "var(--iris-space-xxs, 4px)",
11890
+ lineHeight: "0.6",
11891
+ fontSize: "var(--iris-font-size-xs, 12px)",
11892
+ color
11893
+ }
11894
+ },
11895
+ [
11896
+ vue.h("span", { style: { opacity: direction === "asc" ? "1" : "0.45" } }, "\u25B2"),
11897
+ vue.h("span", { style: { opacity: direction === "desc" ? "1" : "0.45" } }, "\u25BC")
11898
+ ]
11899
+ );
11900
+ }
11901
+
11902
+ // src/primitives/table/table-row-target.ts
11903
+ function createTableRowTarget(root) {
11904
+ let timer = null;
11905
+ const find = (key) => {
11906
+ const element = root();
11907
+ if (!element) return null;
11908
+ const keyString = String(key);
11909
+ return Array.from(element.querySelectorAll("[data-iris-table-row-key]")).find(
11910
+ (node) => node.getAttribute("data-iris-table-row-key") === keyString
11911
+ ) ?? null;
11912
+ };
11913
+ const scrollTo = (key) => {
11914
+ find(key)?.scrollIntoView?.({ block: "nearest" });
11915
+ };
11916
+ const goTo = (key) => {
11917
+ const row = find(key);
11918
+ if (!row) return;
11919
+ row.scrollIntoView?.({ block: "nearest" });
11920
+ root()?.querySelector('[data-iris-row-target="true"]')?.removeAttribute("data-iris-row-target");
11921
+ row.setAttribute("data-iris-row-target", "true");
11922
+ if (timer !== null) clearTimeout(timer);
11923
+ timer = setTimeout(() => {
11924
+ timer = null;
11925
+ row.removeAttribute("data-iris-row-target");
11926
+ }, 2e3);
11927
+ };
11928
+ const dispose = () => {
11929
+ if (timer !== null) clearTimeout(timer);
11930
+ timer = null;
11931
+ };
11932
+ return { find, scrollTo, goTo, dispose };
11933
+ }
11934
+ function createTablePinnedDrag(options) {
11935
+ const firstRightPinnedIndex = vue.computed(() => {
11936
+ const index = options.columns().findIndex((column) => column.pinned === "right");
11937
+ return index < 0 ? options.columns().length : index;
11938
+ });
11939
+ const pinnedBoundaryColumn = vue.computed(() => {
11940
+ if (!options.enabled()) return null;
11941
+ for (let index = firstRightPinnedIndex.value - 1; index >= 0; index -= 1) {
11942
+ const column = options.columns()[index];
11943
+ if (column?.pinned === "left") return column;
11944
+ }
11945
+ return null;
11946
+ });
11947
+ const handleRefs = /* @__PURE__ */ new Map();
11948
+ const wiredKeys = /* @__PURE__ */ new Set();
11949
+ const activeKey = vue.ref(null);
11950
+ const dragDx = vue.ref(0);
11951
+ const getHandleRef = (key) => {
11952
+ let handle = handleRefs.get(key);
11953
+ if (!handle) {
11954
+ handle = vue.ref(null);
11955
+ handleRefs.set(key, handle);
11956
+ }
11957
+ return handle;
11958
+ };
11959
+ const resolvePinnedCount = (dx) => {
11960
+ const columns = options.columns();
11961
+ const cap = firstRightPinnedIndex.value;
11962
+ const current = core.leftPinnedCount(columns, cap);
11963
+ let currentWidth = 0;
11964
+ for (let index = 0; index < current; index += 1) {
11965
+ const column = columns[index];
11966
+ if (column) currentWidth += options.widthOf(column);
11967
+ }
11968
+ return core.pinnedCountFromBudget(
11969
+ columns,
11970
+ (column) => options.widthOf(column),
11971
+ currentWidth + dx,
11972
+ cap
11973
+ );
11974
+ };
11975
+ const commitPinnedCount = (count) => {
11976
+ if (!options.enabled()) return;
11977
+ const columns = options.columns();
11978
+ const cap = firstRightPinnedIndex.value;
11979
+ const clamped = Math.max(0, Math.min(cap, count));
11980
+ const current = core.leftPinnedCount(columns, cap);
11981
+ if (clamped === current) return;
11982
+ for (let index = 0; index < cap; index += 1) {
11983
+ const column = columns[index];
11984
+ if (!column) continue;
11985
+ const target = index < clamped ? "left" : null;
11986
+ if (column.pinned === target) continue;
11987
+ options.onColumnPinnedChange?.(column.key, target);
11988
+ }
11989
+ options.onPinnedCountChange?.(clamped);
11990
+ };
11991
+ const wire = (key) => {
11992
+ if (wiredKeys.has(key)) return;
11993
+ wiredKeys.add(key);
11994
+ const handle = getHandleRef(key);
11995
+ useDrag({
11996
+ handle,
11997
+ disabled: vue.computed(() => !options.enabled()),
11998
+ onStart: () => {
11999
+ activeKey.value = key;
12000
+ dragDx.value = 0;
12001
+ },
12002
+ onDrag: ({ dx }) => {
12003
+ activeKey.value = key;
12004
+ dragDx.value = dx;
12005
+ },
12006
+ onEnd: ({ dx }) => {
12007
+ commitPinnedCount(resolvePinnedCount(dx));
12008
+ activeKey.value = null;
12009
+ dragDx.value = 0;
12010
+ }
12011
+ });
12012
+ };
12013
+ for (const column of options.columns()) wire(column.key);
12014
+ return (column) => {
12015
+ const boundary = pinnedBoundaryColumn.value;
12016
+ if (!options.enabled() || !boundary || boundary.key !== column.key) return null;
12017
+ const handle = getHandleRef(column.key);
12018
+ return vue.h(
12019
+ "span",
12020
+ {
12021
+ ref: (element) => {
12022
+ handle.value = element ?? null;
12023
+ },
12024
+ role: "separator",
12025
+ "aria-orientation": "vertical",
12026
+ "aria-label": `Adjust pinned column count at ${column.title}`,
12027
+ tabindex: 0,
12028
+ "data-iris-pinned-drag-handle": "",
12029
+ "data-column-key": column.key,
12030
+ "data-iris-pinned-drag-active": activeKey.value === column.key && dragDx.value !== 0 ? "true" : void 0,
12031
+ onPointerdown: (event) => event.stopPropagation(),
12032
+ onKeydown: (event) => {
12033
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
12034
+ event.preventDefault();
12035
+ event.stopPropagation();
12036
+ commitPinnedCount(resolvePinnedCount(0) + (event.key === "ArrowRight" ? 1 : -1));
12037
+ },
12038
+ style: {
12039
+ position: "absolute",
12040
+ top: "0",
12041
+ right: "0",
12042
+ bottom: "0",
12043
+ width: "8px",
12044
+ cursor: "col-resize",
12045
+ touchAction: "none",
12046
+ userSelect: "none",
12047
+ zIndex: "2",
12048
+ transform: activeKey.value === column.key && dragDx.value !== 0 ? `translateX(${dragDx.value}px)` : void 0
12049
+ }
12050
+ },
12051
+ vue.h("span", {
12052
+ "aria-hidden": "true",
12053
+ "data-iris-pinned-drag-line": "",
12054
+ style: {
12055
+ position: "absolute",
12056
+ top: "0",
12057
+ bottom: "0",
12058
+ insetInlineStart: "50%",
12059
+ width: "2px",
12060
+ background: "var(--iris-primary)",
12061
+ transform: "translateX(-50%)"
12062
+ }
12063
+ })
12064
+ );
12065
+ };
12066
+ }
12067
+ function useTableImport(isPreviewEnabled, getOnImport) {
12068
+ const importFileInput = vue.ref(null);
12069
+ const importPreviewRows = vue.ref(null);
12070
+ const handleImportFile = (event) => {
12071
+ const input = event.target;
12072
+ const file = input?.files?.[0];
12073
+ if (!file || !getOnImport() || typeof FileReader === "undefined") return;
12074
+ const reader = new FileReader();
12075
+ reader.onload = () => {
12076
+ const rows = core.rowsFromCsv(String(reader.result ?? ""));
12077
+ if (rows.length === 0) return;
12078
+ if (isPreviewEnabled()) importPreviewRows.value = rows;
12079
+ else getOnImport()?.(rows);
12080
+ };
12081
+ reader.readAsText(file);
12082
+ if (input) input.value = "";
12083
+ };
12084
+ const confirmImportPreview = () => {
12085
+ if (!importPreviewRows.value) return;
12086
+ getOnImport()?.(importPreviewRows.value);
12087
+ importPreviewRows.value = null;
12088
+ };
12089
+ const cancelImportPreview = () => {
12090
+ importPreviewRows.value = null;
12091
+ };
12092
+ vue.watch(importPreviewRows, (rows, _previous, onCleanup) => {
12093
+ if (!rows || typeof window === "undefined") return;
12094
+ const closeOnEscape = (event) => {
12095
+ if (event.key === "Escape") cancelImportPreview();
12096
+ };
12097
+ window.addEventListener("keydown", closeOnEscape);
12098
+ onCleanup(() => window.removeEventListener("keydown", closeOnEscape));
12099
+ });
12100
+ return {
12101
+ importFileInput,
12102
+ importPreviewRows,
12103
+ handleImportFile,
12104
+ confirmImportPreview,
12105
+ cancelImportPreview
12106
+ };
12107
+ }
12108
+ var editorInputStyle = (error) => ({
12109
+ width: "100%",
12110
+ border: `1px solid ${error ? "var(--iris-danger)" : "var(--iris-primary)"}`,
12111
+ borderRadius: "var(--iris-radius-sm)",
12112
+ padding: "var(--iris-space-xxs, 4px) var(--iris-padding-sm, 6px)",
12113
+ font: "inherit",
12114
+ background: "var(--iris-background)",
12115
+ color: "var(--iris-foreground)",
12116
+ outline: "none",
12117
+ boxShadow: "0 0 0 3px color-mix(in srgb, var(--iris-primary) 18%, transparent)"
12118
+ });
12119
+ function editorErrorVNode(editCellId, error) {
12120
+ return vue.h(
12121
+ "div",
12122
+ {
12123
+ id: `${editCellId}-error`,
12124
+ role: "alert",
12125
+ "data-iris-table-editor-error": "",
12126
+ style: {
12127
+ marginTop: "var(--iris-space-xxs, 4px)",
12128
+ fontSize: "var(--iris-font-size-xs, 12px)",
12129
+ color: "var(--iris-danger)"
12130
+ }
12131
+ },
12132
+ error
12133
+ );
12134
+ }
12135
+ var editPreviewStyle = {
12136
+ flexBasis: "100%",
12137
+ minWidth: "0",
12138
+ marginTop: "var(--iris-space-xxs, 4px)",
12139
+ fontSize: "var(--iris-font-size-xs, 12px)",
12140
+ color: "var(--iris-muted)"
12141
+ };
12142
+ function previewVNode(ctx, row, col, draft) {
12143
+ if (!ctx.editPreview || !col.formatter) return null;
12144
+ return vue.h(
12145
+ "div",
12146
+ { "data-iris-edit-preview": "", style: editPreviewStyle },
12147
+ ctx.previewValue(row, col, draft)
12148
+ );
12149
+ }
12150
+ function renderRowSessionContent(ctx, row, col, index, key, editCellId, session) {
12151
+ const error = session.error;
12152
+ const input = vue.h("input", {
12153
+ ref: (el) => {
12154
+ ctx.rowEditorRefs.set(col.key, el ?? null);
12155
+ },
12156
+ type: col.editor === "number" ? "number" : "text",
12157
+ value: session.draft,
12158
+ "data-iris-table-editor": "",
12159
+ "aria-invalid": error ? "true" : void 0,
12160
+ "aria-describedby": error ? `${editCellId}-error` : void 0,
12161
+ onInput: (e) => {
12162
+ session.draft = e.target.value;
12163
+ },
12164
+ onKeydown: (e) => {
12165
+ if (e.key === "Enter") {
12166
+ e.preventDefault();
12167
+ ctx.commitRowSession(key, col, index, editCellId);
12168
+ } else if (e.key === "Escape") {
12169
+ e.preventDefault();
12170
+ ctx.cancelRowEdit();
12171
+ }
12172
+ },
12173
+ onBlur: () => ctx.commitRowSession(key, col, index, editCellId),
12174
+ onClick: (e) => e.stopPropagation(),
12175
+ onDblclick: (e) => e.stopPropagation(),
12176
+ style: editorInputStyle(error)
12177
+ });
12178
+ const preview = previewVNode(ctx, row, col, session.draft);
12179
+ const children = [input, preview, error ? editorErrorVNode(editCellId, error) : null].filter(
12180
+ (node) => node !== null
12181
+ );
12182
+ return children.length === 1 ? children[0] : children;
12183
+ }
12184
+ function renderCellEditContent(ctx, row, col, index, editCellId) {
12185
+ const error = ctx.editError.value;
12186
+ const input = vue.h("input", {
12187
+ ref: (el) => {
12188
+ ctx.editorInputRef.value = el ?? null;
12189
+ },
12190
+ type: col.editor === "number" ? "number" : "text",
12191
+ value: ctx.editingDraft.value,
12192
+ "data-iris-table-editor": "",
12193
+ "aria-invalid": error ? "true" : void 0,
12194
+ "aria-describedby": error ? `${editCellId}-error` : void 0,
12195
+ onInput: (e) => {
12196
+ ctx.editingDraft.value = e.target.value;
12197
+ },
12198
+ onKeydown: (e) => {
12199
+ if (e.key === "Enter") {
12200
+ e.preventDefault();
12201
+ ctx.commitEdit(row, col, index);
12202
+ } else if (e.key === "Escape") {
12203
+ e.preventDefault();
12204
+ ctx.cancelEdit();
12205
+ }
12206
+ },
12207
+ onBlur: () => ctx.commitEdit(row, col, index),
12208
+ onClick: (e) => e.stopPropagation(),
12209
+ onDblclick: (e) => e.stopPropagation(),
12210
+ style: editorInputStyle(error)
12211
+ });
12212
+ const preview = previewVNode(ctx, row, col, ctx.editingDraft.value);
12213
+ const children = [input, preview, error ? editorErrorVNode(editCellId, error) : null].filter(
12214
+ (node) => node !== null
12215
+ );
12216
+ return children.length === 1 ? children[0] : children;
12217
+ }
12218
+ function renderGroupedHeader(ctx, matrix) {
12219
+ const lead = (ctx.showDrag ? 1 : 0) + (ctx.showSeq ? 1 : 0) + (ctx.showDetail ? 1 : 0) + (ctx.showSelection ? 1 : 0);
12220
+ const cells = [];
12221
+ if (ctx.showDrag) {
12222
+ cells.push(
12223
+ vue.h("div", {
12224
+ key: "__drag__",
12225
+ role: "columnheader",
12226
+ "data-iris-table-header": "__drag",
12227
+ style: { gridColumn: "1", gridRow: "1 / -1" }
12228
+ })
12229
+ );
12230
+ }
12231
+ if (ctx.showSeq) {
12232
+ cells.push(
12233
+ vue.h("div", {
12234
+ key: "__seq__",
12235
+ role: "columnheader",
12236
+ "data-iris-table-header": "__seq",
12237
+ style: { gridColumn: ctx.showDrag ? "2" : "1", gridRow: "1 / -1" }
12238
+ })
12239
+ );
12240
+ }
12241
+ if (ctx.showDetail) {
12242
+ cells.push(
12243
+ vue.h("div", {
12244
+ key: "__expand__",
12245
+ role: "columnheader",
12246
+ style: {
12247
+ gridColumn: `${(ctx.showDrag ? 1 : 0) + (ctx.showSeq ? 1 : 0) + 1}`,
12248
+ gridRow: "1 / -1"
12249
+ }
12250
+ })
12251
+ );
12252
+ }
12253
+ if (ctx.showSelection) {
12254
+ cells.push(
12255
+ vue.h(
12256
+ "div",
12257
+ {
12258
+ key: "__select__",
12259
+ role: "columnheader",
12260
+ "data-iris-table-header": "",
12261
+ style: {
12262
+ gridColumn: `${(ctx.showDrag ? 1 : 0) + (ctx.showSeq ? 1 : 0) + (ctx.showDetail ? 1 : 0) + 1}`,
12263
+ gridRow: "1 / -1",
12264
+ display: "flex",
12265
+ alignItems: "center",
12266
+ justifyContent: "center",
12267
+ padding: "8px 12px",
12268
+ background: "var(--iris-surface)",
12269
+ borderBottom: "1px solid var(--iris-border)"
12270
+ }
12271
+ },
12272
+ ctx.selectable === "multi" ? [
12273
+ [
12274
+ vue.h(IrisCheckbox, {
12275
+ modelValue: ctx.allSelected.value ? true : ctx.someSelected.value ? "indeterminate" : false,
12276
+ size: "sm",
12277
+ ariaLabel: ctx.t("table.selectAll"),
12278
+ "onUpdate:modelValue": ctx.toggleAll
12279
+ }),
12280
+ ctx.selection && ctx.selection.length > 0 ? vue.h(
12281
+ "span",
12282
+ {
12283
+ "data-iris-table-selected-count": "",
12284
+ style: {
12285
+ marginInlineStart: "var(--iris-space-xs, 8px)",
12286
+ fontSize: "var(--iris-font-size-sm, 13px)",
12287
+ color: "var(--iris-muted)",
12288
+ whiteSpace: "nowrap"
12289
+ }
12290
+ },
12291
+ ctx.t("table.selectedCount", { count: String(ctx.selection.length) })
12292
+ ) : null
12293
+ ]
12294
+ ] : ""
12295
+ )
12296
+ );
12297
+ }
12298
+ for (const rowCells of matrix) {
12299
+ for (const cell of rowCells) {
12300
+ const col = cell.column;
12301
+ const isLeaf = !col.children || col.children.length === 0;
12302
+ const sortable = isLeaf && col.sortable;
12303
+ const align = col.align ?? "left";
12304
+ const headerSlot = ctx.slots[`header.${col.key}`];
12305
+ const title = headerSlot?.({ column: col }) ?? col.title;
12306
+ cells.push(
12307
+ vue.h(
12308
+ "div",
12309
+ {
12310
+ key: `${col.key}-${cell.level}`,
12311
+ role: "columnheader",
12312
+ "data-iris-table-header": col.key,
12313
+ "data-iris-table-header-group": isLeaf ? void 0 : "",
12314
+ "aria-colspan": cell.colSpan,
12315
+ onClick: sortable ? () => ctx.onHeaderClick(col) : void 0,
12316
+ "aria-sort": sortable ? ctx.ariaSortFor(col) : void 0,
12317
+ style: {
12318
+ gridColumn: `${lead + cell.colStart} / span ${cell.colSpan}`,
12319
+ gridRow: `${cell.level + 1} / span ${cell.rowSpan}`,
12320
+ display: "flex",
12321
+ alignItems: "center",
12322
+ justifyContent: isLeaf ? align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start" : "center",
12323
+ padding: "8px var(--iris-padding-md)",
12324
+ cursor: sortable ? "pointer" : "default",
12325
+ userSelect: sortable ? "none" : "auto",
12326
+ background: "var(--iris-surface)",
12327
+ borderBottom: "1px solid var(--iris-border)",
12328
+ fontWeight: "600",
12329
+ fontSize: "var(--iris-font-size-md, 14px)",
12330
+ color: "var(--iris-foreground)",
12331
+ whiteSpace: "nowrap",
12332
+ overflow: "hidden",
12333
+ textOverflow: "ellipsis"
12334
+ }
12335
+ },
12336
+ [
12337
+ title,
12338
+ sortable ? ctx.sortIndicator(col) : null,
12339
+ ctx.multiSortSeq(col),
12340
+ ctx.renderFilterTrigger(col, isLeaf),
12341
+ isLeaf ? ctx.pinnedDragHandle?.(col) ?? null : null
12342
+ ]
12343
+ )
12344
+ );
12345
+ }
10545
12346
  }
12347
+ return vue.h(
12348
+ "div",
12349
+ {
12350
+ role: "row",
12351
+ "data-iris-table-row": "header",
12352
+ "data-iris-table-header-row": "",
12353
+ "data-iris-table-header-grouped": "",
12354
+ style: {
12355
+ display: "grid",
12356
+ gridTemplateColumns: ctx.gridTemplate.value,
12357
+ gridTemplateRows: `repeat(${matrix.length}, auto)`
12358
+ }
12359
+ },
12360
+ cells
12361
+ );
12362
+ }
12363
+ function createTableViewsController(options) {
12364
+ const views = vue.ref(core.readTableViews(options.config()));
12365
+ const internalActiveKey = vue.ref(null);
12366
+ const activeKey = vue.computed(() => {
12367
+ const config = options.config();
12368
+ return config?.activeKey !== void 0 ? config.activeKey ?? null : internalActiveKey.value;
12369
+ });
12370
+ const saveOpen = vue.ref(false);
12371
+ const draft = vue.ref("");
12372
+ const activeTab = vue.ref(null);
12373
+ const persist = (next) => {
12374
+ views.value = next;
12375
+ core.writeTableViews(options.config(), next);
12376
+ };
12377
+ const applySnapshot = (snapshot) => {
12378
+ if (Object.prototype.hasOwnProperty.call(snapshot, "sort"))
12379
+ options.setSort(snapshot.sort ?? null);
12380
+ };
12381
+ const select = (key) => {
12382
+ const view = views.value.find((candidate) => candidate.name === key);
12383
+ if (!view) return;
12384
+ applySnapshot(view.snapshot);
12385
+ internalActiveKey.value = key;
12386
+ options.onActiveViewChange(key);
12387
+ };
12388
+ const save = (name) => {
12389
+ const trimmed = name.trim();
12390
+ if (!trimmed) return;
12391
+ const currentSort = options.sort.value;
12392
+ const entry = {
12393
+ name: trimmed,
12394
+ snapshot: { sort: currentSort ? { ...currentSort } : null }
12395
+ };
12396
+ const index = views.value.findIndex((view) => view.name === trimmed);
12397
+ persist(
12398
+ index < 0 ? [...views.value, entry] : views.value.map((view, i) => i === index ? entry : view)
12399
+ );
12400
+ internalActiveKey.value = trimmed;
12401
+ options.onActiveViewChange(trimmed);
12402
+ };
12403
+ const remove = (key) => {
12404
+ persist(views.value.filter((view) => view.name !== key));
12405
+ if (activeKey.value === key) {
12406
+ internalActiveKey.value = null;
12407
+ options.onActiveViewChange(null);
12408
+ }
12409
+ };
12410
+ const applyTab = (tab) => {
12411
+ activeTab.value = tab.key;
12412
+ for (const name of tab.views ?? []) select(name);
12413
+ };
10546
12414
  return {
10547
- sortState,
10548
- cycleSort,
10549
- setSort,
10550
- sortComparator,
10551
- sortedData
12415
+ activeKey,
12416
+ views,
12417
+ saveOpen,
12418
+ draft,
12419
+ renderTabs: (tabs) => renderTableTabs(tabs, activeTab, applyTab),
12420
+ renderViews: () => renderTableViews({
12421
+ config: options.config(),
12422
+ views,
12423
+ activeKey,
12424
+ saveOpen,
12425
+ draft,
12426
+ onSelect: select,
12427
+ onSave: save,
12428
+ onDelete: remove
12429
+ })
10552
12430
  };
10553
12431
  }
10554
-
10555
- // src/primitives/table/Table.ts
10556
- function getCellValue2(row, column) {
10557
- const key = column.dataIndex ?? column.key;
10558
- return row[key];
12432
+ function renderTableViews(ctx) {
12433
+ if (!ctx.config) return null;
12434
+ return vue.h(
12435
+ "div",
12436
+ {
12437
+ "data-iris-table-views-bar": "",
12438
+ style: {
12439
+ display: "flex",
12440
+ alignItems: "center",
12441
+ gap: "var(--iris-space-xxs, 4px)",
12442
+ padding: "var(--iris-space-xxs, 4px) 0"
12443
+ }
12444
+ },
12445
+ [
12446
+ vue.h(
12447
+ "select",
12448
+ {
12449
+ "data-iris-table-views": "",
12450
+ value: ctx.saveOpen.value ? core.TABLE_VIEWS_SAVE_ITEM : ctx.activeKey.value ?? "",
12451
+ "aria-label": "Table views",
12452
+ onChange: (event) => {
12453
+ const value = event.target.value;
12454
+ if (value === core.TABLE_VIEWS_SAVE_ITEM) {
12455
+ ctx.draft.value = "";
12456
+ ctx.saveOpen.value = true;
12457
+ } else if (value !== "") {
12458
+ ctx.onSelect(value);
12459
+ }
12460
+ },
12461
+ style: {
12462
+ border: "1px solid var(--iris-border)",
12463
+ borderRadius: "var(--iris-radius-sm, 4px)",
12464
+ background: "var(--iris-surface)",
12465
+ color: "var(--iris-foreground)",
12466
+ font: "inherit",
12467
+ fontSize: "var(--iris-font-size-sm, 13px)",
12468
+ padding: "0 var(--iris-space-xxs, 4px)",
12469
+ maxWidth: "180px"
12470
+ }
12471
+ },
12472
+ [
12473
+ ctx.activeKey.value === null && !ctx.saveOpen.value ? vue.h("option", { value: "", disabled: true }, "Select view") : null,
12474
+ ...ctx.views.value.map(
12475
+ (view) => vue.h(
12476
+ "option",
12477
+ { key: view.name, value: view.name },
12478
+ ctx.config?.label?.(view.name) ?? view.name
12479
+ )
12480
+ ),
12481
+ vue.h("option", { value: core.TABLE_VIEWS_SAVE_ITEM }, "\uFF0B Save view")
12482
+ ]
12483
+ ),
12484
+ ctx.saveOpen.value ? vue.h("input", {
12485
+ "data-iris-views-save": "",
12486
+ type: "text",
12487
+ value: ctx.draft.value,
12488
+ placeholder: "View name",
12489
+ "aria-label": "Save view",
12490
+ onInput: (event) => {
12491
+ ctx.draft.value = event.target.value;
12492
+ },
12493
+ onKeydown: (event) => {
12494
+ if (event.key === "Enter") {
12495
+ event.preventDefault();
12496
+ if (ctx.draft.value.trim()) ctx.onSave(ctx.draft.value);
12497
+ ctx.saveOpen.value = false;
12498
+ } else if (event.key === "Escape") {
12499
+ ctx.saveOpen.value = false;
12500
+ }
12501
+ },
12502
+ onBlur: () => {
12503
+ ctx.saveOpen.value = false;
12504
+ },
12505
+ style: {
12506
+ border: "1px solid var(--iris-border)",
12507
+ borderRadius: "var(--iris-radius-sm, 4px)",
12508
+ padding: "var(--iris-space-xxs, 4px)",
12509
+ width: "120px",
12510
+ font: "inherit"
12511
+ }
12512
+ }) : null,
12513
+ ctx.activeKey.value !== null ? vue.h(
12514
+ "button",
12515
+ {
12516
+ type: "button",
12517
+ "data-iris-table-views-delete": "",
12518
+ "aria-label": "Delete view",
12519
+ onClick: () => ctx.onDelete(ctx.activeKey.value),
12520
+ style: {
12521
+ border: "none",
12522
+ background: "transparent",
12523
+ cursor: "pointer",
12524
+ color: "var(--iris-muted)",
12525
+ font: "inherit",
12526
+ padding: "0 var(--iris-space-xxs, 4px)"
12527
+ }
12528
+ },
12529
+ "\xD7"
12530
+ ) : null
12531
+ ]
12532
+ );
10559
12533
  }
10560
- var SELECTION_COL_WIDTH = 40;
10561
- var EXPAND_COL_WIDTH = 40;
10562
- var DEFAULT_COL_WIDTH = 140;
10563
- var DEFAULT_MIN_WIDTH = 60;
10564
- var RESIZE_STEP = 16;
10565
- function resolveInitialWidth(col) {
10566
- if (typeof col.width === "number") return col.width;
10567
- if (typeof col.width === "string") {
10568
- const m = col.width.match(/^(\d+(?:\.\d+)?)px$/);
10569
- if (m) return Number(m[1]);
10570
- }
10571
- return DEFAULT_COL_WIDTH;
12534
+ function renderTableTabs(tabs, activeTab, onApply) {
12535
+ const unique = core.uniqueTableTabs(tabs ?? []);
12536
+ if (unique.length === 0) return null;
12537
+ return vue.h(
12538
+ "div",
12539
+ {
12540
+ role: "tablist",
12541
+ "data-iris-table-tabs": "",
12542
+ style: {
12543
+ display: "flex",
12544
+ alignItems: "center",
12545
+ gap: "var(--iris-space-xs, 8px)",
12546
+ padding: "var(--iris-space-xs, 8px) var(--iris-space-sm, 12px)",
12547
+ border: "1px solid var(--iris-border)",
12548
+ borderBottom: "none",
12549
+ borderTopLeftRadius: "var(--iris-radius-md, 6px)",
12550
+ borderTopRightRadius: "var(--iris-radius-md, 6px)",
12551
+ background: "var(--iris-surface)"
12552
+ }
12553
+ },
12554
+ unique.map(
12555
+ (tab) => vue.h(
12556
+ "button",
12557
+ {
12558
+ key: tab.key,
12559
+ type: "button",
12560
+ role: "tab",
12561
+ "data-iris-table-tab": tab.key,
12562
+ "aria-selected": activeTab.value === tab.key ? "true" : "false",
12563
+ onClick: () => onApply(tab),
12564
+ style: {
12565
+ border: "none",
12566
+ background: "transparent",
12567
+ color: activeTab.value === tab.key ? "var(--iris-primary)" : "var(--iris-foreground)",
12568
+ cursor: "pointer",
12569
+ font: "inherit",
12570
+ fontWeight: activeTab.value === tab.key ? "600" : "400",
12571
+ padding: "0 var(--iris-space-xxs, 4px)"
12572
+ }
12573
+ },
12574
+ tab.label
12575
+ )
12576
+ )
12577
+ );
10572
12578
  }
12579
+
12580
+ // src/primitives/table/Table.ts
10573
12581
  var IrisTable = vue.defineComponent({
10574
12582
  name: "IrisTable",
10575
12583
  inheritAttrs: false,
10576
- props: {
10577
- columns: {
10578
- type: Array,
10579
- required: true
10580
- },
10581
- data: {
10582
- type: Array,
10583
- required: false,
10584
- default: void 0
10585
- },
10586
- rowKey: { type: String, default: "id" },
10587
- ...tableControlProps,
10588
- striped: { type: Boolean, default: false },
10589
- editConfig: {
10590
- type: Object,
10591
- default: void 0
10592
- },
10593
- bordered: { type: Boolean, default: true },
10594
- /** Enable per-column resize handles. Combine with `v-model:columnWidths` for persistence. */
10595
- resizableColumns: { type: Boolean, default: false },
10596
- /** Controlled column widths in px. */
10597
- columnWidths: {
10598
- type: Object,
10599
- default: void 0
10600
- },
10601
- /** Enable virtual scrolling for the body. */
10602
- virtualScroll: {
10603
- type: Object,
10604
- default: void 0
10605
- },
10606
- /** Show the loading state instead of rows. */
10607
- loading: { type: Boolean, default: false },
10608
- /** Show the error state instead of rows (takes precedence over loading). */
10609
- error: { type: Boolean, default: false },
10610
- onRetry: { type: Function, default: void 0 },
10611
- /** Render only the horizontally-visible columns (+ pinned + overscan) for wide tables. */
10612
- columnVirtualization: { type: Boolean, default: false },
10613
- /**
10614
- * Enable WAI-ARIA grid keyboard navigation: the table becomes `role="grid"`
10615
- * and Arrow / Home / End / Page Up·Down move a roving cell focus across the
10616
- * data cells. Off by default; opt-in and additive (no effect on mouse / Tab
10617
- * behavior). Pairs best without virtualization (the focused cell must be
10618
- * rendered) and does not hijack keystrokes while a cell is being edited.
10619
- */
10620
- keyboardNavigation: { type: Boolean, default: false },
10621
- /**
10622
- * Enable rectangular cell-range selection (Excel-style). Click starts a
10623
- * range; Shift+Click or Shift+Arrow extends it; Escape clears it.
10624
- * Cells within the range get `data-iris-cell-selected="true"`.
10625
- */
10626
- cellRange: { type: Boolean, default: false },
10627
- /**
10628
- * Render an expandable detail panel beneath a row. Providing this adds a
10629
- * leading expand-toggle column; clicking it reveals a full-width detail row.
10630
- * (Not applied in the virtual-scroll path.)
10631
- */
10632
- renderDetail: {
10633
- type: Function,
10634
- default: void 0
10635
- },
10636
- /** Which rows can expand a detail panel. Defaults to all rows when `renderDetail` is set. */
10637
- rowExpandable: {
10638
- type: Function,
10639
- default: void 0
10640
- },
10641
- /** Initially-expanded row keys (uncontrolled). */
10642
- defaultExpandedRowKeys: {
10643
- type: Array,
10644
- default: void 0
10645
- },
10646
- /**
10647
- * Read a row's child rows to render the table as a TREE. Providing this
10648
- * enables tree mode: `data` is treated as the root rows, each row's first
10649
- * cell gains a depth indent + an expand/collapse toggle (when it has
10650
- * children), and the expand state reuses `defaultExpandedRowKeys` /
10651
- * `expandedRowsChange`. Column sort reorders siblings hierarchically (each
10652
- * level sorted, structure kept), and tree rows virtualize like flat rows
10653
- * when `virtualScroll` is set (unless `renderDetail` is also used, since
10654
- * detail panels are variable-height).
10655
- */
10656
- getSubRows: {
10657
- type: Function,
10658
- default: void 0
10659
- },
10660
- /**
10661
- * Cascade multi-row selection through the complete tree supplied by
10662
- * `getSubRows`. Selecting a branch selects every descendant (including
10663
- * collapsed rows); partially selected branches render indeterminate.
10664
- * Ignored outside tree mode and for non-multi selection.
10665
- */
10666
- treeSelectionCascade: { type: Boolean, default: false }
10667
- },
12584
+ props: tableProps,
10668
12585
  emits: {
10669
12586
  "update:selection": (_value) => true,
10670
12587
  "update:sort": (_value) => true,
12588
+ "update:multiSortState": (_value) => true,
12589
+ multiSortChange: (_value) => true,
10671
12590
  "update:columnWidths": (_value) => true,
12591
+ /** Controlled columnVisibility channel (parent owns the map). */
12592
+ "update:columnVisibility": (_value) => true,
10672
12593
  rowClick: (_row, _index) => true,
10673
12594
  rowDblclick: (_row, _index) => true,
10674
12595
  cellEdit: (_payload) => true,
10675
12596
  expandedRowsChange: (_keys) => true
10676
12597
  },
10677
- setup(props, { slots, attrs, emit }) {
10678
- if (typeof document !== "undefined" && !document.getElementById("iris-table-row-styles")) {
10679
- const style = document.createElement("style");
10680
- style.id = "iris-table-row-styles";
10681
- style.textContent = `
10682
- [data-iris-table] [role="row"]:hover {
10683
- --iris-cell-bg: var(--iris-surface-hover);
10684
- }
10685
- [data-iris-table-row-selected="true"] {
10686
- --iris-cell-bg: var(--iris-surface-selected);
10687
- }
10688
- `;
10689
- document.head.appendChild(style);
10690
- }
12598
+ setup(props, { slots, attrs, emit, expose }) {
12599
+ ensureTableStyles();
10691
12600
  const { t } = useI18n();
10692
- const grouped = vue.computed(() => props.columns.some((c) => c.children && c.children.length > 0));
12601
+ const densityState = vue.ref("comfortable");
12602
+ const effectiveDensity = vue.computed(() => {
12603
+ if (props.densityToggle) return densityState.value;
12604
+ return props.density === "compact" || props.density === "cozy" ? props.density : "comfortable";
12605
+ });
12606
+ const cycleDensity = () => {
12607
+ densityState.value = densityState.value === "comfortable" ? "compact" : densityState.value === "compact" ? "cozy" : "comfortable";
12608
+ };
12609
+ const importController = useTableImport(
12610
+ () => props.importPreview,
12611
+ () => props.toolbar?.onImport
12612
+ );
12613
+ const responsiveWidth = vue.ref(0);
12614
+ const internalWidths = vue.ref({});
12615
+ const effectiveWidths = vue.computed(() => {
12616
+ if (props.columnWidths) return props.columnWidths;
12617
+ return internalWidths.value;
12618
+ });
12619
+ const sourceDisplayColumns = vue.computed(() => {
12620
+ const vis = props.columnVisibility;
12621
+ if (!vis) return props.columns;
12622
+ return props.columns.filter((c) => vis[c.key] !== false);
12623
+ });
12624
+ const detectedTypes = vue.ref({});
12625
+ const detectTypesDone = vue.ref(false);
12626
+ const detectedDisplayColumns = vue.computed(() => {
12627
+ if (!props.autoDetectTypes || Object.keys(detectedTypes.value).length === 0) {
12628
+ return sourceDisplayColumns.value;
12629
+ }
12630
+ return applyDetectedTableTypes(sourceDisplayColumns.value, detectedTypes.value);
12631
+ });
12632
+ const responsiveLeadingWidth = vue.computed(
12633
+ () => (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)
12634
+ );
12635
+ const responsiveWidthOf = (column) => {
12636
+ const width = effectiveWidths.value[column.key] ?? resolveInitialWidth(column);
12637
+ return Number.isFinite(width) && width >= 0 ? width : resolveInitialWidth(column);
12638
+ };
12639
+ const responsiveResult = vue.computed(
12640
+ () => props.responsive ? computeResponsiveTableColumns(
12641
+ detectedDisplayColumns.value,
12642
+ responsiveWidth.value,
12643
+ responsiveLeadingWidth.value,
12644
+ responsiveWidthOf
12645
+ ) : { columns: detectedDisplayColumns.value, overflow: false }
12646
+ );
12647
+ const responsiveDisplayColumns = vue.computed(
12648
+ () => responsiveResult.value.columns
12649
+ );
12650
+ const responsiveOverflow = vue.computed(() => responsiveResult.value.overflow);
12651
+ const displayColumns = vue.computed(
12652
+ () => responsiveDisplayColumns.value
12653
+ );
12654
+ const grouped = vue.computed(
12655
+ () => displayColumns.value.some((c) => c.children && c.children.length > 0)
12656
+ );
10693
12657
  const leafColumns = vue.computed(
10694
- () => grouped.value ? core.flattenLeafColumns(props.columns) : props.columns
12658
+ () => grouped.value ? core.flattenLeafColumns(displayColumns.value) : displayColumns.value
12659
+ );
12660
+ const headerMatrix = vue.computed(
12661
+ () => grouped.value ? core.buildHeaderMatrix(displayColumns.value) : null
12662
+ );
12663
+ const remoteSort = vue.computed(() => props.proxyConfig?.remoteSort === true);
12664
+ const remoteFilter = vue.computed(() => props.proxyConfig?.remoteFilter === true);
12665
+ const proxyCtrl = useTableProxy({
12666
+ proxyConfig: () => props.proxyConfig,
12667
+ remoteSort,
12668
+ remoteFilter,
12669
+ multiSort: () => props.multiSort,
12670
+ sort: () => props.sort,
12671
+ defaultSort: props.defaultSort,
12672
+ multiSortState: () => props.multiSortState,
12673
+ defaultMultiSort: props.defaultMultiSort,
12674
+ filters: () => props.filters,
12675
+ filterValues: () => props.filterValues
12676
+ });
12677
+ const tableLoading = vue.computed(
12678
+ () => proxyCtrl.proxy.value ? proxyCtrl.state.value.loading : props.loading
12679
+ );
12680
+ const tableError = vue.computed(
12681
+ () => proxyCtrl.proxy.value ? proxyCtrl.state.value.error !== null : props.error
12682
+ );
12683
+ const handleRetry = () => {
12684
+ if (proxyCtrl.proxy.value) void proxyCtrl.proxy.value.refetch();
12685
+ props.onRetry?.();
12686
+ };
12687
+ const retry = vue.computed(() => proxyCtrl.proxy.value ? handleRetry : props.onRetry);
12688
+ const localRowsOverride = vue.ref(null);
12689
+ vue.watch(
12690
+ () => props.data,
12691
+ () => {
12692
+ if (localRowsOverride.value !== null) localRowsOverride.value = null;
12693
+ }
12694
+ );
12695
+ const tableData = vue.computed(
12696
+ () => proxyCtrl.proxy.value ? proxyCtrl.liveData.value : localRowsOverride.value ?? props.data ?? []
12697
+ );
12698
+ vue.watch(
12699
+ [() => props.autoDetectTypes, tableData, () => props.columns],
12700
+ () => {
12701
+ if (!props.autoDetectTypes || detectTypesDone.value || tableData.value.length === 0) return;
12702
+ detectTypesDone.value = true;
12703
+ const next = {};
12704
+ for (const column of core.flattenLeafColumns(props.columns)) {
12705
+ const candidate = column;
12706
+ if (candidate.formula) continue;
12707
+ next[column.key] = core.detectColumnType(
12708
+ tableData.value.map((row) => getCellValue(row, candidate))
12709
+ );
12710
+ }
12711
+ detectedTypes.value = next;
12712
+ },
12713
+ { immediate: true }
10695
12714
  );
10696
- const headerMatrix = vue.computed(() => grouped.value ? core.buildHeaderMatrix(props.columns) : null);
10697
12715
  const {
10698
12716
  sortState: internalSort,
10699
12717
  cycleSort,
12718
+ setSort,
10700
12719
  sortComparator,
10701
- sortedData: sortedRows
10702
- } = useTableSort(
10703
- vue.computed(() => props.data ?? []),
10704
- {
10705
- leafColumns,
10706
- sort: vue.computed(() => props.sort),
10707
- defaultSort: props.defaultSort,
10708
- onSortChange: (next) => emit("update:sort", next)
12720
+ sortedData: sortedRows,
12721
+ multiSortState,
12722
+ cycleMultiSort
12723
+ } = useTableSort(tableData, {
12724
+ leafColumns,
12725
+ sort: vue.computed(() => props.sort),
12726
+ defaultSort: props.defaultSort,
12727
+ onSortChange: (next) => {
12728
+ emit("update:sort", next);
12729
+ if (remoteSort.value) proxyCtrl.setParams({ sort: next });
12730
+ },
12731
+ multiSort: () => props.multiSort,
12732
+ multiSortState: () => props.multiSortState,
12733
+ defaultMultiSort: props.defaultMultiSort,
12734
+ onMultiSortChange: (next) => {
12735
+ emit("update:multiSortState", next);
12736
+ emit("multiSortChange", next);
12737
+ if (remoteSort.value) proxyCtrl.setParams({ sorts: next });
12738
+ }
12739
+ });
12740
+ const tableViews = createTableViewsController({
12741
+ config: () => props.views,
12742
+ sort: internalSort,
12743
+ setSort,
12744
+ onActiveViewChange: (key) => props.onActiveViewChange?.(key)
12745
+ });
12746
+ const sortedData = vue.computed(() => remoteSort.value ? tableData.value : sortedRows.value);
12747
+ vue.watch([internalSort, () => props.multiSort, remoteSort, () => proxyCtrl.proxy.value], () => {
12748
+ if (!proxyCtrl.proxy.value || !remoteSort.value || props.multiSort) return;
12749
+ proxyCtrl.setParams({ sort: internalSort.value });
12750
+ });
12751
+ vue.watch([multiSortState, () => props.multiSort, remoteSort, () => proxyCtrl.proxy.value], () => {
12752
+ if (!proxyCtrl.proxy.value || !remoteSort.value || !props.multiSort) return;
12753
+ proxyCtrl.setParams({ sorts: multiSortState.value });
12754
+ });
12755
+ const formDraft = vue.ref({});
12756
+ const formApplied = vue.ref({});
12757
+ const formFieldSignature = vue.computed(
12758
+ () => (props.formConfig?.fields ?? []).map((f) => `${f.key}=${f.defaultValue ?? ""}`).join("\0")
12759
+ );
12760
+ vue.watch(
12761
+ formFieldSignature,
12762
+ () => {
12763
+ formDraft.value = core.seedFormValues(props.formConfig?.fields);
12764
+ formApplied.value = {};
12765
+ },
12766
+ { immediate: true }
12767
+ );
12768
+ const setFormValue = (key, value) => {
12769
+ if (formDraft.value[key] === value) return;
12770
+ formDraft.value = { ...formDraft.value, [key]: value };
12771
+ };
12772
+ const mergedProxyFilters = (form) => mergeFilterValues(core.mergeFormFilters(props.filters ?? {}, form), props.filterValues ?? {});
12773
+ const handleFormSubmit = () => {
12774
+ const values = core.buildFormValues(props.formConfig?.fields, formDraft.value);
12775
+ props.formConfig?.onSearch?.(values);
12776
+ formApplied.value = values;
12777
+ if (proxyCtrl.proxy.value) {
12778
+ proxyCtrl.setParams({ filters: mergedProxyFilters(values), page: 1 });
12779
+ }
12780
+ };
12781
+ const handleFormReset = () => {
12782
+ const defaults = core.seedFormValues(props.formConfig?.fields);
12783
+ formDraft.value = defaults;
12784
+ const values = core.buildFormValues(props.formConfig?.fields, defaults);
12785
+ formApplied.value = values;
12786
+ props.formConfig?.onReset?.(values);
12787
+ if (proxyCtrl.proxy.value) {
12788
+ if (!proxyCtrl.setParams({ filters: mergedProxyFilters(values), page: 1 })) {
12789
+ void proxyCtrl.refetch();
12790
+ }
10709
12791
  }
12792
+ };
12793
+ vue.watch(
12794
+ [formApplied, () => props.filters, () => props.filterValues],
12795
+ () => {
12796
+ if (proxyCtrl.proxy.value && remoteFilter.value) {
12797
+ proxyCtrl.setParams({
12798
+ filters: mergedProxyFilters(formApplied.value)
12799
+ });
12800
+ }
12801
+ },
12802
+ { immediate: true }
10710
12803
  );
10711
12804
  const selControlled = vue.computed(() => props.selection !== void 0);
10712
12805
  const selectionModel = core.createSelectionModel({
@@ -10749,18 +12842,56 @@ var IrisTable = vue.defineComponent({
10749
12842
  if (typeof v === "string" || typeof v === "number") return v;
10750
12843
  return index;
10751
12844
  };
10752
- const treeMode = vue.computed(() => props.getSubRows !== void 0);
12845
+ const treeMode = vue.computed(() => props.getSubRows !== void 0 || props.lazyLoad !== void 0);
12846
+ const lazyChildren = vue.ref(/* @__PURE__ */ new Map());
12847
+ const lazyLoading = vue.ref(/* @__PURE__ */ new Set());
12848
+ let lazyEpoch = 0;
12849
+ vue.watch(
12850
+ () => proxyCtrl.proxy.value ? proxyCtrl.state.value.data : props.data,
12851
+ () => {
12852
+ lazyChildren.value = /* @__PURE__ */ new Map();
12853
+ lazyLoading.value = /* @__PURE__ */ new Set();
12854
+ lazyEpoch += 1;
12855
+ }
12856
+ );
12857
+ const lazyChildrenOf = (row) => {
12858
+ const key = String(row[props.rowKey]);
12859
+ return lazyChildren.value.get(key) ?? props.getSubRows?.(row);
12860
+ };
10753
12861
  const flatTree = vue.computed(
10754
- () => treeMode.value ? core.flattenTree(sortedRows.value, {
12862
+ () => treeMode.value ? core.flattenTree(sortedData.value, {
10755
12863
  getKey: (r) => String(r[props.rowKey]),
10756
12864
  // With an active sort, sort each level's children by the same
10757
- // comparator so the whole tree reorders hierarchically.
10758
- getChildren: sortComparator.value ? core.withSortedChildren((r) => props.getSubRows(r), sortComparator.value) : (r) => props.getSubRows(r),
12865
+ // comparator so the whole tree reorders hierarchically. Lazy-loaded
12866
+ // children win over `getSubRows` and still participate.
12867
+ getChildren: sortComparator.value ? core.withSortedChildren(lazyChildrenOf, sortComparator.value) : lazyChildrenOf,
10759
12868
  isExpanded: (k) => expandedKeys.value.includes(k)
10760
12869
  }) : null
10761
12870
  );
12871
+ const filteredData = vue.computed(() => {
12872
+ if (remoteFilter.value) return sortedData.value;
12873
+ const merged = proxyCtrl.proxy.value ? props.filters ?? {} : core.mergeFormFilters(props.filters ?? {}, formApplied.value);
12874
+ const active = Object.entries(merged).filter(([, v]) => v != null && v !== "");
12875
+ const checkedEntries = Object.entries(props.filterValues ?? {}).filter(
12876
+ ([, values]) => values.length > 0
12877
+ );
12878
+ if (active.length === 0 && checkedEntries.length === 0) return sortedData.value;
12879
+ return sortedData.value.filter((row) => {
12880
+ const textOk = active.every(([key, value]) => {
12881
+ const col = displayColumns.value.find((c) => c.key === key);
12882
+ if (!col) return true;
12883
+ return String(getCellValue(row, col) ?? "").toLowerCase().includes(value.toLowerCase());
12884
+ });
12885
+ const setsOk = checkedEntries.every(([key, values]) => {
12886
+ const col = displayColumns.value.find((c) => c.key === key);
12887
+ if (!col) return true;
12888
+ return values.includes(String(getCellValue(row, col) ?? ""));
12889
+ });
12890
+ return textOk && setsOk;
12891
+ });
12892
+ });
10762
12893
  const bodyData = vue.computed(
10763
- () => flatTree.value ? flatTree.value.map((t2) => t2.row) : sortedRows.value
12894
+ () => flatTree.value ? flatTree.value.map((t2) => t2.row) : filteredData.value
10764
12895
  );
10765
12896
  const cascadingTreeSelection = vue.computed(
10766
12897
  () => props.treeSelectionCascade && props.selectable === "multi" && treeMode.value
@@ -10782,7 +12913,7 @@ var IrisTable = vue.defineComponent({
10782
12913
  if (children && children.length > 0) walk(children, key);
10783
12914
  }
10784
12915
  };
10785
- walk(sortedRows.value);
12916
+ walk(sortedData.value);
10786
12917
  return nodes;
10787
12918
  });
10788
12919
  const compactTreeSelectionSeed = (keys) => {
@@ -10855,25 +12986,27 @@ var IrisTable = vue.defineComponent({
10855
12986
  selectionModel.set([...allRowIds.value]);
10856
12987
  };
10857
12988
  const editingCellId = vue.ref(null);
12989
+ const editingColumnKey = vue.ref(null);
10858
12990
  const editingDraft = vue.ref("");
10859
12991
  const editError = vue.ref(null);
10860
12992
  const editorInputRef = vue.ref(null);
10861
- const cellId = (rowIdent, colKey) => `${rowIdent}::${colKey}`;
10862
12993
  const beginEdit = (row, column, rowIdent) => {
10863
12994
  if (!column.editable) return;
10864
12995
  editingCellId.value = cellId(rowIdent, column.key);
10865
- const current = getCellValue2(row, column);
12996
+ editingColumnKey.value = column.key;
12997
+ const current = getCellValue(row, column);
10866
12998
  editingDraft.value = current == null ? "" : String(current);
10867
12999
  editError.value = null;
10868
13000
  void vue.nextTick(() => editorInputRef.value?.focus());
10869
13001
  };
10870
13002
  const commitEdit = (row, column, rowIndex) => {
10871
13003
  if (editingCellId.value === null) return;
10872
- const oldValue = getCellValue2(row, column);
13004
+ const oldValue = getCellValue(row, column);
10873
13005
  const draft = editingDraft.value;
10874
13006
  const newValue = column.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? oldValue : Number(draft) : draft;
10875
13007
  if (column.editRules && column.editRules.length > 0) {
10876
- core.validateEditRulesAsync(column.editRules, draft, row).then((r) => {
13008
+ const context = { rows: tableData.value, columnKey: column.key };
13009
+ core.validateEditRulesAsync(column.editRules, draft, row, false, context).then((r) => {
10877
13010
  if (!r.valid) {
10878
13011
  editError.value = r.messages[0] ?? null;
10879
13012
  return;
@@ -10896,67 +13029,272 @@ var IrisTable = vue.defineComponent({
10896
13029
  emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
10897
13030
  }
10898
13031
  };
13032
+ const writeCellValue = (row, column, rowIndex, oldValue, newValue) => {
13033
+ if (newValue === oldValue) return;
13034
+ emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
13035
+ if (proxyCtrl.proxy.value) {
13036
+ const id = rowId(row, rowIndex);
13037
+ const idx = proxyCtrl.liveData.value.findIndex((r, i) => rowId(r, i) === id);
13038
+ if (idx >= 0) {
13039
+ const key = column.dataIndex ?? column.key;
13040
+ const next = { ...proxyCtrl.liveData.value[idx], [key]: newValue };
13041
+ proxyCtrl.liveData.value = [
13042
+ ...proxyCtrl.liveData.value.slice(0, idx),
13043
+ next,
13044
+ ...proxyCtrl.liveData.value.slice(idx + 1)
13045
+ ];
13046
+ }
13047
+ }
13048
+ };
10899
13049
  const finishCommit = (row, column, rowIndex, oldValue, newValue) => {
10900
13050
  editError.value = null;
10901
13051
  editingCellId.value = null;
10902
- if (newValue !== oldValue) {
10903
- emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
13052
+ editingColumnKey.value = null;
13053
+ writeCellValue(row, column, rowIndex, oldValue, newValue);
13054
+ };
13055
+ const cancelEdit = () => {
13056
+ editError.value = null;
13057
+ editingCellId.value = null;
13058
+ editingColumnKey.value = null;
13059
+ };
13060
+ const rowMode = vue.computed(() => props.editConfig?.mode === "row");
13061
+ const rowSessions = vue.ref(/* @__PURE__ */ new Map());
13062
+ const rowEditing = vue.ref(null);
13063
+ const rowEditorRefs = /* @__PURE__ */ new Map();
13064
+ const currentRowFor = (k) => bodyData.value.find((r, i) => rowId(r, i) === k);
13065
+ const beginRowEdit = (row, rowIndex, focusColKey) => {
13066
+ const k = rowId(row, rowIndex);
13067
+ const editableCols = leafColumns.value.filter((c) => c.editable);
13068
+ if (editableCols.length === 0) return;
13069
+ const sessions = /* @__PURE__ */ new Map();
13070
+ for (const col of editableCols) {
13071
+ const current = getCellValue(row, col);
13072
+ sessions.set(cellId(k, col.key), {
13073
+ draft: current == null ? "" : String(current),
13074
+ error: null
13075
+ });
13076
+ }
13077
+ rowSessions.value = sessions;
13078
+ rowEditing.value = { k, idx: rowIndex };
13079
+ const focusKey = focusColKey && editableCols.some((c) => c.key === focusColKey) ? focusColKey : editableCols[0].key;
13080
+ void vue.nextTick(() => rowEditorRefs.get(focusKey)?.focus());
13081
+ };
13082
+ const commitRowSession = (k, column, rowIndex, id) => {
13083
+ const session = rowSessions.value.get(id);
13084
+ if (!session) return;
13085
+ const row = currentRowFor(k);
13086
+ if (!row) return;
13087
+ const oldValue = getCellValue(row, column);
13088
+ const draft = session.draft;
13089
+ const newValue = column.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? oldValue : Number(draft) : draft;
13090
+ if (column.editRules && column.editRules.length > 0) {
13091
+ const context = { rows: tableData.value, columnKey: column.key };
13092
+ core.validateEditRulesAsync(column.editRules, draft, row, false, context).then((r) => {
13093
+ if (!rowSessions.value.has(id)) return;
13094
+ if (!r.valid) {
13095
+ const s = rowSessions.value.get(id);
13096
+ if (s) s.error = r.messages[0] ?? null;
13097
+ return;
13098
+ }
13099
+ finishRowCommit(row, column, rowIndex, oldValue, newValue, id);
13100
+ });
13101
+ return;
13102
+ }
13103
+ if (column.validate) {
13104
+ const error = column.validate(newValue, row);
13105
+ if (error) {
13106
+ const s = rowSessions.value.get(id);
13107
+ if (s) s.error = error;
13108
+ return;
13109
+ }
13110
+ }
13111
+ finishRowCommit(row, column, rowIndex, oldValue, newValue, id);
13112
+ };
13113
+ const finishRowCommit = (row, column, rowIndex, oldValue, newValue, id) => {
13114
+ rowSessions.value.delete(id);
13115
+ if (rowSessions.value.size === 0) rowEditing.value = null;
13116
+ writeCellValue(row, column, rowIndex, oldValue, newValue);
13117
+ };
13118
+ const cancelRowEdit = () => {
13119
+ rowSessions.value = /* @__PURE__ */ new Map();
13120
+ rowEditing.value = null;
13121
+ };
13122
+ const switchRowEdit = (row, rowIndex, focusColKey) => {
13123
+ const editing = rowEditing.value;
13124
+ if (editing !== null) {
13125
+ for (const [id] of rowSessions.value) {
13126
+ const colKey = id.slice(id.indexOf("::") + 2);
13127
+ const col = leafColumns.value.find((c) => c.key === colKey);
13128
+ if (!col) continue;
13129
+ commitRowSession(editing.k, col, editing.idx, id);
13130
+ if (rowSessions.value.get(id)?.error != null) return;
13131
+ }
13132
+ }
13133
+ beginRowEdit(row, rowIndex, focusColKey);
13134
+ };
13135
+ const handleRowModeCellClick = (row, col, index, k) => {
13136
+ if (rowEditing.value?.k === k) {
13137
+ const id = cellId(k, col.key);
13138
+ if (col.editable && !rowSessions.value.has(id)) {
13139
+ const current = getCellValue(row, col);
13140
+ rowSessions.value.set(id, {
13141
+ draft: current == null ? "" : String(current),
13142
+ error: null
13143
+ });
13144
+ void vue.nextTick(() => rowEditorRefs.get(col.key)?.focus());
13145
+ }
13146
+ } else {
13147
+ switchRowEdit(row, index, col.key);
10904
13148
  }
10905
13149
  };
10906
- const cancelEdit = () => {
10907
- editError.value = null;
10908
- editingCellId.value = null;
13150
+ const editorRenderContext = {
13151
+ rowEditorRefs,
13152
+ editorInputRef,
13153
+ editingDraft,
13154
+ editError,
13155
+ commitEdit,
13156
+ cancelEdit,
13157
+ commitRowSession,
13158
+ cancelRowEdit,
13159
+ editPreview: props.editPreview,
13160
+ previewValue: (row, col, draft) => {
13161
+ const raw = col.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? getCellValue(row, col) : Number(draft) : draft;
13162
+ return col.formatter?.(core.applyTableMask(raw, col), row) ?? "";
13163
+ }
10909
13164
  };
10910
- const internalWidths = vue.ref({});
13165
+ const buildRowSessionContent = (row, col, index, k, editCellId, session) => renderRowSessionContent(editorRenderContext, row, col, index, k, editCellId, session);
13166
+ const buildCellEditContent = (row, col, index, editCellId) => renderCellEditContent(editorRenderContext, row, col, index, editCellId);
13167
+ const buildTreeIndent = (treeMeta, row) => vue.h(
13168
+ "span",
13169
+ {
13170
+ "data-iris-table-tree-indent": "",
13171
+ style: {
13172
+ display: "inline-flex",
13173
+ alignItems: "center",
13174
+ flex: "none",
13175
+ paddingLeft: `${treeMeta.depth * 16}px`
13176
+ }
13177
+ },
13178
+ treeMeta.hasChildren || props.lazyLoad !== void 0 && !lazyChildren.value.has(treeMeta.key) ? [
13179
+ vue.h(
13180
+ "button",
13181
+ {
13182
+ type: "button",
13183
+ "data-iris-table-tree-toggle": "",
13184
+ "data-iris-tree-loading": lazyLoading.value.has(treeMeta.key) ? "" : void 0,
13185
+ "aria-expanded": treeMeta.expanded ? "true" : "false",
13186
+ "aria-label": t(treeMeta.expanded ? "treeSelect.collapse" : "treeSelect.expand"),
13187
+ onClick: (e) => {
13188
+ e.stopPropagation();
13189
+ if (treeMeta.hasChildren) {
13190
+ expansion.toggle(treeMeta.key);
13191
+ return;
13192
+ }
13193
+ if (lazyLoading.value.has(treeMeta.key)) return;
13194
+ lazyLoading.value = new Set(lazyLoading.value).add(treeMeta.key);
13195
+ const clearLoading = () => {
13196
+ const next = new Set(lazyLoading.value);
13197
+ next.delete(treeMeta.key);
13198
+ lazyLoading.value = next;
13199
+ };
13200
+ try {
13201
+ const epoch = lazyEpoch;
13202
+ props.lazyLoad(row, (children) => {
13203
+ if (epoch !== lazyEpoch) return;
13204
+ lazyChildren.value.set(treeMeta.key, children);
13205
+ if (children && children.length > 0) {
13206
+ expansion.toggle(treeMeta.key);
13207
+ }
13208
+ clearLoading();
13209
+ });
13210
+ } catch {
13211
+ clearLoading();
13212
+ }
13213
+ },
13214
+ style: {
13215
+ border: "none",
13216
+ background: "transparent",
13217
+ cursor: "pointer",
13218
+ padding: "0",
13219
+ marginRight: "4px",
13220
+ font: "inherit",
13221
+ color: "var(--iris-foreground)",
13222
+ transform: treeMeta.expanded ? "rotate(90deg)" : "none",
13223
+ transition: "transform 150ms"
13224
+ }
13225
+ },
13226
+ "\u25B6"
13227
+ )
13228
+ ] : [
13229
+ vue.h("span", {
13230
+ "aria-hidden": "true",
13231
+ style: { display: "inline-block", width: "16px" }
13232
+ })
13233
+ ]
13234
+ );
10911
13235
  vue.watch(
10912
13236
  () => leafColumns.value,
10913
13237
  (cols) => {
10914
13238
  const seeded = { ...internalWidths.value };
13239
+ let changed = false;
10915
13240
  for (const col of cols) {
10916
- if (seeded[col.key] === void 0) seeded[col.key] = resolveInitialWidth(col);
13241
+ if (seeded[col.key] === void 0) {
13242
+ seeded[col.key] = resolveInitialWidth(col);
13243
+ changed = true;
13244
+ }
10917
13245
  }
10918
- internalWidths.value = seeded;
13246
+ if (changed) internalWidths.value = seeded;
10919
13247
  },
10920
13248
  { immediate: true, deep: false }
10921
13249
  );
10922
- const effectiveWidths = vue.computed(() => {
10923
- if (props.columnWidths) return props.columnWidths;
10924
- return internalWidths.value;
10925
- });
10926
13250
  const setColumnWidths = (next) => {
10927
13251
  if (props.columnWidths === void 0) internalWidths.value = next;
10928
13252
  emit("update:columnWidths", next);
10929
13253
  };
10930
13254
  const onHeaderClick = (column) => {
10931
- cycleSort(column);
10932
- };
10933
- const sortIndicator = (col) => {
10934
- if (!col.sortable) return null;
10935
- const state = internalSort.value;
10936
- const isActive = state?.key === col.key;
10937
- const direction = isActive ? state.direction : null;
10938
- const color = isActive ? "var(--iris-primary)" : "var(--iris-muted)";
13255
+ if (props.multiSort) cycleMultiSort(column);
13256
+ else cycleSort(column);
13257
+ };
13258
+ const multiSortSeq = (col) => {
13259
+ if (!props.multiSort) return null;
13260
+ const idx = multiSortState.value.findIndex((s) => s.key === col.key);
13261
+ if (idx <= 0) return null;
10939
13262
  return vue.h(
10940
13263
  "span",
10941
13264
  {
10942
- "aria-hidden": "true",
13265
+ "data-iris-sort-seq": "",
10943
13266
  style: {
10944
- display: "inline-flex",
10945
- flexDirection: "column",
10946
13267
  marginInlineStart: "var(--iris-space-xxs, 4px)",
10947
- lineHeight: "0.6",
10948
13268
  fontSize: "var(--iris-font-size-xs, 12px)",
10949
- color
13269
+ color: "var(--iris-muted)"
10950
13270
  }
10951
13271
  },
10952
- [
10953
- vue.h("span", { style: { opacity: direction === "asc" ? "1" : "0.45" } }, "\u25B2"),
10954
- vue.h("span", { style: { opacity: direction === "desc" ? "1" : "0.45" } }, "\u25BC")
10955
- ]
13272
+ String(idx + 1)
10956
13273
  );
10957
13274
  };
13275
+ const ariaSortFor = (col) => {
13276
+ const state = props.multiSort ? multiSortState.value.find((s) => s.key === col.key) ?? null : internalSort.value;
13277
+ if (state?.key === col.key) return state.direction === "asc" ? "ascending" : "descending";
13278
+ return col.sortable ? "none" : void 0;
13279
+ };
13280
+ const sortIndicator = (col) => renderTableSortIndicator(col, {
13281
+ multiSort: props.multiSort,
13282
+ multiSortState: multiSortState.value,
13283
+ sort: internalSort.value
13284
+ });
13285
+ const renderFilterTrigger = (col, leaf) => renderTableFilterTrigger({
13286
+ column: col,
13287
+ leaf,
13288
+ active: (props.filterValues?.[col.key]?.length ?? 0) > 0,
13289
+ open: filterPanelState.value?.open === true && filterPanelState.value.colKey === col.key,
13290
+ label: t("table.filter"),
13291
+ onOpen: openFilterPanel
13292
+ });
13293
+ const leadTrackCount = () => (props.rowDrag ? 1 : 0) + (props.seq ? 1 : 0) + (hasDetail.value ? 1 : 0) + (props.selectable !== "none" ? 1 : 0);
10958
13294
  const gridTemplate = vue.computed(() => {
10959
13295
  const parts = [];
13296
+ if (props.rowDrag) parts.push(`${DRAG_COL_WIDTH}px`);
13297
+ if (props.seq) parts.push(`${SEQ_COL_WIDTH}px`);
10960
13298
  if (hasDetail.value) parts.push(`${EXPAND_COL_WIDTH}px`);
10961
13299
  if (props.selectable !== "none") parts.push(`${SELECTION_COL_WIDTH}px`);
10962
13300
  for (const col of leafColumns.value) {
@@ -10971,70 +13309,45 @@ var IrisTable = vue.defineComponent({
10971
13309
  cellRangeState.value = s;
10972
13310
  })
10973
13311
  );
10974
- const isInRange = (row, col) => {
10975
- const { anchor, active } = cellRangeState.value;
10976
- if (!anchor || !active) return false;
10977
- const minRow = Math.min(anchor.row, active.row);
10978
- const maxRow = Math.max(anchor.row, active.row);
10979
- const minCol = Math.min(anchor.col, active.col);
10980
- const maxCol = Math.max(anchor.col, active.col);
10981
- return row >= minRow && row <= maxRow && col >= minCol && col <= maxCol;
10982
- };
10983
13312
  const rootRef = vue.ref(null);
13313
+ let responsiveObserver = null;
13314
+ const measureResponsiveWidth = () => {
13315
+ responsiveWidth.value = props.responsive && rootRef.value ? rootRef.value.clientWidth : 0;
13316
+ };
13317
+ const updateResponsiveObserver = () => {
13318
+ responsiveObserver?.disconnect();
13319
+ responsiveObserver = null;
13320
+ responsiveWidth.value = 0;
13321
+ if (!props.responsive || !rootRef.value || typeof ResizeObserver === "undefined") return;
13322
+ measureResponsiveWidth();
13323
+ responsiveObserver = new ResizeObserver(measureResponsiveWidth);
13324
+ responsiveObserver.observe(rootRef.value);
13325
+ };
13326
+ vue.onMounted(updateResponsiveObserver);
13327
+ vue.watch(() => props.responsive, updateResponsiveObserver);
13328
+ vue.onBeforeUnmount(() => {
13329
+ responsiveObserver?.disconnect();
13330
+ responsiveObserver = null;
13331
+ });
13332
+ const rowTarget = createTableRowTarget(() => rootRef.value);
13333
+ const { scrollTo: scrollToRow, goTo: goToRow } = rowTarget;
13334
+ vue.onBeforeUnmount(rowTarget.dispose);
10984
13335
  const focusedCell = vue.ref(null);
10985
- const GRID_NAV_KEYS = /* @__PURE__ */ new Set([
10986
- "ArrowUp",
10987
- "ArrowDown",
10988
- "ArrowLeft",
10989
- "ArrowRight",
10990
- "Home",
10991
- "End",
10992
- "PageUp",
10993
- "PageDown"
10994
- ]);
10995
- const handleGridKey = (e) => {
10996
- if (!props.keyboardNavigation || !GRID_NAV_KEYS.has(e.key)) return;
10997
- const target = e.target;
10998
- if (target.dataset.gridRow === void 0) return;
10999
- e.preventDefault();
11000
- const current = focusedCell.value ?? { row: 0, col: 0 };
11001
- const next = core.nextGridCell(current, e.key, {
11002
- rowCount: bodyData.value.length,
11003
- colCount: leafColumns.value.length,
11004
- pageSize: 10
11005
- });
11006
- focusedCell.value = next;
11007
- const cell = rootRef.value?.querySelector(
11008
- `[data-grid-row="${next.row}"][data-grid-col="${next.col}"]`
11009
- );
11010
- cell?.focus();
11011
- };
11012
- const CELL_RANGE_ARROW_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
11013
- const handleCellRangeKey = (e) => {
11014
- if (!props.cellRange) return;
11015
- if (e.key === "Escape") {
11016
- cellRangeCtrl.clearRange();
11017
- return;
11018
- }
11019
- if (!e.shiftKey || !CELL_RANGE_ARROW_KEYS.has(e.key)) return;
11020
- const target = e.target;
11021
- const rowAttr = target.dataset.irisCellRow;
11022
- const colAttr = target.dataset.irisCellCol;
11023
- if (rowAttr === void 0 || colAttr === void 0) return;
11024
- e.preventDefault();
11025
- const anchor = cellRangeCtrl.getState().anchor;
11026
- const active = anchor ? cellRangeCtrl.getState().active ?? { row: Number(rowAttr), col: Number(colAttr) } : { row: Number(rowAttr), col: Number(colAttr) };
11027
- let nextRow = active.row;
11028
- let nextCol = active.col;
11029
- if (e.key === "ArrowUp") nextRow = Math.max(0, nextRow - 1);
11030
- else if (e.key === "ArrowDown") nextRow = Math.min(bodyData.value.length - 1, nextRow + 1);
11031
- else if (e.key === "ArrowLeft") nextCol = Math.max(0, nextCol - 1);
11032
- else nextCol = Math.min(leafColumns.value.length - 1, nextCol + 1);
11033
- cellRangeCtrl.extendRange(nextRow, nextCol);
11034
- };
13336
+ const keyboard = createTableKeyboard({
13337
+ keyboardNavigation: () => props.keyboardNavigation,
13338
+ cellRange: () => props.cellRange,
13339
+ clipConfig: () => props.clipConfig,
13340
+ rows: () => bodyData.value,
13341
+ columns: () => leafColumns.value,
13342
+ root: () => rootRef.value,
13343
+ focused: focusedCell,
13344
+ range: cellRangeCtrl,
13345
+ rangeState: cellRangeState
13346
+ });
13347
+ const { handleRootKeyDown, isInRange, activeCellRange, copyActiveRange } = keyboard;
11035
13348
  const scrollLeft = vue.ref(0);
11036
13349
  const viewportWidth = vue.ref(0);
11037
- const colTrack = (i) => (hasDetail.value ? 1 : 0) + (props.selectable !== "none" ? 2 : 1) + i;
13350
+ const colTrack = (i) => leadTrackCount() + 1 + i;
11038
13351
  if (typeof ResizeObserver !== "undefined") {
11039
13352
  let ro = null;
11040
13353
  vue.onMounted(() => {
@@ -11051,27 +13364,19 @@ var IrisTable = vue.defineComponent({
11051
13364
  ro = null;
11052
13365
  });
11053
13366
  }
11054
- const visibleColSet = vue.computed(() => {
11055
- if (!props.columnVirtualization) return null;
11056
- const cols = leafColumns.value;
11057
- const w = core.computeVirtualRange({
11058
- itemCount: cols.length,
11059
- scrollTop: scrollLeft.value,
11060
- viewportSize: viewportWidth.value,
11061
- itemSize: (i) => effectiveWidths.value[cols[i].key] ?? resolveInitialWidth(cols[i]),
11062
- buffer: 2
11063
- });
11064
- const set = /* @__PURE__ */ new Set();
11065
- for (let i = w.startIndex; i <= w.endIndex; i += 1) set.add(i);
11066
- cols.forEach((col, i) => {
11067
- if (col.pinned) set.add(i);
11068
- });
11069
- return set;
11070
- });
13367
+ const visibleColSet = vue.computed(
13368
+ () => computeVisibleColSet(
13369
+ props.columnVirtualization,
13370
+ leafColumns.value,
13371
+ scrollLeft.value,
13372
+ viewportWidth.value,
13373
+ effectiveWidths.value
13374
+ )
13375
+ );
11071
13376
  const pinnedOffsets = vue.computed(() => {
11072
13377
  const map = {};
11073
13378
  const widthOf = (col) => effectiveWidths.value[col.key] ?? resolveInitialWidth(col);
11074
- let left = (hasDetail.value ? EXPAND_COL_WIDTH : 0) + (props.selectable !== "none" ? SELECTION_COL_WIDTH : 0);
13379
+ 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);
11075
13380
  for (const col of leafColumns.value) {
11076
13381
  if (col.pinned === "left") {
11077
13382
  map[col.key] = { side: "left", offset: left };
@@ -11127,126 +13432,449 @@ var IrisTable = vue.defineComponent({
11127
13432
  }
11128
13433
  });
11129
13434
  };
13435
+ const pinnedDragHandle = createTablePinnedDrag({
13436
+ enabled: () => props.pinnedDrag === true,
13437
+ columns: () => leafColumns.value,
13438
+ widthOf: (column) => effectiveWidths.value[column.key] ?? resolveInitialWidth(column),
13439
+ onColumnPinnedChange: props.onColumnPinnedChange,
13440
+ onPinnedCountChange: props.onPinnedCountChange
13441
+ });
13442
+ const rowDragCtrl = core.createSortable();
13443
+ const rowDragState = vue.shallowRef(rowDragCtrl.getState());
13444
+ vue.onBeforeUnmount(
13445
+ rowDragCtrl.subscribe((s) => {
13446
+ rowDragState.value = s;
13447
+ })
13448
+ );
13449
+ const colDragCtrl = core.createSortable();
13450
+ const colDragState = vue.shallowRef(colDragCtrl.getState());
13451
+ vue.onBeforeUnmount(
13452
+ colDragCtrl.subscribe((s) => {
13453
+ colDragState.value = s;
13454
+ })
13455
+ );
13456
+ const rowRectsRef = vue.ref([]);
13457
+ const colRectsRef = vue.ref([]);
13458
+ const handleRowDragPointerDown = (e, rowId2) => {
13459
+ if (!props.rowDrag || e.button !== 0) return;
13460
+ e.preventDefault();
13461
+ rowDragCtrl.press(rowId2, e.clientX, e.clientY);
13462
+ };
13463
+ const handleRowDragPointerMove = (e) => {
13464
+ if (!props.rowDrag) return;
13465
+ if (rowDragCtrl.isPending()) {
13466
+ const started = rowDragCtrl.tryStart(e.clientX, e.clientY);
13467
+ if (started) {
13468
+ const rects = [];
13469
+ rootRef.value?.querySelectorAll("[data-iris-row-drag-handle]").forEach((el) => {
13470
+ const r = el.getBoundingClientRect();
13471
+ const id = el.getAttribute("data-iris-row-drag-handle");
13472
+ if (id) rects.push({ id, left: r.left, top: r.top, width: r.width, height: r.height });
13473
+ });
13474
+ rowRectsRef.value = rects;
13475
+ }
13476
+ }
13477
+ if (rowDragCtrl.getState().activeId !== null) {
13478
+ rowDragCtrl.moveOver({ x: e.clientX, y: e.clientY }, rowRectsRef.value);
13479
+ }
13480
+ };
13481
+ const handleRowDragPointerUp = () => {
13482
+ if (!props.rowDrag) return;
13483
+ if (rowDragCtrl.isPending()) {
13484
+ rowDragCtrl.cancel();
13485
+ return;
13486
+ }
13487
+ const { activeId, overId } = rowDragCtrl.end();
13488
+ if (activeId !== null && overId !== null && activeId !== overId) {
13489
+ const rows = [...bodyData.value];
13490
+ const from = rows.findIndex((r, i) => String(rowId(r, i)) === activeId);
13491
+ const to = rows.findIndex((r, i) => String(rowId(r, i)) === overId);
13492
+ if (from >= 0 && to >= 0 && from !== to) {
13493
+ const [moved] = rows.splice(from, 1);
13494
+ rows.splice(to, 0, moved);
13495
+ if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
13496
+ else localRowsOverride.value = rows;
13497
+ props.onDataChange?.(rows);
13498
+ props.rowDrag.onReorder(rows);
13499
+ }
13500
+ }
13501
+ rowRectsRef.value = [];
13502
+ };
13503
+ const handleRowDragPointerLeave = () => {
13504
+ if (props.rowDrag && rowDragCtrl.getState().activeId !== null) {
13505
+ rowDragCtrl.cancel();
13506
+ }
13507
+ };
13508
+ const handleColDragPointerDown = (e, colKey) => {
13509
+ if (!props.columnDrag || e.button !== 0) return;
13510
+ e.preventDefault();
13511
+ e.stopPropagation();
13512
+ colDragCtrl.press(colKey, e.clientX, e.clientY);
13513
+ };
13514
+ const handleColDragPointerMove = (e) => {
13515
+ if (!props.columnDrag) return;
13516
+ if (colDragCtrl.isPending()) {
13517
+ const started = colDragCtrl.tryStart(e.clientX, e.clientY);
13518
+ if (started) {
13519
+ const rects = [];
13520
+ rootRef.value?.querySelectorAll("[data-iris-table-header]").forEach((el) => {
13521
+ const r = el.getBoundingClientRect();
13522
+ const id = el.getAttribute("data-iris-table-header");
13523
+ if (id) rects.push({ id, left: r.left, top: r.top, width: r.width, height: r.height });
13524
+ });
13525
+ colRectsRef.value = rects;
13526
+ }
13527
+ }
13528
+ if (colDragCtrl.getState().activeId !== null) {
13529
+ colDragCtrl.moveOver({ x: e.clientX, y: e.clientY }, colRectsRef.value);
13530
+ }
13531
+ };
13532
+ const handleColDragPointerUp = () => {
13533
+ if (!props.columnDrag) return;
13534
+ if (colDragCtrl.isPending()) {
13535
+ colDragCtrl.cancel();
13536
+ return;
13537
+ }
13538
+ const { activeId, overId } = colDragCtrl.end();
13539
+ if (activeId !== null && overId !== null && activeId !== overId) {
13540
+ const next = [...leafColumns.value];
13541
+ const from = next.findIndex((c) => c.key === activeId);
13542
+ const to = next.findIndex((c) => c.key === overId);
13543
+ if (from >= 0 && to >= 0 && from !== to) {
13544
+ const [moved] = next.splice(from, 1);
13545
+ next.splice(to, 0, moved);
13546
+ props.columnDrag.onReorder(next);
13547
+ }
13548
+ }
13549
+ colRectsRef.value = [];
13550
+ };
13551
+ const spanOccupy = /* @__PURE__ */ new Set();
13552
+ const tableExpose = {
13553
+ loadData: (rows) => {
13554
+ if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
13555
+ else localRowsOverride.value = rows;
13556
+ props.onDataChange?.(rows);
13557
+ },
13558
+ reloadData: () => {
13559
+ void proxyCtrl.refetch();
13560
+ },
13561
+ commitProxy: (overrides) => {
13562
+ proxyCtrl.setParams(overrides);
13563
+ },
13564
+ getProxyInfo: () => {
13565
+ if (!proxyCtrl.proxy.value) return null;
13566
+ const s = proxyCtrl.state.value;
13567
+ return { page: s.params.page, pageSize: s.params.pageSize, total: s.total };
13568
+ },
13569
+ removeRows: (keys) => {
13570
+ const { rows, removedKeys } = core.removeRowsFromList(tableData.value, props.rowKey, keys);
13571
+ if (removedKeys.size === 0) return;
13572
+ if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
13573
+ else localRowsOverride.value = rows;
13574
+ const selected = displaySelection.value;
13575
+ const nextSelection = selected.filter((key) => !removedKeys.has(key));
13576
+ if (nextSelection.length !== selected.length) {
13577
+ rebaseToProp();
13578
+ selectionModel.set(nextSelection);
13579
+ }
13580
+ props.onDataChange?.(rows);
13581
+ },
13582
+ getFilteredData: () => [...bodyData.value],
13583
+ exportCurrentViewCsv: () => exportCsv(bodyData.value, leafColumns.value),
13584
+ exportMultiCsv: () => {
13585
+ const current = exportCsv(bodyData.value, leafColumns.value);
13586
+ const names = props.exportNames;
13587
+ if (!names || names.length === 0) return current;
13588
+ const segments = [`# current${current ? `
13589
+ ${current}` : ""}`];
13590
+ for (const entry of names) {
13591
+ if (!entry.key) continue;
13592
+ const refCsv = core.toCsvRows(entry.ref());
13593
+ segments.push(`# ${entry.key}${refCsv ? `
13594
+ ${refCsv}` : ""}`);
13595
+ }
13596
+ return segments.join("\n\n");
13597
+ },
13598
+ compareStates: core.compareStates,
13599
+ scrollToRow,
13600
+ goToRow
13601
+ };
13602
+ expose(tableExpose);
13603
+ const buildFormSection = () => renderFormSection({
13604
+ formConfig: props.formConfig,
13605
+ t,
13606
+ formDraft,
13607
+ setFormValue,
13608
+ handleFormSubmit,
13609
+ handleFormReset
13610
+ });
13611
+ const buildToolbarSection = () => renderToolbarSection({
13612
+ toolbar: props.toolbar,
13613
+ selectable: props.selectable,
13614
+ displaySelection,
13615
+ proxyCtrl,
13616
+ t,
13617
+ importFileInput: importController.importFileInput,
13618
+ onImportFile: importController.handleImportFile,
13619
+ densityToggle: props.densityToggle,
13620
+ effectiveDensity: effectiveDensity.value,
13621
+ onDensityToggle: cycleDensity
13622
+ });
13623
+ const buildPagerSection = () => renderPagerSection({ proxyCtrl, proxyConfig: props.proxyConfig });
13624
+ const contextMenuState = vue.ref(null);
13625
+ const contextAnchorRef = vue.ref(null);
13626
+ const contextMenuRef = vue.ref(null);
13627
+ const closeContextMenu = () => {
13628
+ if (contextMenuState.value) contextMenuState.value.open = false;
13629
+ };
13630
+ const handleContextMenu = (e, row, col, idx, ci) => {
13631
+ if (!props.contextMenu) return;
13632
+ e.preventDefault();
13633
+ contextAnchorRef.value = {
13634
+ getBoundingClientRect: () => ({
13635
+ left: e.clientX,
13636
+ top: e.clientY,
13637
+ right: e.clientX,
13638
+ bottom: e.clientY,
13639
+ width: 0,
13640
+ height: 0,
13641
+ x: e.clientX,
13642
+ y: e.clientY,
13643
+ toJSON() {
13644
+ }
13645
+ })
13646
+ };
13647
+ const params = {
13648
+ row,
13649
+ column: col,
13650
+ rowIndex: idx,
13651
+ columnIndex: ci
13652
+ };
13653
+ contextMenuState.value = { open: true, items: props.contextMenu.items(params), params };
13654
+ };
13655
+ const contextMenuOpen = vue.computed(() => contextMenuState.value?.open === true);
13656
+ const { floatingStyles: contextMenuStyles } = useFloating({
13657
+ anchor: contextAnchorRef,
13658
+ floating: contextMenuRef,
13659
+ open: contextMenuOpen,
13660
+ placement: "bottom-start",
13661
+ // Cursor-anchored menu, vxe parity: with a zero-size anchor at the
13662
+ // viewport edge, flip/shift would clamp the coordinates away from the
13663
+ // cursor (React parity — deliberately disabled).
13664
+ flip: false,
13665
+ shift: false
13666
+ });
13667
+ useDismiss({
13668
+ enabled: contextMenuOpen,
13669
+ exclude: [contextMenuRef],
13670
+ onDismiss: closeContextMenu
13671
+ });
13672
+ vue.watch(contextMenuOpen, (open) => {
13673
+ if (typeof document === "undefined") return;
13674
+ if (open) document.addEventListener("scroll", closeContextMenu, true);
13675
+ else document.removeEventListener("scroll", closeContextMenu, true);
13676
+ });
13677
+ vue.onScopeDispose(() => {
13678
+ if (typeof document === "undefined") return;
13679
+ document.removeEventListener("scroll", closeContextMenu, true);
13680
+ });
13681
+ const buildContextMenuSection = () => renderContextMenuSection({
13682
+ state: contextMenuState,
13683
+ styles: contextMenuStyles,
13684
+ menuRef: contextMenuRef,
13685
+ close: closeContextMenu,
13686
+ contextMenu: props.contextMenu
13687
+ });
13688
+ const filterPanelState = vue.ref(null);
13689
+ const filterAnchorRef = vue.ref(null);
13690
+ const filterPanelRef = vue.ref(null);
13691
+ const filterDraft = vue.ref([]);
13692
+ const closeFilterPanel = () => {
13693
+ if (filterPanelState.value) filterPanelState.value.open = false;
13694
+ };
13695
+ const openFilterPanel = (e, colKey) => {
13696
+ e.stopPropagation();
13697
+ filterAnchorRef.value = e.currentTarget;
13698
+ filterDraft.value = [...props.filterValues?.[colKey] ?? []];
13699
+ filterPanelState.value = { open: true, colKey };
13700
+ };
13701
+ const filterPanelOpen = vue.computed(() => filterPanelState.value?.open === true);
13702
+ const { floatingStyles: filterPanelStyles } = useFloating({
13703
+ anchor: filterAnchorRef,
13704
+ floating: filterPanelRef,
13705
+ open: filterPanelOpen,
13706
+ placement: "bottom-start"
13707
+ });
13708
+ useDismiss({
13709
+ enabled: filterPanelOpen,
13710
+ exclude: [filterPanelRef, filterAnchorRef],
13711
+ onDismiss: closeFilterPanel
13712
+ });
13713
+ vue.watch(filterPanelOpen, (open) => {
13714
+ if (typeof document === "undefined") return;
13715
+ if (open) document.addEventListener("scroll", closeFilterPanel, true);
13716
+ else document.removeEventListener("scroll", closeFilterPanel, true);
13717
+ });
13718
+ vue.onScopeDispose(() => {
13719
+ if (typeof document === "undefined") return;
13720
+ document.removeEventListener("scroll", closeFilterPanel, true);
13721
+ });
13722
+ const applyFilterValues = (colKey, values) => {
13723
+ props.onFilterValuesChange?.({ ...props.filterValues ?? {}, [colKey]: values });
13724
+ };
13725
+ const clearFilterValues = (colKey) => {
13726
+ const next = { ...props.filterValues ?? {} };
13727
+ delete next[colKey];
13728
+ props.onFilterValuesChange?.(next);
13729
+ };
13730
+ const toggleFilterDraft = (value) => {
13731
+ filterDraft.value = filterDraft.value.includes(value) ? filterDraft.value.filter((v) => v !== value) : [...filterDraft.value, value];
13732
+ };
13733
+ const buildFilterPanelSection = () => renderFilterPanelSection({
13734
+ state: filterPanelState,
13735
+ styles: filterPanelStyles,
13736
+ panelRef: filterPanelRef,
13737
+ filterDraft,
13738
+ columns: displayColumns.value,
13739
+ filterValues: props.filterValues,
13740
+ t,
13741
+ close: closeFilterPanel,
13742
+ toggle: toggleFilterDraft,
13743
+ apply: applyFilterValues,
13744
+ clear: clearFilterValues
13745
+ });
11130
13746
  return () => {
11131
13747
  const showSelection = props.selectable !== "none";
11132
13748
  const showDetail = hasDetail.value;
11133
- const buildGroupedHeader = (matrix) => {
11134
- const lead = (showDetail ? 1 : 0) + (showSelection ? 1 : 0);
13749
+ const showDrag = props.rowDrag !== void 0;
13750
+ const showSeq = props.seq === true;
13751
+ spanOccupy.clear();
13752
+ const leadFlatHeaderCells = () => {
11135
13753
  const cells = [];
11136
- if (showDetail) {
13754
+ if (showDrag) {
11137
13755
  cells.push(
11138
13756
  vue.h("div", {
11139
- key: "__expand__",
11140
13757
  role: "columnheader",
11141
- style: { gridColumn: "1", gridRow: "1 / -1" }
13758
+ key: "__drag__",
13759
+ "data-iris-table-header": "__drag",
13760
+ style: {
13761
+ display: "flex",
13762
+ alignItems: "center",
13763
+ justifyContent: "center",
13764
+ padding: "8px",
13765
+ background: "var(--iris-surface)",
13766
+ borderBottom: "1px solid var(--iris-border)"
13767
+ }
11142
13768
  })
11143
13769
  );
11144
13770
  }
11145
- if (showSelection) {
13771
+ if (showSeq) {
11146
13772
  cells.push(
11147
- vue.h(
11148
- "div",
11149
- {
11150
- key: "__select__",
11151
- role: "columnheader",
11152
- "data-iris-table-header": "",
11153
- style: {
11154
- gridColumn: showDetail ? "2" : "1",
11155
- gridRow: "1 / -1",
11156
- display: "flex",
11157
- alignItems: "center",
11158
- justifyContent: "center",
11159
- padding: "8px 12px",
11160
- background: "var(--iris-surface)",
11161
- borderBottom: "1px solid var(--iris-border)"
11162
- }
11163
- },
11164
- props.selectable === "multi" ? [
11165
- [
11166
- vue.h(IrisCheckbox, {
11167
- modelValue: allSelected.value ? true : someSelected.value ? "indeterminate" : false,
11168
- size: "sm",
11169
- ariaLabel: t("table.selectAll"),
11170
- "onUpdate:modelValue": toggleAll
11171
- }),
11172
- props.selection && props.selection.length > 0 ? vue.h(
11173
- "span",
11174
- {
11175
- "data-iris-table-selected-count": "",
11176
- style: {
11177
- marginInlineStart: "var(--iris-space-xs, 8px)",
11178
- fontSize: "var(--iris-font-size-sm, 13px)",
11179
- color: "var(--iris-muted)",
11180
- whiteSpace: "nowrap"
11181
- }
11182
- },
11183
- t("table.selectedCount", { count: String(props.selection.length) })
11184
- ) : null
11185
- ]
11186
- ] : ""
11187
- )
13773
+ vue.h("div", {
13774
+ role: "columnheader",
13775
+ key: "__seq__",
13776
+ "data-iris-table-header": "__seq",
13777
+ style: {
13778
+ display: "flex",
13779
+ alignItems: "center",
13780
+ justifyContent: "center",
13781
+ padding: "8px",
13782
+ background: "var(--iris-surface)",
13783
+ borderBottom: "1px solid var(--iris-border)"
13784
+ }
13785
+ })
11188
13786
  );
11189
13787
  }
11190
- for (const rowCells of matrix) {
11191
- for (const cell of rowCells) {
11192
- const col = cell.column;
11193
- const isLeaf = !col.children || col.children.length === 0;
11194
- const sortable = isLeaf && col.sortable;
11195
- const align = col.align ?? "left";
11196
- const headerSlot = slots[`header.${col.key}`];
11197
- const title = headerSlot?.({ column: col }) ?? col.title;
11198
- cells.push(
11199
- vue.h(
11200
- "div",
11201
- {
11202
- key: `${col.key}-${cell.level}`,
11203
- role: "columnheader",
11204
- "data-iris-table-header": col.key,
11205
- "data-iris-table-header-group": isLeaf ? void 0 : "",
11206
- "aria-colspan": cell.colSpan,
11207
- onClick: sortable ? () => onHeaderClick(col) : void 0,
11208
- "aria-sort": sortable ? internalSort.value?.key === col.key ? internalSort.value.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
11209
- style: {
11210
- gridColumn: `${lead + cell.colStart} / span ${cell.colSpan}`,
11211
- gridRow: `${cell.level + 1} / span ${cell.rowSpan}`,
11212
- display: "flex",
11213
- alignItems: "center",
11214
- justifyContent: isLeaf ? align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start" : "center",
11215
- padding: "8px var(--iris-padding-md)",
11216
- cursor: sortable ? "pointer" : "default",
11217
- userSelect: sortable ? "none" : "auto",
11218
- background: "var(--iris-surface)",
11219
- borderBottom: "1px solid var(--iris-border)",
11220
- fontWeight: "600",
11221
- fontSize: "var(--iris-font-size-md, 14px)",
11222
- color: "var(--iris-foreground)",
11223
- whiteSpace: "nowrap",
11224
- overflow: "hidden",
11225
- textOverflow: "ellipsis"
11226
- }
11227
- },
11228
- [title, sortable ? sortIndicator(col) : null]
11229
- )
11230
- );
11231
- }
13788
+ return cells;
13789
+ };
13790
+ const leadSummaryCells = () => {
13791
+ const cells = [];
13792
+ if (showDrag) {
13793
+ cells.push(
13794
+ vue.h("div", {
13795
+ key: "__drag",
13796
+ role: "cell",
13797
+ "data-iris-table-cell": "__drag",
13798
+ style: {
13799
+ display: "flex",
13800
+ alignItems: "center",
13801
+ justifyContent: "center",
13802
+ padding: "8px 12px"
13803
+ }
13804
+ })
13805
+ );
11232
13806
  }
11233
- return vue.h(
11234
- "div",
11235
- {
11236
- role: "row",
11237
- "data-iris-table-row": "header",
11238
- "data-iris-table-header-row": "",
11239
- "data-iris-table-header-grouped": "",
11240
- style: {
11241
- display: "grid",
11242
- gridTemplateColumns: gridTemplate.value,
11243
- gridTemplateRows: `repeat(${matrix.length}, auto)`
11244
- }
11245
- },
11246
- cells
11247
- );
13807
+ if (showSeq) {
13808
+ cells.push(
13809
+ vue.h("div", {
13810
+ key: "__seq",
13811
+ role: "cell",
13812
+ "data-iris-table-cell": "__seq",
13813
+ style: {
13814
+ display: "flex",
13815
+ alignItems: "center",
13816
+ justifyContent: "center",
13817
+ padding: "8px 12px"
13818
+ }
13819
+ })
13820
+ );
13821
+ }
13822
+ if (showDetail) {
13823
+ cells.push(
13824
+ vue.h("div", {
13825
+ key: "__expand",
13826
+ role: "cell",
13827
+ "data-iris-table-cell": "__expand",
13828
+ style: {
13829
+ display: "flex",
13830
+ alignItems: "center",
13831
+ justifyContent: "center",
13832
+ padding: "8px 12px"
13833
+ }
13834
+ })
13835
+ );
13836
+ }
13837
+ if (showSelection) {
13838
+ cells.push(
13839
+ vue.h("div", {
13840
+ key: "__selection",
13841
+ role: "cell",
13842
+ "data-iris-table-cell": "__selection",
13843
+ style: {
13844
+ display: "flex",
13845
+ alignItems: "center",
13846
+ justifyContent: "center",
13847
+ padding: "8px 12px"
13848
+ }
13849
+ })
13850
+ );
13851
+ }
13852
+ return cells;
11248
13853
  };
11249
- const headerCells = [];
13854
+ const buildGroupedHeader = (matrix) => renderGroupedHeader(
13855
+ {
13856
+ showDrag,
13857
+ showSeq,
13858
+ showDetail,
13859
+ showSelection,
13860
+ selectable: props.selectable,
13861
+ selection: props.selection,
13862
+ allSelected,
13863
+ someSelected,
13864
+ toggleAll,
13865
+ t,
13866
+ slots,
13867
+ onHeaderClick,
13868
+ ariaSortFor,
13869
+ sortIndicator,
13870
+ multiSortSeq,
13871
+ renderFilterTrigger,
13872
+ pinnedDragHandle,
13873
+ gridTemplate
13874
+ },
13875
+ matrix
13876
+ );
13877
+ const headerCells = leadFlatHeaderCells();
11250
13878
  if (showDetail) {
11251
13879
  headerCells.push(
11252
13880
  vue.h("div", {
@@ -11291,14 +13919,15 @@ var IrisTable = vue.defineComponent({
11291
13919
  )
11292
13920
  );
11293
13921
  }
11294
- for (let ci = 0; ci < props.columns.length; ci += 1) {
11295
- const col = props.columns[ci];
13922
+ for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
13923
+ const col = leafColumns.value[ci];
11296
13924
  if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
11297
13925
  const align = col.align ?? "left";
11298
13926
  const headerSlot = slots[`header.${col.key}`];
11299
13927
  const title = headerSlot?.({ column: col }) ?? col.title;
11300
13928
  wireResize(col);
11301
- const handle = props.resizableColumns ? vue.h("span", {
13929
+ const pinnedHandle = pinnedDragHandle(col);
13930
+ const handle = props.resizableColumns && !pinnedHandle ? vue.h("span", {
11302
13931
  ref: (el) => {
11303
13932
  getHandleRef(col.key).value = el ?? null;
11304
13933
  },
@@ -11342,6 +13971,13 @@ var IrisTable = vue.defineComponent({
11342
13971
  role: "columnheader",
11343
13972
  "data-iris-table-header": col.key,
11344
13973
  "data-iris-table-pinned": col.pinned,
13974
+ // Column drag-sort (vxe columnDragConfig parity, batch Y): the
13975
+ // header cell is the press target; grouped headers are NOT
13976
+ // supported (documented simplification — the reorder maps leaf
13977
+ // columns back to the parent, which a group column cannot do).
13978
+ "data-iris-col-drag-active": props.columnDrag && colDragState.value.activeId === col.key ? "true" : void 0,
13979
+ "data-iris-col-drag-over": props.columnDrag && colDragState.value.overId === col.key ? "true" : void 0,
13980
+ onPointerdown: props.columnDrag && !grouped.value ? (e) => handleColDragPointerDown(e, col.key) : void 0,
11345
13981
  onClick: () => onHeaderClick(col),
11346
13982
  style: {
11347
13983
  position: "relative",
@@ -11362,9 +13998,16 @@ var IrisTable = vue.defineComponent({
11362
13998
  ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
11363
13999
  ...col.pinned ? { ...pinnedStyle(col.key), background: "var(--iris-surface)" } : {}
11364
14000
  },
11365
- "aria-sort": internalSort.value?.key === col.key ? internalSort.value.direction === "asc" ? "ascending" : "descending" : col.sortable ? "none" : void 0
14001
+ "aria-sort": ariaSortFor(col)
11366
14002
  },
11367
- [title, sortIndicator(col), handle]
14003
+ [
14004
+ title,
14005
+ sortIndicator(col),
14006
+ multiSortSeq(col),
14007
+ renderFilterTrigger(col, true),
14008
+ pinnedHandle,
14009
+ handle
14010
+ ]
11368
14011
  )
11369
14012
  );
11370
14013
  }
@@ -11384,6 +14027,69 @@ var IrisTable = vue.defineComponent({
11384
14027
  const id = rowId(row, index);
11385
14028
  const selected = isSelected(id);
11386
14029
  const cells = [];
14030
+ if (showDrag) {
14031
+ const rowDragActive = rowDragState.value.activeId === String(id);
14032
+ const rowDragOver = rowDragState.value.overId === String(id);
14033
+ cells.push(
14034
+ vue.h(
14035
+ "div",
14036
+ {
14037
+ key: "__drag",
14038
+ role: "cell",
14039
+ "data-iris-table-cell": "__drag",
14040
+ // The drag handle carries the row's id as its attribute VALUE
14041
+ // (rect collection for closestCenter reads it back).
14042
+ "data-iris-row-drag-handle": String(id),
14043
+ "data-iris-row-drag-active": rowDragActive ? "true" : void 0,
14044
+ "data-iris-row-drag-over": rowDragOver ? "true" : void 0,
14045
+ onPointerdown: (e) => handleRowDragPointerDown(e, String(id)),
14046
+ onClick: (e) => e.stopPropagation(),
14047
+ style: {
14048
+ display: "flex",
14049
+ alignItems: "center",
14050
+ justifyContent: "center",
14051
+ padding: "8px 12px",
14052
+ borderBottom: "1px solid var(--iris-border)",
14053
+ cursor: "grab",
14054
+ color: "var(--iris-muted)",
14055
+ background: rowDragActive ? "var(--iris-surface-hover)" : rowDragOver ? "var(--iris-surface-selected, rgba(99,102,241,0.12))" : "transparent"
14056
+ }
14057
+ },
14058
+ [
14059
+ vue.h(
14060
+ "span",
14061
+ {
14062
+ "aria-hidden": "true",
14063
+ style: { fontSize: "var(--iris-font-size-sm, 13px)" }
14064
+ },
14065
+ "\u283F"
14066
+ )
14067
+ ]
14068
+ )
14069
+ );
14070
+ }
14071
+ if (showSeq) {
14072
+ cells.push(
14073
+ vue.h(
14074
+ "div",
14075
+ {
14076
+ key: "__seq",
14077
+ role: "cell",
14078
+ "data-iris-table-cell": "__seq",
14079
+ style: {
14080
+ display: "flex",
14081
+ alignItems: "center",
14082
+ justifyContent: "center",
14083
+ padding: "8px 12px",
14084
+ borderBottom: "1px solid var(--iris-border)",
14085
+ color: "var(--iris-muted)",
14086
+ userSelect: "none"
14087
+ }
14088
+ },
14089
+ String(index + props.seqStartIndex)
14090
+ )
14091
+ );
14092
+ }
11387
14093
  if (showDetail) {
11388
14094
  const rowExpandable = isRowExpandable(row, index);
11389
14095
  const isExpanded = expandedKeys.value.includes(String(id));
@@ -11460,115 +14166,30 @@ var IrisTable = vue.defineComponent({
11460
14166
  for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
11461
14167
  const col = leafColumns.value[ci];
11462
14168
  if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
14169
+ const span = resolveSpan(spanOccupy, index, ci, props.spanMethod);
14170
+ if (span === null) continue;
14171
+ const rowspan = span.rowspan;
14172
+ const colspan = span.colspan;
11463
14173
  const align = col.align ?? "left";
11464
14174
  const cellSlot = slots[`cell.${col.key}`];
11465
- const isEditing = editingCellId.value === cellId(id, col.key);
14175
+ const isRowEditing = rowMode.value && rowEditing.value !== null && rowEditing.value.k === id && rowSessions.value.has(cellId(id, col.key));
14176
+ const isEditing = isRowEditing || editingCellId.value === cellId(id, col.key);
14177
+ const patternHint = (props.pattern || props.patternFill) && !rowMode.value && editingColumnKey.value === col.key && !isEditing && editingDraft.value !== "" && String(getCellValue(row, col) ?? "") === editingDraft.value;
11466
14178
  let content;
11467
14179
  if (isEditing) {
11468
14180
  const editCellId = cellId(id, col.key);
11469
- const error = editError.value;
11470
- const input = vue.h("input", {
11471
- ref: (el) => {
11472
- editorInputRef.value = el ?? null;
11473
- },
11474
- type: col.editor === "number" ? "number" : "text",
11475
- value: editingDraft.value,
11476
- "data-iris-table-editor": "",
11477
- "aria-invalid": error ? "true" : void 0,
11478
- "aria-describedby": error ? `${editCellId}-error` : void 0,
11479
- onInput: (e) => {
11480
- editingDraft.value = e.target.value;
11481
- },
11482
- onKeydown: (e) => {
11483
- if (e.key === "Enter") {
11484
- e.preventDefault();
11485
- commitEdit(row, col, index);
11486
- } else if (e.key === "Escape") {
11487
- e.preventDefault();
11488
- cancelEdit();
11489
- }
11490
- },
11491
- onBlur: () => commitEdit(row, col, index),
11492
- onClick: (e) => e.stopPropagation(),
11493
- onDblclick: (e) => e.stopPropagation(),
11494
- style: {
11495
- width: "100%",
11496
- border: `1px solid ${error ? "var(--iris-danger)" : "var(--iris-primary)"}`,
11497
- borderRadius: "var(--iris-radius-sm)",
11498
- padding: "var(--iris-space-xxs, 4px) var(--iris-padding-sm, 6px)",
11499
- font: "inherit",
11500
- background: "var(--iris-background)",
11501
- color: "var(--iris-foreground)",
11502
- outline: "none",
11503
- boxShadow: "0 0 0 3px color-mix(in srgb, var(--iris-primary) 18%, transparent)"
11504
- }
11505
- });
11506
- content = error ? [
11507
- input,
11508
- vue.h(
11509
- "div",
11510
- {
11511
- id: `${editCellId}-error`,
11512
- role: "alert",
11513
- "data-iris-table-editor-error": "",
11514
- style: {
11515
- marginTop: "var(--iris-space-xxs, 4px)",
11516
- fontSize: "var(--iris-font-size-xs, 12px)",
11517
- color: "var(--iris-danger)"
11518
- }
11519
- },
11520
- error
11521
- )
11522
- ] : input;
14181
+ content = isRowEditing ? buildRowSessionContent(
14182
+ row,
14183
+ col,
14184
+ index,
14185
+ id,
14186
+ editCellId,
14187
+ rowSessions.value.get(editCellId)
14188
+ ) : buildCellEditContent(row, col, index, editCellId);
11523
14189
  } else {
11524
- content = cellSlot?.({ row, index, value: getCellValue2(row, col) }) ?? String(getCellValue2(row, col) ?? "");
14190
+ content = cellSlot?.({ row, index, value: getCellValue(row, col) }) ?? core.tableDisplayText(row, col);
11525
14191
  }
11526
- const treeIndent = treeMeta && ci === 0 ? vue.h(
11527
- "span",
11528
- {
11529
- "data-iris-table-tree-indent": "",
11530
- style: {
11531
- display: "inline-flex",
11532
- alignItems: "center",
11533
- flex: "none",
11534
- paddingLeft: `${treeMeta.depth * 16}px`
11535
- }
11536
- },
11537
- treeMeta.hasChildren ? [
11538
- vue.h(
11539
- "button",
11540
- {
11541
- type: "button",
11542
- "data-iris-table-tree-toggle": "",
11543
- "aria-expanded": treeMeta.expanded ? "true" : "false",
11544
- "aria-label": t(
11545
- treeMeta.expanded ? "treeSelect.collapse" : "treeSelect.expand"
11546
- ),
11547
- onClick: (e) => {
11548
- e.stopPropagation();
11549
- expansion.toggle(treeMeta.key);
11550
- },
11551
- style: {
11552
- border: "none",
11553
- background: "transparent",
11554
- cursor: "pointer",
11555
- padding: "0",
11556
- marginRight: "4px",
11557
- font: "inherit",
11558
- color: "var(--iris-foreground)",
11559
- transform: treeMeta.expanded ? "rotate(90deg)" : "none",
11560
- transition: "transform 150ms"
11561
- }
11562
- },
11563
- "\u25B6"
11564
- )
11565
- ] : [
11566
- vue.h("span", {
11567
- "aria-hidden": "true",
11568
- style: { display: "inline-block", width: "16px" }
11569
- })
11570
- ]
11571
- ) : null;
14192
+ const treeIndent = treeMeta && ci === 0 ? buildTreeIndent(treeMeta, row) : null;
11572
14193
  const cellChildren = treeIndent ? [treeIndent, ...Array.isArray(content) ? content : [content]] : content;
11573
14194
  cells.push(
11574
14195
  vue.h(
@@ -11580,6 +14201,7 @@ var IrisTable = vue.defineComponent({
11580
14201
  "data-iris-table-pinned": col.pinned,
11581
14202
  "data-editable": col.editable ? "" : void 0,
11582
14203
  "data-editing": isEditing ? "" : void 0,
14204
+ "data-iris-input-hint": patternHint ? "true" : void 0,
11583
14205
  // Grid keyboard navigation (opt-in): grid coords + roving tabindex
11584
14206
  // (exactly one cell is 0) + an onFocus that syncs the focused cell.
11585
14207
  ...props.keyboardNavigation ? {
@@ -11603,14 +14225,19 @@ var IrisTable = vue.defineComponent({
11603
14225
  }
11604
14226
  }
11605
14227
  } : {},
11606
- onDblclick: col.editable ? () => beginEdit(row, col, id) : void 0,
11607
- onClick: col.editable && props.editConfig?.trigger === "click" ? () => beginEdit(row, col, id) : void 0,
14228
+ onDblclick: rowMode.value ? () => switchRowEdit(row, index, col.key) : col.editable ? () => beginEdit(row, col, id) : void 0,
14229
+ onClick: rowMode.value ? () => handleRowModeCellClick(row, col, index, id) : props.cellRange ? (e) => {
14230
+ if (e.shiftKey) cellRangeCtrl.extendRange(index, ci);
14231
+ else cellRangeCtrl.startRange(index, ci);
14232
+ } : col.editable && props.editConfig?.trigger === "click" ? () => beginEdit(row, col, id) : void 0,
14233
+ onContextmenu: props.contextMenu ? (e) => handleContextMenu(e, row, col, index, ci) : void 0,
11608
14234
  style: {
11609
14235
  display: "flex",
11610
14236
  alignItems: "center",
11611
14237
  justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
11612
14238
  background: "var(--iris-cell-bg, transparent)",
11613
14239
  padding: isEditing ? "var(--iris-space-xxs, 4px)" : "var(--iris-space-xs, 8px) var(--iris-padding-md)",
14240
+ ...isEditing ? { flexWrap: "wrap" } : {},
11614
14241
  borderBottom: "1px solid var(--iris-border)",
11615
14242
  fontSize: "var(--iris-font-size-md, 14px)",
11616
14243
  whiteSpace: "nowrap",
@@ -11619,6 +14246,11 @@ var IrisTable = vue.defineComponent({
11619
14246
  cursor: col.editable ? "cell" : "default",
11620
14247
  ...props.cellRange && isInRange(index, ci) ? { background: "var(--iris-surface-selected, rgba(99,102,241,0.12))" } : {},
11621
14248
  ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
14249
+ ...colspan > 1 ? { gridColumnEnd: `span ${colspan}` } : {},
14250
+ ...rowspan > 1 ? { gridRowEnd: `span ${rowspan}` } : {},
14251
+ ...patternHint ? {
14252
+ backgroundImage: "linear-gradient(var(--iris-input-hint, rgba(251, 191, 36, 0.16)), var(--iris-input-hint, rgba(251, 191, 36, 0.16)))"
14253
+ } : {},
11622
14254
  ...pinnedStyle(col.key)
11623
14255
  }
11624
14256
  },
@@ -11640,6 +14272,8 @@ var IrisTable = vue.defineComponent({
11640
14272
  "aria-setsize": treeMeta ? treeMeta.setSize : void 0,
11641
14273
  "aria-posinset": treeMeta ? treeMeta.posInset : void 0,
11642
14274
  "data-iris-table-row": "",
14275
+ "data-iris-table-row-key": String(id),
14276
+ "data-iris-row-editing": rowMode.value && rowEditing.value?.k === id ? "true" : void 0,
11643
14277
  "data-state": selected ? "selected" : void 0,
11644
14278
  onClick: () => emit("rowClick", row, index),
11645
14279
  onDblclick: () => emit("rowDblclick", row, index),
@@ -11660,50 +14294,21 @@ var IrisTable = vue.defineComponent({
11660
14294
  textAlign: "center",
11661
14295
  color: "var(--iris-muted)"
11662
14296
  };
14297
+ const stateNode = renderTableStateRow({
14298
+ error: tableError.value,
14299
+ loading: tableLoading.value,
14300
+ rowCount: bodyData.value.length,
14301
+ stateRowStyle,
14302
+ errorContent: slots.error ? slots.error() : t("table.error"),
14303
+ loadingContent: slots.loading ? slots.loading() : t("table.loading"),
14304
+ emptyContent: slots.empty ? slots.empty() : t("table.empty"),
14305
+ retry: retry.value,
14306
+ onRetry: props.onRetry,
14307
+ retryLabel: t("table.retry")
14308
+ });
11663
14309
  let bodyNode;
11664
- if (props.error) {
11665
- bodyNode = vue.h("div", { role: "row", "data-iris-table-row": "error", style: stateRowStyle }, [
11666
- vue.h(
11667
- "span",
11668
- { style: { marginInlineEnd: props.onRetry ? "var(--iris-space-sm, 12px)" : "0px" } },
11669
- slots.error ? slots.error() : t("table.error")
11670
- ),
11671
- props.onRetry ? vue.h(
11672
- "button",
11673
- {
11674
- type: "button",
11675
- "data-iris-table-retry": "",
11676
- onClick: props.onRetry,
11677
- style: {
11678
- border: "1px solid var(--iris-border)",
11679
- background: "var(--iris-surface)",
11680
- color: "var(--iris-foreground)",
11681
- borderRadius: "var(--iris-radius-sm, 4px)",
11682
- padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
11683
- fontSize: "var(--iris-font-size-sm, 13px)",
11684
- cursor: "pointer"
11685
- }
11686
- },
11687
- t("table.retry")
11688
- ) : null
11689
- ]);
11690
- } else if (props.loading) {
11691
- bodyNode = vue.h(
11692
- "div",
11693
- {
11694
- role: "row",
11695
- "aria-busy": "true",
11696
- "data-iris-table-row": "loading",
11697
- style: stateRowStyle
11698
- },
11699
- slots.loading ? slots.loading() : t("table.loading")
11700
- );
11701
- } else if (bodyData.value.length === 0) {
11702
- bodyNode = vue.h(
11703
- "div",
11704
- { role: "row", "data-iris-table-row": "empty", style: stateRowStyle },
11705
- slots.empty ? slots.empty() : t("table.empty")
11706
- );
14310
+ if (stateNode) {
14311
+ bodyNode = stateNode;
11707
14312
  } else if (props.virtualScroll && (!treeMode.value || !hasDetail.value)) {
11708
14313
  bodyNode = vue.h(
11709
14314
  IrisVirtualScroll,
@@ -11767,127 +14372,105 @@ var IrisTable = vue.defineComponent({
11767
14372
  bodyChildren
11768
14373
  );
11769
14374
  }
11770
- let summaryRow = null;
11771
- if (!props.error && !props.loading && bodyData.value.length > 0 && leafColumns.value.some((c) => c.summary)) {
11772
- const summaryCells = [];
11773
- if (showSelection) {
11774
- summaryCells.push(
11775
- vue.h("div", {
11776
- key: "__selection",
11777
- role: "cell",
11778
- "data-iris-table-cell": "__selection",
11779
- style: {
11780
- display: "flex",
11781
- alignItems: "center",
11782
- justifyContent: "center",
11783
- padding: "8px 12px"
11784
- }
11785
- })
11786
- );
11787
- }
11788
- for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
11789
- const col = leafColumns.value[ci];
11790
- if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
11791
- const align = col.align ?? "left";
11792
- const op = col.summary;
11793
- const value = op ? core.aggregate(bodyData.value, (r) => getCellValue2(r, col), op) : null;
11794
- const summaryContent = op != null && value != null ? col.renderSummary ? col.renderSummary(value, bodyData.value) : String(value) : "";
11795
- summaryCells.push(
11796
- vue.h(
11797
- "div",
11798
- {
11799
- key: col.key,
11800
- role: "cell",
11801
- "data-iris-table-cell": col.key,
11802
- "data-iris-table-summary-cell": op ? "" : void 0,
11803
- "data-iris-table-pinned": col.pinned,
11804
- style: {
11805
- display: "flex",
11806
- alignItems: "center",
11807
- justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
11808
- padding: "var(--iris-space-xs, 8px) var(--iris-padding-md)",
11809
- fontSize: "var(--iris-font-size-md, 14px)",
11810
- whiteSpace: "nowrap",
11811
- overflow: "hidden",
11812
- textOverflow: "ellipsis",
11813
- ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
11814
- ...pinnedStyle(col.key)
11815
- }
11816
- },
11817
- summaryContent
11818
- )
11819
- );
11820
- }
11821
- summaryRow = vue.h(
14375
+ const summaryRow = !tableError.value && !tableLoading.value ? renderTableSummaryRow({
14376
+ bodyData: bodyData.value,
14377
+ leafColumns: leafColumns.value,
14378
+ visibleColSet: visibleColSet.value,
14379
+ gridTemplate: gridTemplate.value,
14380
+ leadingCells: leadSummaryCells(),
14381
+ colTrack,
14382
+ getCellValue,
14383
+ pinnedStyle
14384
+ }) : null;
14385
+ const rootNodes = [
14386
+ tableViews.renderTabs(props.tableTabs),
14387
+ tableViews.renderViews(),
14388
+ buildFormSection(),
14389
+ buildToolbarSection(),
14390
+ props.clipConfig && props.clipConfig.copy !== false && activeCellRange() ? vue.h(
14391
+ "button",
14392
+ { type: "button", "data-iris-table-range-copy": "", onClick: copyActiveRange },
14393
+ t("table.range.copy")
14394
+ ) : null,
14395
+ renderImportPreviewSection({
14396
+ rows: importController.importPreviewRows.value,
14397
+ t,
14398
+ onConfirm: importController.confirmImportPreview,
14399
+ onCancel: importController.cancelImportPreview
14400
+ }),
14401
+ vue.h(
11822
14402
  "div",
11823
14403
  {
11824
- role: "row",
11825
- "data-iris-table-row": "summary",
14404
+ ...attrs,
14405
+ ref: (el) => {
14406
+ rootRef.value = el ?? null;
14407
+ },
14408
+ // A keyboard-navigable hierarchical table is a `treegrid`; otherwise the
14409
+ // grid/table role as before (treegrid implies managed cell focus).
14410
+ role: props.keyboardNavigation ? treeMode.value ? "treegrid" : "grid" : "table",
14411
+ "data-iris-table": "",
14412
+ "data-density": effectiveDensity.value,
14413
+ "data-printable": props.printable ? "true" : void 0,
14414
+ "data-virtual": props.virtualScroll ? "" : void 0,
14415
+ "data-column-virtualized": props.columnVirtualization ? "true" : void 0,
14416
+ onKeydown: props.keyboardNavigation || props.cellRange || props.clipConfig ? (e) => handleRootKeyDown(e) : void 0,
14417
+ onScroll: props.columnVirtualization ? (e) => {
14418
+ scrollLeft.value = e.currentTarget.scrollLeft;
14419
+ } : void 0,
14420
+ // Row/column drag-sort (batch Y): container-level pointer handling
14421
+ // for both reorders (React parity) — press happens on the row
14422
+ // handle / header cell, move/up resolve at the container.
14423
+ onPointermove: props.rowDrag || props.columnDrag ? (e) => {
14424
+ handleRowDragPointerMove(e);
14425
+ handleColDragPointerMove(e);
14426
+ } : void 0,
14427
+ onPointerup: props.rowDrag || props.columnDrag ? () => {
14428
+ handleRowDragPointerUp();
14429
+ handleColDragPointerUp();
14430
+ } : void 0,
14431
+ onPointerleave: props.rowDrag ? handleRowDragPointerLeave : void 0,
11826
14432
  style: {
11827
- display: "grid",
11828
- gridTemplateColumns: gridTemplate.value,
11829
- fontWeight: "600",
11830
- borderTop: "2px solid var(--iris-border)",
11831
- background: "var(--iris-surface)"
14433
+ background: "var(--iris-background)",
14434
+ color: "var(--iris-foreground)",
14435
+ fontSize: "var(--iris-font-size-md, 14px)",
14436
+ border: props.bordered ? "1px solid var(--iris-border)" : "none",
14437
+ borderRadius: "var(--iris-radius-md)",
14438
+ // Column virtualization and responsive overflow turn the table
14439
+ // into a horizontal scroll container.
14440
+ overflow: props.columnVirtualization || responsiveOverflow.value ? "auto" : "hidden",
14441
+ ...responsiveOverflow.value ? { overflowX: "auto" } : {},
14442
+ ...attrs.style ?? {}
11832
14443
  }
11833
14444
  },
11834
- summaryCells
11835
- );
11836
- }
11837
- return vue.h(
11838
- "div",
11839
- {
11840
- ...attrs,
11841
- ref: (el) => {
11842
- rootRef.value = el ?? null;
14445
+ [headerRow, bodyNode, summaryRow, buildPagerSection()]
14446
+ ),
14447
+ props.responsive && responsiveOverflow.value && !props.printable ? vue.h(
14448
+ "div",
14449
+ {
14450
+ "data-iris-scroll-hint": "",
14451
+ role: "status",
14452
+ "aria-live": "polite",
14453
+ style: {
14454
+ display: "flex",
14455
+ alignItems: "center",
14456
+ gap: "var(--iris-space-xxs, 4px)",
14457
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-sm, 12px)",
14458
+ color: "var(--iris-muted)",
14459
+ background: "var(--iris-surface)",
14460
+ borderInline: "1px solid var(--iris-border)",
14461
+ borderBottom: "1px solid var(--iris-border)",
14462
+ fontSize: "var(--iris-font-size-sm, 13px)"
14463
+ }
11843
14464
  },
11844
- // A keyboard-navigable hierarchical table is a `treegrid`; otherwise the
11845
- // grid/table role as before (treegrid implies managed cell focus).
11846
- role: props.keyboardNavigation ? treeMode.value ? "treegrid" : "grid" : "table",
11847
- "data-iris-table": "",
11848
- "data-virtual": props.virtualScroll ? "" : void 0,
11849
- "data-column-virtualized": props.columnVirtualization ? "true" : void 0,
11850
- onKeydown: props.keyboardNavigation || props.cellRange ? (e) => {
11851
- if (props.keyboardNavigation) handleGridKey(e);
11852
- if (props.cellRange) handleCellRangeKey(e);
11853
- } : void 0,
11854
- onScroll: props.columnVirtualization ? (e) => {
11855
- scrollLeft.value = e.currentTarget.scrollLeft;
11856
- } : void 0,
11857
- style: {
11858
- background: "var(--iris-background)",
11859
- color: "var(--iris-foreground)",
11860
- fontSize: "var(--iris-font-size-md, 14px)",
11861
- border: props.bordered ? "1px solid var(--iris-border)" : "none",
11862
- borderRadius: "var(--iris-radius-md)",
11863
- // Column virtualization turns the table into a horizontal scroll container.
11864
- overflow: props.columnVirtualization ? "auto" : "hidden",
11865
- ...attrs.style ?? {}
11866
- }
11867
- },
11868
- [headerRow, bodyNode, summaryRow]
11869
- );
14465
+ [vue.h("span", { "aria-hidden": "true" }, "\u21C6"), vue.h("span", t("table.scrollHint"))]
14466
+ ) : null,
14467
+ buildContextMenuSection(),
14468
+ buildFilterPanelSection()
14469
+ ].filter((n) => n !== null);
14470
+ return rootNodes.length === 1 ? rootNodes[0] : rootNodes;
11870
14471
  };
11871
14472
  }
11872
14473
  });
11873
- function exportCsv(rows, columns) {
11874
- return core.toCsv(
11875
- rows,
11876
- columns.map((column) => ({
11877
- key: column.key,
11878
- title: column.title,
11879
- dataIndex: typeof column.dataIndex === "string" ? column.dataIndex : void 0
11880
- }))
11881
- );
11882
- }
11883
- async function downloadCsv(filename, csv) {
11884
- const BOM = String.fromCharCode(65279);
11885
- await core.downloadFile({
11886
- filename,
11887
- content: BOM + csv,
11888
- mimeType: "text/csv;charset=utf-8;"
11889
- });
11890
- }
11891
14474
  function exportExcel(rows, columns, options) {
11892
14475
  return core.toSpreadsheetXml(
11893
14476
  rows,
@@ -11908,7 +14491,7 @@ async function downloadExcel(filename, xml) {
11908
14491
  }
11909
14492
 
11910
14493
  // src/primitives/tabs/context.ts
11911
- var TabsContextKey = /* @__PURE__ */ Symbol("IrisTabs");
14494
+ var TabsContextKey = Symbol("IrisTabs");
11912
14495
 
11913
14496
  // src/primitives/tabs/Tabs.ts
11914
14497
  var IrisTabs = vue.defineComponent({
@@ -12208,7 +14791,7 @@ var IrisTabsContent = vue.defineComponent({
12208
14791
  });
12209
14792
 
12210
14793
  // src/primitives/menu/context.ts
12211
- var MenuContextKey = /* @__PURE__ */ Symbol("IrisMenu");
14794
+ var MenuContextKey = Symbol("IrisMenu");
12212
14795
 
12213
14796
  // src/primitives/menu/Menu.ts
12214
14797
  var IrisMenu = vue.defineComponent({
@@ -13055,6 +15638,29 @@ var TREE_STATE_STYLE = {
13055
15638
  color: "var(--iris-muted)",
13056
15639
  fontSize: "var(--iris-font-size-md, 14px)"
13057
15640
  };
15641
+ function renderTreeState({
15642
+ state,
15643
+ stateKey,
15644
+ stateProps,
15645
+ slots,
15646
+ translate
15647
+ }) {
15648
+ 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");
15649
+ return vue.h(
15650
+ "div",
15651
+ {
15652
+ key: stateKey,
15653
+ role: "presentation",
15654
+ "data-iris-tree-state": state,
15655
+ "aria-live": "polite",
15656
+ ...stateProps,
15657
+ style: TREE_STATE_STYLE
15658
+ },
15659
+ content
15660
+ );
15661
+ }
15662
+
15663
+ // src/primitives/tree/Tree.ts
13058
15664
  var IrisTree = vue.defineComponent({
13059
15665
  name: "IrisTree",
13060
15666
  inheritAttrs: false,
@@ -13284,21 +15890,6 @@ var IrisTree = vue.defineComponent({
13284
15890
  break;
13285
15891
  }
13286
15892
  }
13287
- const renderStateNode = () => {
13288
- 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");
13289
- return vue.h(
13290
- "div",
13291
- {
13292
- key: stateKey.value,
13293
- role: "presentation",
13294
- "data-iris-tree-state": state.value,
13295
- "aria-live": "polite",
13296
- ...stateProps.value,
13297
- style: TREE_STATE_STYLE
13298
- },
13299
- content
13300
- );
13301
- };
13302
15893
  const renderItems = () => {
13303
15894
  void checkedKeys.value;
13304
15895
  return flat.value.map((entry) => {
@@ -13431,7 +16022,15 @@ var IrisTree = vue.defineComponent({
13431
16022
  ...attrs.style ?? {}
13432
16023
  }
13433
16024
  },
13434
- isContent.value ? renderItems() : [renderStateNode()]
16025
+ isContent.value ? renderItems() : [
16026
+ renderTreeState({
16027
+ state: state.value,
16028
+ stateKey: stateKey.value,
16029
+ stateProps: stateProps.value,
16030
+ slots,
16031
+ translate: t
16032
+ })
16033
+ ]
13435
16034
  );
13436
16035
  }
13437
16036
  });
@@ -18664,12 +21263,12 @@ function defaultFilter(query, item) {
18664
21263
  if (!q) return 0;
18665
21264
  const haystacks = [item.label, ...item.keywords ?? []].map((s) => s.toLowerCase());
18666
21265
  let best = null;
18667
- for (const h124 of haystacks) {
21266
+ for (const h136 of haystacks) {
18668
21267
  let qi = 0;
18669
21268
  let lastIdx = -1;
18670
21269
  let score = 0;
18671
- for (let i = 0; i < h124.length && qi < q.length; i += 1) {
18672
- if (h124[i] === q[qi]) {
21270
+ for (let i = 0; i < h136.length && qi < q.length; i += 1) {
21271
+ if (h136[i] === q[qi]) {
18673
21272
  score += lastIdx === -1 ? i : i - lastIdx - 1;
18674
21273
  lastIdx = i;
18675
21274
  qi += 1;
@@ -19035,8 +21634,8 @@ var IrisTimePicker = vue.defineComponent({
19035
21634
  if (props.format === "24h") {
19036
21635
  return { h: value.value.hours, m: value.value.minutes };
19037
21636
  }
19038
- const h124 = value.value.hours % 12 === 0 ? 12 : value.value.hours % 12;
19039
- return { h: h124, m: value.value.minutes };
21637
+ const h1210 = value.value.hours % 12 === 0 ? 12 : value.value.hours % 12;
21638
+ return { h: h1210, m: value.value.minutes };
19040
21639
  });
19041
21640
  const emitNew = (next) => {
19042
21641
  emit("update:modelValue", next);
@@ -19052,16 +21651,16 @@ var IrisTimePicker = vue.defineComponent({
19052
21651
  };
19053
21652
  const toggleMeridiem = () => {
19054
21653
  const isPM = meridiem.value === "PM";
19055
- const h124 = value.value.hours % 12;
19056
- const newH24 = isPM ? h124 : h124 + 12;
21654
+ const h1210 = value.value.hours % 12;
21655
+ const newH24 = isPM ? h1210 : h1210 + 12;
19057
21656
  emitNew({ hours: newH24, minutes: value.value.minutes });
19058
21657
  };
19059
21658
  const onHoursInput = (e) => {
19060
21659
  const v = parseInt(e.target.value || "0", 10);
19061
21660
  if (Number.isNaN(v)) return;
19062
21661
  if (props.format === "12h") {
19063
- const h124 = clamp5(v, 1, 12);
19064
- const wrap = h124 === 12 ? 0 : h124;
21662
+ const h1210 = clamp5(v, 1, 12);
21663
+ const wrap = h1210 === 12 ? 0 : h1210;
19065
21664
  const newH24 = meridiem.value === "PM" ? wrap + 12 : wrap;
19066
21665
  setHours24(newH24);
19067
21666
  } else {
@@ -20582,6 +23181,10 @@ Object.defineProperty(exports, "nodeAllowsRoles", {
20582
23181
  enumerable: true,
20583
23182
  get: function () { return core.nodeAllowsRoles; }
20584
23183
  });
23184
+ Object.defineProperty(exports, "parseCsv", {
23185
+ enumerable: true,
23186
+ get: function () { return core.parseCsv; }
23187
+ });
20585
23188
  Object.defineProperty(exports, "rgbToHex", {
20586
23189
  enumerable: true,
20587
23190
  get: function () { return core.rgbToHex; }