@iris-ui-kit/vue 0.2.28 → 0.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/admin.cjs +3 -3
  2. package/dist/admin.cjs.map +1 -1
  3. package/dist/admin.js +3 -3
  4. package/dist/behaviors.d.cts +2 -2
  5. package/dist/behaviors.d.ts +2 -2
  6. package/dist/{chunk-IAG64CHL.js → chunk-BVKOOTXG.js} +3 -3
  7. package/dist/chunk-BVKOOTXG.js.map +1 -0
  8. package/dist/{chunk-AMNHDYFY.js → chunk-EZUDM4TM.js} +5 -5
  9. package/dist/chunk-EZUDM4TM.js.map +1 -0
  10. package/dist/{chunk-UPC4GN3F.js → chunk-KAOPZNZF.js} +3 -3
  11. package/dist/chunk-KAOPZNZF.js.map +1 -0
  12. package/dist/{chunk-MO7AAXA6.js → chunk-KSJZLFAT.js} +6 -6
  13. package/dist/chunk-KSJZLFAT.js.map +1 -0
  14. package/dist/{chunk-XCNCIG2I.js → chunk-LQ2YAOYR.js} +11 -10
  15. package/dist/chunk-LQ2YAOYR.js.map +1 -0
  16. package/dist/{chunk-ZTPIXL7Q.js → chunk-OH4YSGFK.js} +3 -3
  17. package/dist/chunk-OH4YSGFK.js.map +1 -0
  18. package/dist/{chunk-TCAXRWT6.js → chunk-W3JMJVEK.js} +3 -3
  19. package/dist/chunk-W3JMJVEK.js.map +1 -0
  20. package/dist/{chunk-LWABTQCB.js → chunk-X5UOY4IV.js} +13 -9
  21. package/dist/chunk-X5UOY4IV.js.map +1 -0
  22. package/dist/{chunk-3547J6IN.js → chunk-X7Y35UUL.js} +3 -3
  23. package/dist/{chunk-3547J6IN.js.map → chunk-X7Y35UUL.js.map} +1 -1
  24. package/dist/error-boundary.cjs +1 -1
  25. package/dist/error-boundary.cjs.map +1 -1
  26. package/dist/error-boundary.js +2 -2
  27. package/dist/form.cjs +1 -1
  28. package/dist/form.cjs.map +1 -1
  29. package/dist/form.js +1 -1
  30. package/dist/i18n.cjs +1 -1
  31. package/dist/i18n.cjs.map +1 -1
  32. package/dist/i18n.js +1 -1
  33. package/dist/index.cjs +3456 -861
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +723 -283
  36. package/dist/index.d.ts +723 -283
  37. package/dist/index.js +3432 -846
  38. package/dist/index.js.map +1 -1
  39. package/dist/modal-utils.cjs +9 -8
  40. package/dist/modal-utils.cjs.map +1 -1
  41. package/dist/modal-utils.js +1 -1
  42. package/dist/provider.cjs +4 -4
  43. package/dist/provider.cjs.map +1 -1
  44. package/dist/provider.js +4 -4
  45. package/dist/skeletons.cjs +11 -7
  46. package/dist/skeletons.cjs.map +1 -1
  47. package/dist/skeletons.d.cts +1 -1
  48. package/dist/skeletons.d.ts +1 -1
  49. package/dist/skeletons.js +2 -2
  50. package/dist/skins.cjs +1 -1
  51. package/dist/skins.cjs.map +1 -1
  52. package/dist/skins.js +1 -1
  53. package/dist/theme.cjs +1 -1
  54. package/dist/theme.cjs.map +1 -1
  55. package/dist/theme.js +1 -1
  56. package/package.json +11 -11
  57. package/dist/chunk-AMNHDYFY.js.map +0 -1
  58. package/dist/chunk-IAG64CHL.js.map +0 -1
  59. package/dist/chunk-LWABTQCB.js.map +0 -1
  60. package/dist/chunk-MO7AAXA6.js.map +0 -1
  61. package/dist/chunk-TCAXRWT6.js.map +0 -1
  62. package/dist/chunk-UPC4GN3F.js.map +0 -1
  63. package/dist/chunk-XCNCIG2I.js.map +0 -1
  64. package/dist/chunk-ZTPIXL7Q.js.map +0 -1
package/dist/index.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({
@@ -2013,7 +2013,7 @@ function useTabsNav(nav) {
2013
2013
  }
2014
2014
 
2015
2015
  // src/primitives/dropdown/context.ts
2016
- var DropdownContextKey = /* @__PURE__ */ Symbol("IrisDropdown");
2016
+ var DropdownContextKey = Symbol("IrisDropdown");
2017
2017
 
2018
2018
  // src/primitives/dropdown/Dropdown.ts
2019
2019
  var IrisDropdown = vue.defineComponent({
@@ -3430,7 +3430,7 @@ function useUndoStack(options) {
3430
3430
  }
3431
3431
 
3432
3432
  // src/provider/context.ts
3433
- var PluginStoreContextKey = /* @__PURE__ */ Symbol("IrisPluginStore");
3433
+ var PluginStoreContextKey = Symbol("IrisPluginStore");
3434
3434
 
3435
3435
  // src/provider/IrisProvider.ts
3436
3436
  var DEFAULT_LOCALE = "en-US";
@@ -3691,15 +3691,18 @@ function installButtonStyles() {
3691
3691
  var SIZE_STYLES = {
3692
3692
  sm: {
3693
3693
  padding: "var(--iris-padding-sm) var(--iris-padding-md)",
3694
- fontSize: "var(--iris-font-size-xs, 12px)"
3694
+ fontSize: "var(--iris-font-size-xs, 12px)",
3695
+ minHeight: "var(--iris-control-height-sm, 28px)"
3695
3696
  },
3696
3697
  md: {
3697
3698
  padding: "var(--iris-padding-sm) var(--iris-padding-lg)",
3698
- fontSize: "var(--iris-font-size-md, 14px)"
3699
+ fontSize: "var(--iris-font-size-md, 14px)",
3700
+ minHeight: "var(--iris-control-height-md, 34px)"
3699
3701
  },
3700
3702
  lg: {
3701
3703
  padding: "var(--iris-padding-md) var(--iris-padding-lg)",
3702
- fontSize: "var(--iris-font-size-lg, 16px)"
3704
+ fontSize: "var(--iris-font-size-lg, 16px)",
3705
+ minHeight: "var(--iris-control-height-lg, 40px)"
3703
3706
  }
3704
3707
  };
3705
3708
  var VARIANT_STYLES = {
@@ -3734,6 +3737,7 @@ function buildInlineStyle(variant, size) {
3734
3737
  const base = { ...SIZE_STYLES[size], ...VARIANT_STYLES[variant] };
3735
3738
  if (variant === "link") {
3736
3739
  base.padding = "0";
3740
+ delete base.minHeight;
3737
3741
  }
3738
3742
  return base;
3739
3743
  }
@@ -3835,7 +3839,7 @@ var IrisButton = vue.defineComponent({
3835
3839
  });
3836
3840
 
3837
3841
  // src/primitives/popover/context.ts
3838
- var PopoverContextKey = /* @__PURE__ */ Symbol("IrisPopover");
3842
+ var PopoverContextKey = Symbol("IrisPopover");
3839
3843
 
3840
3844
  // src/primitives/popover/Popover.ts
3841
3845
  var IrisPopover = vue.defineComponent({
@@ -3956,42 +3960,18 @@ var ANIM_DIALOG = "var(--iris-anim-dialog)";
3956
3960
  var ANIM_POPOVER = "var(--iris-anim-popover)";
3957
3961
  var ANIM_TOAST = "var(--iris-anim-toast)";
3958
3962
  var ANIM_TOOLTIP = "var(--iris-anim-tooltip)";
3959
- function installFloatingAnimations() {
3960
- if (installed4 || typeof document === "undefined") return;
3961
- installed4 = true;
3962
- if (document.getElementById(STYLE_ID2)) return;
3963
- const style = document.createElement("style");
3964
- style.id = STYLE_ID2;
3965
- style.textContent = `
3963
+ var ANIMATION_CSS = `
3966
3964
  :root {
3967
3965
  --iris-anim-dialog: iris-dialog-in 150ms ease-out;
3968
3966
  --iris-anim-popover: iris-popover-in 150ms ease-out;
3969
3967
  --iris-anim-toast: iris-toast-in 200ms ease-out;
3970
3968
  --iris-anim-tooltip: iris-tooltip-in 120ms ease-out;
3971
3969
  }
3972
- /*
3973
- * NOTE: these keyframes animate OPACITY ONLY. The floating layers are
3974
- * positioned via inline transform: translate3d(...) (Floating UI), and any
3975
- * transform in the keyframes would override it for the animation duration \u2014
3976
- * flashing the panel at the viewport origin (0,0) before it jumps into place.
3977
- * Fade-only keeps the entrance motion without touching positioning.
3978
- */
3979
- @keyframes iris-dialog-in {
3980
- from { opacity: 0; }
3981
- to { opacity: 1; }
3982
- }
3983
- @keyframes iris-popover-in {
3984
- from { opacity: 0; }
3985
- to { opacity: 1; }
3986
- }
3987
- @keyframes iris-toast-in {
3988
- from { opacity: 0; }
3989
- to { opacity: 1; }
3990
- }
3991
- @keyframes iris-tooltip-in {
3992
- from { opacity: 0; }
3993
- to { opacity: 1; }
3994
- }
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; } }
3995
3975
  @media (prefers-reduced-motion: reduce) {
3996
3976
  :root {
3997
3977
  --iris-anim-dialog: none;
@@ -4000,7 +3980,6 @@ function installFloatingAnimations() {
4000
3980
  --iris-anim-tooltip: none;
4001
3981
  }
4002
3982
  }
4003
- /* P4: global focus-visible ring for interactive controls */
4004
3983
  [data-iris-tabs-trigger]:focus-visible,
4005
3984
  [data-iris-switch]:focus-visible,
4006
3985
  [data-iris-checkbox]:focus-visible,
@@ -4012,6 +3991,13 @@ function installFloatingAnimations() {
4012
3991
  outline-offset: 2px;
4013
3992
  }
4014
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;
4015
4001
  document.head.appendChild(style);
4016
4002
  }
4017
4003
 
@@ -4184,12 +4170,12 @@ var IrisTooltip = vue.defineComponent({
4184
4170
  const triggerListeners = {
4185
4171
  onPointerenter: handleEnter,
4186
4172
  onPointerleave: handleLeave,
4187
- onFocus: (() => {
4173
+ onFocus: () => {
4188
4174
  if (!props.disabled) hi.open();
4189
- }),
4190
- onBlur: (() => {
4175
+ },
4176
+ onBlur: () => {
4191
4177
  if (!props.disabled) hi.close();
4192
- })
4178
+ }
4193
4179
  };
4194
4180
  const captureTriggerRef = (el) => {
4195
4181
  triggerRef.value = el ?? null;
@@ -4258,7 +4244,7 @@ var IrisTooltip = vue.defineComponent({
4258
4244
  });
4259
4245
 
4260
4246
  // src/primitives/dialog/context.ts
4261
- var DialogContextKey = /* @__PURE__ */ Symbol("IrisDialog");
4247
+ var DialogContextKey = Symbol("IrisDialog");
4262
4248
 
4263
4249
  // src/primitives/dialog/Dialog.ts
4264
4250
  var IrisDialog = vue.defineComponent({
@@ -4384,9 +4370,8 @@ function getFocusable(root) {
4384
4370
  (el) => !el.hasAttribute("disabled") && el.tabIndex !== -1
4385
4371
  );
4386
4372
  }
4387
- function useFocusTrap(options) {
4388
- let previouslyFocused = null;
4389
- const onKeyDown = (event) => {
4373
+ function createFocusTrapKeyHandler(options) {
4374
+ return (event) => {
4390
4375
  if (!options.active.value || event.key !== "Tab") return;
4391
4376
  const root = options.container.value;
4392
4377
  if (!root) return;
@@ -4404,13 +4389,15 @@ function useFocusTrap(options) {
4404
4389
  event.preventDefault();
4405
4390
  last.focus();
4406
4391
  }
4407
- } else {
4408
- if (active === last || !root.contains(active)) {
4409
- event.preventDefault();
4410
- first.focus();
4411
- }
4392
+ } else if (active === last || !root.contains(active)) {
4393
+ event.preventDefault();
4394
+ first.focus();
4412
4395
  }
4413
4396
  };
4397
+ }
4398
+ function useFocusTrap(options) {
4399
+ let previouslyFocused = null;
4400
+ const onKeyDown = createFocusTrapKeyHandler(options);
4414
4401
  const activate = () => {
4415
4402
  if (typeof document === "undefined") return;
4416
4403
  previouslyFocused = document.activeElement ?? null;
@@ -4715,7 +4702,7 @@ var IrisDialogClose = vue.defineComponent({
4715
4702
  });
4716
4703
 
4717
4704
  // src/primitives/drawer/context.ts
4718
- var DrawerContextKey = /* @__PURE__ */ Symbol("IrisDrawer");
4705
+ var DrawerContextKey = Symbol("IrisDrawer");
4719
4706
 
4720
4707
  // src/primitives/drawer/Drawer.ts
4721
4708
  var IrisDrawer = vue.defineComponent({
@@ -5099,11 +5086,11 @@ var IrisInput = vue.defineComponent({
5099
5086
  display: "inline-flex",
5100
5087
  alignItems: "center",
5101
5088
  gap: "var(--iris-gap-sm)",
5102
- background: "var(--iris-background)",
5089
+ background: props.readonly ? "var(--iris-surface)" : "var(--iris-background)",
5103
5090
  color: "var(--iris-foreground)",
5104
5091
  border: `1px solid ${borderColor}`,
5105
5092
  borderRadius: "var(--iris-radius-md)",
5106
- cursor: props.disabled ? "not-allowed" : "text",
5093
+ cursor: props.disabled ? "not-allowed" : props.readonly ? "default" : "text",
5107
5094
  opacity: props.disabled ? "0.6" : "1",
5108
5095
  transition: "border-color 120ms ease, box-shadow 120ms ease",
5109
5096
  boxShadow,
@@ -5143,7 +5130,7 @@ var IrisInput = vue.defineComponent({
5143
5130
  ...attrs,
5144
5131
  "data-iris-input": "",
5145
5132
  "data-iris-input-size": props.size,
5146
- "data-state": props.invalid ? "invalid" : focused.value ? "focused" : "idle",
5133
+ "data-state": props.invalid ? "invalid" : focused.value ? "focused" : props.readonly ? "readonly" : "idle",
5147
5134
  style: {
5148
5135
  ...wrapperStyle.value,
5149
5136
  ...attrs.style ?? {}
@@ -6725,7 +6712,7 @@ var IrisCheckbox = vue.defineComponent({
6725
6712
  );
6726
6713
  }
6727
6714
  });
6728
- var RadioGroupKey = /* @__PURE__ */ Symbol("IrisRadioGroup");
6715
+ var RadioGroupKey = Symbol("IrisRadioGroup");
6729
6716
  var IrisRadioGroup = vue.defineComponent({
6730
6717
  name: "IrisRadioGroup",
6731
6718
  props: {
@@ -7099,6 +7086,152 @@ var IrisList = vue.defineComponent({
7099
7086
  );
7100
7087
  }
7101
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
7102
7235
  var IrisSelect = vue.defineComponent({
7103
7236
  name: "IrisSelect",
7104
7237
  inheritAttrs: false,
@@ -7336,138 +7469,6 @@ var IrisSelect = vue.defineComponent({
7336
7469
  })
7337
7470
  ]
7338
7471
  );
7339
- const renderVirtualListbox = () => {
7340
- const list = props.items;
7341
- const optionStyle = (selected, active, disabled) => ({
7342
- display: "flex",
7343
- alignItems: "center",
7344
- gap: "var(--iris-gap-sm)",
7345
- padding: "var(--iris-padding-sm, 6px) var(--iris-padding-md)",
7346
- borderRadius: "var(--iris-radius-sm)",
7347
- cursor: disabled ? "not-allowed" : "pointer",
7348
- opacity: disabled ? "0.5" : "1",
7349
- fontSize: "var(--iris-font-size-md, 14px)",
7350
- background: selected ? "var(--iris-surface-selected, rgba(99, 102, 241, 0.12))" : active ? "var(--iris-surface-hover)" : "transparent",
7351
- color: "var(--iris-foreground)",
7352
- fontWeight: selected ? "600" : "400",
7353
- outline: "none"
7354
- });
7355
- const check = () => vue.h(
7356
- "svg",
7357
- {
7358
- "aria-hidden": "true",
7359
- width: "14",
7360
- height: "14",
7361
- viewBox: "0 0 24 24",
7362
- fill: "none",
7363
- stroke: "var(--iris-primary)",
7364
- "stroke-width": "2.5",
7365
- "stroke-linecap": "round",
7366
- "stroke-linejoin": "round"
7367
- },
7368
- [vue.h("path", { d: "M20 6 9 17l-5-5" })]
7369
- );
7370
- return vue.h(
7371
- "ul",
7372
- {
7373
- role: "listbox",
7374
- "aria-label": t("select.options"),
7375
- "data-iris-list": "",
7376
- ref: (el) => {
7377
- listboxRef.value = el ?? null;
7378
- },
7379
- onKeydown: onListKeyDown,
7380
- onScroll: (e) => {
7381
- v?.setScroll(e.currentTarget.scrollTop);
7382
- },
7383
- style: {
7384
- listStyle: "none",
7385
- margin: "0",
7386
- padding: "var(--iris-padding-sm)",
7387
- display: "flex",
7388
- flexDirection: "column",
7389
- outline: "none",
7390
- maxHeight: "240px",
7391
- overflowY: "auto"
7392
- }
7393
- },
7394
- list.length === 0 ? [
7395
- vue.h(
7396
- "li",
7397
- {
7398
- role: "presentation",
7399
- "data-iris-list-state": "empty",
7400
- "aria-live": "polite",
7401
- style: {
7402
- listStyle: "none",
7403
- padding: "12px",
7404
- textAlign: "center",
7405
- color: "var(--iris-muted)",
7406
- fontSize: "var(--iris-font-size-md, 14px)"
7407
- }
7408
- },
7409
- t("list.empty")
7410
- )
7411
- ] : [
7412
- vue.h("li", {
7413
- role: "presentation",
7414
- "aria-hidden": "true",
7415
- "data-iris-select-spacer": "",
7416
- "data-iris-select-spacer-type": "top",
7417
- style: { height: `${vstate.value.offsetBefore}px` }
7418
- }),
7419
- ...vstate.value.items.map((item) => {
7420
- const opt = list[item.index];
7421
- if (!opt) return null;
7422
- const active = item.index === activeIndex.value;
7423
- const selected = props.multiple ? selectedValues.value.includes(opt.value) : opt.value === currentValue.value;
7424
- return vue.h(
7425
- "li",
7426
- {
7427
- key: String(opt.value ?? item.index),
7428
- role: "option",
7429
- tabindex: active ? 0 : -1,
7430
- "aria-selected": selected ? "true" : "false",
7431
- "aria-disabled": opt.disabled ? "true" : void 0,
7432
- "aria-setsize": list.length,
7433
- "aria-posinset": item.index + 1,
7434
- "data-iris-list-index": item.index,
7435
- "data-iris-list-item": "",
7436
- "data-state": selected ? "selected" : active ? "active" : "idle",
7437
- onClick: opt.disabled ? void 0 : () => {
7438
- activeIndex.value = item.index;
7439
- onSelect(opt);
7440
- },
7441
- onFocus: () => {
7442
- activeIndex.value = item.index;
7443
- },
7444
- onMouseenter: () => {
7445
- activeIndex.value = item.index;
7446
- },
7447
- style: optionStyle(selected, active, !!opt.disabled)
7448
- },
7449
- [
7450
- vue.h(
7451
- "span",
7452
- { style: { flex: "1", minWidth: "0" } },
7453
- opt.label ?? String(opt.value)
7454
- ),
7455
- selected ? check() : null
7456
- ]
7457
- );
7458
- }),
7459
- vue.h("li", {
7460
- role: "presentation",
7461
- "aria-hidden": "true",
7462
- "data-iris-select-spacer": "",
7463
- "data-iris-select-spacer-type": "bottom",
7464
- style: {
7465
- height: `${vstate.value.totalSize - vstate.value.offsetBefore - vstate.value.items.length * ROW_HEIGHT3}px`
7466
- }
7467
- })
7468
- ]
7469
- );
7470
- };
7471
7472
  return () => vue.h(
7472
7473
  IrisPopover,
7473
7474
  {
@@ -7514,7 +7515,20 @@ var IrisSelect = vue.defineComponent({
7514
7515
  style: { padding: "var(--iris-padding-sm)", minWidth: "180px" },
7515
7516
  ...props.teleport !== void 0 ? { teleport: props.teleport } : {}
7516
7517
  },
7517
- () => 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, {
7518
7532
  items: props.items,
7519
7533
  modelValue: currentValue.value,
7520
7534
  onSelect,
@@ -7976,8 +7990,8 @@ var IrisSkeleton = vue.defineComponent({
7976
7990
  vue.onMounted(installSkeletonStyles);
7977
7991
  const style = vue.computed(() => {
7978
7992
  const w = props.width !== void 0 ? toCss(props.width) : defaultWidth(props.shape);
7979
- const h124 = props.height !== void 0 ? toCss(props.height) : defaultHeight(props.shape, props.width);
7980
- 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 };
7981
7995
  });
7982
7996
  return () => vue.h("div", {
7983
7997
  ...attrs,
@@ -9088,7 +9102,7 @@ var IrisChip = vue.defineComponent({
9088
9102
  });
9089
9103
 
9090
9104
  // src/primitives/accordion/context.ts
9091
- var AccordionContextKey = /* @__PURE__ */ Symbol("IrisAccordion");
9105
+ var AccordionContextKey = Symbol("IrisAccordion");
9092
9106
 
9093
9107
  // src/primitives/accordion/Accordion.ts
9094
9108
  var IrisAccordion = vue.defineComponent({
@@ -9326,7 +9340,7 @@ var IrisAccordionItem = vue.defineComponent({
9326
9340
  });
9327
9341
 
9328
9342
  // src/primitives/stepper/context.ts
9329
- var StepperContextKey = /* @__PURE__ */ Symbol("IrisStepper");
9343
+ var StepperContextKey = Symbol("IrisStepper");
9330
9344
 
9331
9345
  // src/primitives/stepper/Stepper.ts
9332
9346
  var IrisStepper = vue.defineComponent({
@@ -9574,7 +9588,7 @@ var IrisStepperStep = vue.defineComponent({
9574
9588
  });
9575
9589
 
9576
9590
  // src/primitives/toggle-group/context.ts
9577
- var ToggleGroupContextKey = /* @__PURE__ */ Symbol("IrisToggleGroup");
9591
+ var ToggleGroupContextKey = Symbol("IrisToggleGroup");
9578
9592
  var SIZE_PADDING = {
9579
9593
  sm: "4px 10px",
9580
9594
  md: "6px 14px",
@@ -10504,35 +10518,287 @@ var tableControlProps = {
10504
10518
  default: void 0
10505
10519
  }
10506
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;
10507
10693
  function getCellValue(row, column) {
10508
10694
  const key = column.dataIndex ?? column.key;
10509
10695
  return row[key];
10510
10696
  }
10511
-
10512
- // src/primitives/table/useTableSort.ts
10513
- function useTableSort(data, options) {
10514
- const sortProp = vue.computed(() => options.sort === void 0 ? void 0 : vue.toValue(options.sort));
10515
- const internalSortValue = vue.ref(options.defaultSort ?? null);
10516
- const sortState = vue.computed({
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));
10767
+ const internalSortValue = vue.ref(options.defaultSort ?? null);
10768
+ const sortState = vue.computed({
10517
10769
  get: () => sortProp.value === void 0 ? internalSortValue.value : sortProp.value ?? null,
10518
10770
  set: (val) => {
10519
10771
  if (sortProp.value === void 0) internalSortValue.value = val;
10520
10772
  options.onSortChange?.(val);
10521
10773
  }
10522
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
+ });
10523
10787
  const sortComparator = vue.computed(() => {
10524
10788
  const s = sortState.value;
10525
10789
  if (!s) return null;
10526
10790
  const col = vue.toValue(options.leafColumns).find((c) => c.key === s.key);
10527
10791
  if (!col) return null;
10528
10792
  const dir = s.direction === "asc" ? 1 : -1;
10529
- const sorter = col.sorter ?? ((a, b) => core.compareValues(getCellValue(a, col), getCellValue(b, col)));
10530
- return (a, b) => sorter(a, b) * dir;
10793
+ return (a, b) => buildSorter(col)(a, b) * dir;
10531
10794
  });
10795
+ const multiSortComparator = vue.computed(
10796
+ () => buildMultiSortComparator(vue.toValue(options.leafColumns), multiSortState.value)
10797
+ );
10532
10798
  const sortedData = vue.computed(() => {
10533
- const compare = sortComparator.value;
10534
- if (!compare) return data.value;
10535
- 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);
10536
10802
  });
10537
10803
  function setSort(next) {
10538
10804
  if (sortProp.value === void 0) internalSortValue.value = next;
@@ -10549,172 +10815,1991 @@ function useTableSort(data, options) {
10549
10815
  setSort({ key: col.key, direction: "desc" });
10550
10816
  return;
10551
10817
  }
10552
- 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
+ }
10553
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
+ };
10554
12414
  return {
10555
- sortState,
10556
- cycleSort,
10557
- setSort,
10558
- sortComparator,
10559
- 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
+ })
10560
12430
  };
10561
12431
  }
10562
-
10563
- // src/primitives/table/Table.ts
10564
- function getCellValue2(row, column) {
10565
- const key = column.dataIndex ?? column.key;
10566
- 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
+ );
10567
12533
  }
10568
- var SELECTION_COL_WIDTH = 40;
10569
- var EXPAND_COL_WIDTH = 40;
10570
- var DEFAULT_COL_WIDTH = 140;
10571
- var DEFAULT_MIN_WIDTH = 60;
10572
- var RESIZE_STEP = 16;
10573
- function resolveInitialWidth(col) {
10574
- if (typeof col.width === "number") return col.width;
10575
- if (typeof col.width === "string") {
10576
- const m = col.width.match(/^(\d+(?:\.\d+)?)px$/);
10577
- if (m) return Number(m[1]);
10578
- }
10579
- 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
+ );
10580
12578
  }
12579
+
12580
+ // src/primitives/table/Table.ts
10581
12581
  var IrisTable = vue.defineComponent({
10582
12582
  name: "IrisTable",
10583
12583
  inheritAttrs: false,
10584
- props: {
10585
- columns: {
10586
- type: Array,
10587
- required: true
10588
- },
10589
- data: {
10590
- type: Array,
10591
- required: false,
10592
- default: void 0
10593
- },
10594
- rowKey: { type: String, default: "id" },
10595
- ...tableControlProps,
10596
- striped: { type: Boolean, default: false },
10597
- editConfig: {
10598
- type: Object,
10599
- default: void 0
10600
- },
10601
- bordered: { type: Boolean, default: true },
10602
- /** Enable per-column resize handles. Combine with `v-model:columnWidths` for persistence. */
10603
- resizableColumns: { type: Boolean, default: false },
10604
- /** Controlled column widths in px. */
10605
- columnWidths: {
10606
- type: Object,
10607
- default: void 0
10608
- },
10609
- /** Enable virtual scrolling for the body. */
10610
- virtualScroll: {
10611
- type: Object,
10612
- default: void 0
10613
- },
10614
- /** Show the loading state instead of rows. */
10615
- loading: { type: Boolean, default: false },
10616
- /** Show the error state instead of rows (takes precedence over loading). */
10617
- error: { type: Boolean, default: false },
10618
- onRetry: { type: Function, default: void 0 },
10619
- /** Render only the horizontally-visible columns (+ pinned + overscan) for wide tables. */
10620
- columnVirtualization: { type: Boolean, default: false },
10621
- /**
10622
- * Enable WAI-ARIA grid keyboard navigation: the table becomes `role="grid"`
10623
- * and Arrow / Home / End / Page Up·Down move a roving cell focus across the
10624
- * data cells. Off by default; opt-in and additive (no effect on mouse / Tab
10625
- * behavior). Pairs best without virtualization (the focused cell must be
10626
- * rendered) and does not hijack keystrokes while a cell is being edited.
10627
- */
10628
- keyboardNavigation: { type: Boolean, default: false },
10629
- /**
10630
- * Enable rectangular cell-range selection (Excel-style). Click starts a
10631
- * range; Shift+Click or Shift+Arrow extends it; Escape clears it.
10632
- * Cells within the range get `data-iris-cell-selected="true"`.
10633
- */
10634
- cellRange: { type: Boolean, default: false },
10635
- /**
10636
- * Render an expandable detail panel beneath a row. Providing this adds a
10637
- * leading expand-toggle column; clicking it reveals a full-width detail row.
10638
- * (Not applied in the virtual-scroll path.)
10639
- */
10640
- renderDetail: {
10641
- type: Function,
10642
- default: void 0
10643
- },
10644
- /** Which rows can expand a detail panel. Defaults to all rows when `renderDetail` is set. */
10645
- rowExpandable: {
10646
- type: Function,
10647
- default: void 0
10648
- },
10649
- /** Initially-expanded row keys (uncontrolled). */
10650
- defaultExpandedRowKeys: {
10651
- type: Array,
10652
- default: void 0
10653
- },
10654
- /**
10655
- * Read a row's child rows to render the table as a TREE. Providing this
10656
- * enables tree mode: `data` is treated as the root rows, each row's first
10657
- * cell gains a depth indent + an expand/collapse toggle (when it has
10658
- * children), and the expand state reuses `defaultExpandedRowKeys` /
10659
- * `expandedRowsChange`. Column sort reorders siblings hierarchically (each
10660
- * level sorted, structure kept), and tree rows virtualize like flat rows
10661
- * when `virtualScroll` is set (unless `renderDetail` is also used, since
10662
- * detail panels are variable-height).
10663
- */
10664
- getSubRows: {
10665
- type: Function,
10666
- default: void 0
10667
- },
10668
- /**
10669
- * Cascade multi-row selection through the complete tree supplied by
10670
- * `getSubRows`. Selecting a branch selects every descendant (including
10671
- * collapsed rows); partially selected branches render indeterminate.
10672
- * Ignored outside tree mode and for non-multi selection.
10673
- */
10674
- treeSelectionCascade: { type: Boolean, default: false }
10675
- },
12584
+ props: tableProps,
10676
12585
  emits: {
10677
12586
  "update:selection": (_value) => true,
10678
12587
  "update:sort": (_value) => true,
12588
+ "update:multiSortState": (_value) => true,
12589
+ multiSortChange: (_value) => true,
10679
12590
  "update:columnWidths": (_value) => true,
12591
+ /** Controlled columnVisibility channel (parent owns the map). */
12592
+ "update:columnVisibility": (_value) => true,
10680
12593
  rowClick: (_row, _index) => true,
10681
12594
  rowDblclick: (_row, _index) => true,
10682
12595
  cellEdit: (_payload) => true,
10683
12596
  expandedRowsChange: (_keys) => true
10684
12597
  },
10685
- setup(props, { slots, attrs, emit }) {
10686
- if (typeof document !== "undefined" && !document.getElementById("iris-table-row-styles")) {
10687
- const style = document.createElement("style");
10688
- style.id = "iris-table-row-styles";
10689
- style.textContent = `
10690
- [data-iris-table] [role="row"]:hover {
10691
- --iris-cell-bg: var(--iris-surface-hover);
10692
- }
10693
- [data-iris-table-row-selected="true"] {
10694
- --iris-cell-bg: var(--iris-surface-selected);
10695
- }
10696
- `;
10697
- document.head.appendChild(style);
10698
- }
12598
+ setup(props, { slots, attrs, emit, expose }) {
12599
+ ensureTableStyles();
10699
12600
  const { t } = useI18n();
10700
- 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
+ );
10701
12657
  const leafColumns = vue.computed(
10702
- () => 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 }
10703
12714
  );
10704
- const headerMatrix = vue.computed(() => grouped.value ? core.buildHeaderMatrix(props.columns) : null);
10705
12715
  const {
10706
12716
  sortState: internalSort,
10707
12717
  cycleSort,
12718
+ setSort,
10708
12719
  sortComparator,
10709
- sortedData: sortedRows
10710
- } = useTableSort(
10711
- vue.computed(() => props.data ?? []),
10712
- {
10713
- leafColumns,
10714
- sort: vue.computed(() => props.sort),
10715
- defaultSort: props.defaultSort,
10716
- 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
+ }
10717
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 }
10718
12803
  );
10719
12804
  const selControlled = vue.computed(() => props.selection !== void 0);
10720
12805
  const selectionModel = core.createSelectionModel({
@@ -10757,18 +12842,56 @@ var IrisTable = vue.defineComponent({
10757
12842
  if (typeof v === "string" || typeof v === "number") return v;
10758
12843
  return index;
10759
12844
  };
10760
- 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
+ };
10761
12861
  const flatTree = vue.computed(
10762
- () => treeMode.value ? core.flattenTree(sortedRows.value, {
12862
+ () => treeMode.value ? core.flattenTree(sortedData.value, {
10763
12863
  getKey: (r) => String(r[props.rowKey]),
10764
12864
  // With an active sort, sort each level's children by the same
10765
- // comparator so the whole tree reorders hierarchically.
10766
- 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,
10767
12868
  isExpanded: (k) => expandedKeys.value.includes(k)
10768
12869
  }) : null
10769
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
+ });
10770
12893
  const bodyData = vue.computed(
10771
- () => flatTree.value ? flatTree.value.map((t2) => t2.row) : sortedRows.value
12894
+ () => flatTree.value ? flatTree.value.map((t2) => t2.row) : filteredData.value
10772
12895
  );
10773
12896
  const cascadingTreeSelection = vue.computed(
10774
12897
  () => props.treeSelectionCascade && props.selectable === "multi" && treeMode.value
@@ -10790,7 +12913,7 @@ var IrisTable = vue.defineComponent({
10790
12913
  if (children && children.length > 0) walk(children, key);
10791
12914
  }
10792
12915
  };
10793
- walk(sortedRows.value);
12916
+ walk(sortedData.value);
10794
12917
  return nodes;
10795
12918
  });
10796
12919
  const compactTreeSelectionSeed = (keys) => {
@@ -10863,25 +12986,27 @@ var IrisTable = vue.defineComponent({
10863
12986
  selectionModel.set([...allRowIds.value]);
10864
12987
  };
10865
12988
  const editingCellId = vue.ref(null);
12989
+ const editingColumnKey = vue.ref(null);
10866
12990
  const editingDraft = vue.ref("");
10867
12991
  const editError = vue.ref(null);
10868
12992
  const editorInputRef = vue.ref(null);
10869
- const cellId = (rowIdent, colKey) => `${rowIdent}::${colKey}`;
10870
12993
  const beginEdit = (row, column, rowIdent) => {
10871
12994
  if (!column.editable) return;
10872
12995
  editingCellId.value = cellId(rowIdent, column.key);
10873
- const current = getCellValue2(row, column);
12996
+ editingColumnKey.value = column.key;
12997
+ const current = getCellValue(row, column);
10874
12998
  editingDraft.value = current == null ? "" : String(current);
10875
12999
  editError.value = null;
10876
13000
  void vue.nextTick(() => editorInputRef.value?.focus());
10877
13001
  };
10878
13002
  const commitEdit = (row, column, rowIndex) => {
10879
13003
  if (editingCellId.value === null) return;
10880
- const oldValue = getCellValue2(row, column);
13004
+ const oldValue = getCellValue(row, column);
10881
13005
  const draft = editingDraft.value;
10882
13006
  const newValue = column.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? oldValue : Number(draft) : draft;
10883
13007
  if (column.editRules && column.editRules.length > 0) {
10884
- 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) => {
10885
13010
  if (!r.valid) {
10886
13011
  editError.value = r.messages[0] ?? null;
10887
13012
  return;
@@ -10904,67 +13029,272 @@ var IrisTable = vue.defineComponent({
10904
13029
  emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
10905
13030
  }
10906
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
+ };
10907
13049
  const finishCommit = (row, column, rowIndex, oldValue, newValue) => {
10908
13050
  editError.value = null;
10909
13051
  editingCellId.value = null;
10910
- if (newValue !== oldValue) {
10911
- 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);
10912
13148
  }
10913
13149
  };
10914
- const cancelEdit = () => {
10915
- editError.value = null;
10916
- 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
+ }
10917
13164
  };
10918
- 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
+ );
10919
13235
  vue.watch(
10920
13236
  () => leafColumns.value,
10921
13237
  (cols) => {
10922
13238
  const seeded = { ...internalWidths.value };
13239
+ let changed = false;
10923
13240
  for (const col of cols) {
10924
- 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
+ }
10925
13245
  }
10926
- internalWidths.value = seeded;
13246
+ if (changed) internalWidths.value = seeded;
10927
13247
  },
10928
13248
  { immediate: true, deep: false }
10929
13249
  );
10930
- const effectiveWidths = vue.computed(() => {
10931
- if (props.columnWidths) return props.columnWidths;
10932
- return internalWidths.value;
10933
- });
10934
13250
  const setColumnWidths = (next) => {
10935
13251
  if (props.columnWidths === void 0) internalWidths.value = next;
10936
13252
  emit("update:columnWidths", next);
10937
13253
  };
10938
13254
  const onHeaderClick = (column) => {
10939
- cycleSort(column);
10940
- };
10941
- const sortIndicator = (col) => {
10942
- if (!col.sortable) return null;
10943
- const state = internalSort.value;
10944
- const isActive = state?.key === col.key;
10945
- const direction = isActive ? state.direction : null;
10946
- 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;
10947
13262
  return vue.h(
10948
13263
  "span",
10949
13264
  {
10950
- "aria-hidden": "true",
13265
+ "data-iris-sort-seq": "",
10951
13266
  style: {
10952
- display: "inline-flex",
10953
- flexDirection: "column",
10954
13267
  marginInlineStart: "var(--iris-space-xxs, 4px)",
10955
- lineHeight: "0.6",
10956
13268
  fontSize: "var(--iris-font-size-xs, 12px)",
10957
- color
13269
+ color: "var(--iris-muted)"
10958
13270
  }
10959
13271
  },
10960
- [
10961
- vue.h("span", { style: { opacity: direction === "asc" ? "1" : "0.45" } }, "\u25B2"),
10962
- vue.h("span", { style: { opacity: direction === "desc" ? "1" : "0.45" } }, "\u25BC")
10963
- ]
13272
+ String(idx + 1)
10964
13273
  );
10965
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);
10966
13294
  const gridTemplate = vue.computed(() => {
10967
13295
  const parts = [];
13296
+ if (props.rowDrag) parts.push(`${DRAG_COL_WIDTH}px`);
13297
+ if (props.seq) parts.push(`${SEQ_COL_WIDTH}px`);
10968
13298
  if (hasDetail.value) parts.push(`${EXPAND_COL_WIDTH}px`);
10969
13299
  if (props.selectable !== "none") parts.push(`${SELECTION_COL_WIDTH}px`);
10970
13300
  for (const col of leafColumns.value) {
@@ -10979,70 +13309,45 @@ var IrisTable = vue.defineComponent({
10979
13309
  cellRangeState.value = s;
10980
13310
  })
10981
13311
  );
10982
- const isInRange = (row, col) => {
10983
- const { anchor, active } = cellRangeState.value;
10984
- if (!anchor || !active) return false;
10985
- const minRow = Math.min(anchor.row, active.row);
10986
- const maxRow = Math.max(anchor.row, active.row);
10987
- const minCol = Math.min(anchor.col, active.col);
10988
- const maxCol = Math.max(anchor.col, active.col);
10989
- return row >= minRow && row <= maxRow && col >= minCol && col <= maxCol;
10990
- };
10991
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);
10992
13335
  const focusedCell = vue.ref(null);
10993
- const GRID_NAV_KEYS = /* @__PURE__ */ new Set([
10994
- "ArrowUp",
10995
- "ArrowDown",
10996
- "ArrowLeft",
10997
- "ArrowRight",
10998
- "Home",
10999
- "End",
11000
- "PageUp",
11001
- "PageDown"
11002
- ]);
11003
- const handleGridKey = (e) => {
11004
- if (!props.keyboardNavigation || !GRID_NAV_KEYS.has(e.key)) return;
11005
- const target = e.target;
11006
- if (target.dataset.gridRow === void 0) return;
11007
- e.preventDefault();
11008
- const current = focusedCell.value ?? { row: 0, col: 0 };
11009
- const next = core.nextGridCell(current, e.key, {
11010
- rowCount: bodyData.value.length,
11011
- colCount: leafColumns.value.length,
11012
- pageSize: 10
11013
- });
11014
- focusedCell.value = next;
11015
- const cell = rootRef.value?.querySelector(
11016
- `[data-grid-row="${next.row}"][data-grid-col="${next.col}"]`
11017
- );
11018
- cell?.focus();
11019
- };
11020
- const CELL_RANGE_ARROW_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
11021
- const handleCellRangeKey = (e) => {
11022
- if (!props.cellRange) return;
11023
- if (e.key === "Escape") {
11024
- cellRangeCtrl.clearRange();
11025
- return;
11026
- }
11027
- if (!e.shiftKey || !CELL_RANGE_ARROW_KEYS.has(e.key)) return;
11028
- const target = e.target;
11029
- const rowAttr = target.dataset.irisCellRow;
11030
- const colAttr = target.dataset.irisCellCol;
11031
- if (rowAttr === void 0 || colAttr === void 0) return;
11032
- e.preventDefault();
11033
- const anchor = cellRangeCtrl.getState().anchor;
11034
- const active = anchor ? cellRangeCtrl.getState().active ?? { row: Number(rowAttr), col: Number(colAttr) } : { row: Number(rowAttr), col: Number(colAttr) };
11035
- let nextRow = active.row;
11036
- let nextCol = active.col;
11037
- if (e.key === "ArrowUp") nextRow = Math.max(0, nextRow - 1);
11038
- else if (e.key === "ArrowDown") nextRow = Math.min(bodyData.value.length - 1, nextRow + 1);
11039
- else if (e.key === "ArrowLeft") nextCol = Math.max(0, nextCol - 1);
11040
- else nextCol = Math.min(leafColumns.value.length - 1, nextCol + 1);
11041
- cellRangeCtrl.extendRange(nextRow, nextCol);
11042
- };
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;
11043
13348
  const scrollLeft = vue.ref(0);
11044
13349
  const viewportWidth = vue.ref(0);
11045
- const colTrack = (i) => (hasDetail.value ? 1 : 0) + (props.selectable !== "none" ? 2 : 1) + i;
13350
+ const colTrack = (i) => leadTrackCount() + 1 + i;
11046
13351
  if (typeof ResizeObserver !== "undefined") {
11047
13352
  let ro = null;
11048
13353
  vue.onMounted(() => {
@@ -11059,27 +13364,19 @@ var IrisTable = vue.defineComponent({
11059
13364
  ro = null;
11060
13365
  });
11061
13366
  }
11062
- const visibleColSet = vue.computed(() => {
11063
- if (!props.columnVirtualization) return null;
11064
- const cols = leafColumns.value;
11065
- const w = core.computeVirtualRange({
11066
- itemCount: cols.length,
11067
- scrollTop: scrollLeft.value,
11068
- viewportSize: viewportWidth.value,
11069
- itemSize: (i) => effectiveWidths.value[cols[i].key] ?? resolveInitialWidth(cols[i]),
11070
- buffer: 2
11071
- });
11072
- const set = /* @__PURE__ */ new Set();
11073
- for (let i = w.startIndex; i <= w.endIndex; i += 1) set.add(i);
11074
- cols.forEach((col, i) => {
11075
- if (col.pinned) set.add(i);
11076
- });
11077
- return set;
11078
- });
13367
+ const visibleColSet = vue.computed(
13368
+ () => computeVisibleColSet(
13369
+ props.columnVirtualization,
13370
+ leafColumns.value,
13371
+ scrollLeft.value,
13372
+ viewportWidth.value,
13373
+ effectiveWidths.value
13374
+ )
13375
+ );
11079
13376
  const pinnedOffsets = vue.computed(() => {
11080
13377
  const map = {};
11081
13378
  const widthOf = (col) => effectiveWidths.value[col.key] ?? resolveInitialWidth(col);
11082
- 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);
11083
13380
  for (const col of leafColumns.value) {
11084
13381
  if (col.pinned === "left") {
11085
13382
  map[col.key] = { side: "left", offset: left };
@@ -11135,126 +13432,449 @@ var IrisTable = vue.defineComponent({
11135
13432
  }
11136
13433
  });
11137
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
+ });
11138
13746
  return () => {
11139
13747
  const showSelection = props.selectable !== "none";
11140
13748
  const showDetail = hasDetail.value;
11141
- const buildGroupedHeader = (matrix) => {
11142
- 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 = () => {
11143
13753
  const cells = [];
11144
- if (showDetail) {
13754
+ if (showDrag) {
11145
13755
  cells.push(
11146
13756
  vue.h("div", {
11147
- key: "__expand__",
11148
13757
  role: "columnheader",
11149
- 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
+ }
11150
13768
  })
11151
13769
  );
11152
13770
  }
11153
- if (showSelection) {
13771
+ if (showSeq) {
11154
13772
  cells.push(
11155
- vue.h(
11156
- "div",
11157
- {
11158
- key: "__select__",
11159
- role: "columnheader",
11160
- "data-iris-table-header": "",
11161
- style: {
11162
- gridColumn: showDetail ? "2" : "1",
11163
- gridRow: "1 / -1",
11164
- display: "flex",
11165
- alignItems: "center",
11166
- justifyContent: "center",
11167
- padding: "8px 12px",
11168
- background: "var(--iris-surface)",
11169
- borderBottom: "1px solid var(--iris-border)"
11170
- }
11171
- },
11172
- props.selectable === "multi" ? [
11173
- [
11174
- vue.h(IrisCheckbox, {
11175
- modelValue: allSelected.value ? true : someSelected.value ? "indeterminate" : false,
11176
- size: "sm",
11177
- ariaLabel: t("table.selectAll"),
11178
- "onUpdate:modelValue": toggleAll
11179
- }),
11180
- props.selection && props.selection.length > 0 ? vue.h(
11181
- "span",
11182
- {
11183
- "data-iris-table-selected-count": "",
11184
- style: {
11185
- marginInlineStart: "var(--iris-space-xs, 8px)",
11186
- fontSize: "var(--iris-font-size-sm, 13px)",
11187
- color: "var(--iris-muted)",
11188
- whiteSpace: "nowrap"
11189
- }
11190
- },
11191
- t("table.selectedCount", { count: String(props.selection.length) })
11192
- ) : null
11193
- ]
11194
- ] : ""
11195
- )
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
+ })
11196
13786
  );
11197
13787
  }
11198
- for (const rowCells of matrix) {
11199
- for (const cell of rowCells) {
11200
- const col = cell.column;
11201
- const isLeaf = !col.children || col.children.length === 0;
11202
- const sortable = isLeaf && col.sortable;
11203
- const align = col.align ?? "left";
11204
- const headerSlot = slots[`header.${col.key}`];
11205
- const title = headerSlot?.({ column: col }) ?? col.title;
11206
- cells.push(
11207
- vue.h(
11208
- "div",
11209
- {
11210
- key: `${col.key}-${cell.level}`,
11211
- role: "columnheader",
11212
- "data-iris-table-header": col.key,
11213
- "data-iris-table-header-group": isLeaf ? void 0 : "",
11214
- "aria-colspan": cell.colSpan,
11215
- onClick: sortable ? () => onHeaderClick(col) : void 0,
11216
- "aria-sort": sortable ? internalSort.value?.key === col.key ? internalSort.value.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
11217
- style: {
11218
- gridColumn: `${lead + cell.colStart} / span ${cell.colSpan}`,
11219
- gridRow: `${cell.level + 1} / span ${cell.rowSpan}`,
11220
- display: "flex",
11221
- alignItems: "center",
11222
- justifyContent: isLeaf ? align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start" : "center",
11223
- padding: "8px var(--iris-padding-md)",
11224
- cursor: sortable ? "pointer" : "default",
11225
- userSelect: sortable ? "none" : "auto",
11226
- background: "var(--iris-surface)",
11227
- borderBottom: "1px solid var(--iris-border)",
11228
- fontWeight: "600",
11229
- fontSize: "var(--iris-font-size-md, 14px)",
11230
- color: "var(--iris-foreground)",
11231
- whiteSpace: "nowrap",
11232
- overflow: "hidden",
11233
- textOverflow: "ellipsis"
11234
- }
11235
- },
11236
- [title, sortable ? sortIndicator(col) : null]
11237
- )
11238
- );
11239
- }
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
+ );
11240
13806
  }
11241
- return vue.h(
11242
- "div",
11243
- {
11244
- role: "row",
11245
- "data-iris-table-row": "header",
11246
- "data-iris-table-header-row": "",
11247
- "data-iris-table-header-grouped": "",
11248
- style: {
11249
- display: "grid",
11250
- gridTemplateColumns: gridTemplate.value,
11251
- gridTemplateRows: `repeat(${matrix.length}, auto)`
11252
- }
11253
- },
11254
- cells
11255
- );
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;
11256
13853
  };
11257
- 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();
11258
13878
  if (showDetail) {
11259
13879
  headerCells.push(
11260
13880
  vue.h("div", {
@@ -11299,14 +13919,15 @@ var IrisTable = vue.defineComponent({
11299
13919
  )
11300
13920
  );
11301
13921
  }
11302
- for (let ci = 0; ci < props.columns.length; ci += 1) {
11303
- const col = props.columns[ci];
13922
+ for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
13923
+ const col = leafColumns.value[ci];
11304
13924
  if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
11305
13925
  const align = col.align ?? "left";
11306
13926
  const headerSlot = slots[`header.${col.key}`];
11307
13927
  const title = headerSlot?.({ column: col }) ?? col.title;
11308
13928
  wireResize(col);
11309
- const handle = props.resizableColumns ? vue.h("span", {
13929
+ const pinnedHandle = pinnedDragHandle(col);
13930
+ const handle = props.resizableColumns && !pinnedHandle ? vue.h("span", {
11310
13931
  ref: (el) => {
11311
13932
  getHandleRef(col.key).value = el ?? null;
11312
13933
  },
@@ -11350,6 +13971,13 @@ var IrisTable = vue.defineComponent({
11350
13971
  role: "columnheader",
11351
13972
  "data-iris-table-header": col.key,
11352
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,
11353
13981
  onClick: () => onHeaderClick(col),
11354
13982
  style: {
11355
13983
  position: "relative",
@@ -11370,9 +13998,16 @@ var IrisTable = vue.defineComponent({
11370
13998
  ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
11371
13999
  ...col.pinned ? { ...pinnedStyle(col.key), background: "var(--iris-surface)" } : {}
11372
14000
  },
11373
- "aria-sort": internalSort.value?.key === col.key ? internalSort.value.direction === "asc" ? "ascending" : "descending" : col.sortable ? "none" : void 0
14001
+ "aria-sort": ariaSortFor(col)
11374
14002
  },
11375
- [title, sortIndicator(col), handle]
14003
+ [
14004
+ title,
14005
+ sortIndicator(col),
14006
+ multiSortSeq(col),
14007
+ renderFilterTrigger(col, true),
14008
+ pinnedHandle,
14009
+ handle
14010
+ ]
11376
14011
  )
11377
14012
  );
11378
14013
  }
@@ -11392,6 +14027,69 @@ var IrisTable = vue.defineComponent({
11392
14027
  const id = rowId(row, index);
11393
14028
  const selected = isSelected(id);
11394
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
+ }
11395
14093
  if (showDetail) {
11396
14094
  const rowExpandable = isRowExpandable(row, index);
11397
14095
  const isExpanded = expandedKeys.value.includes(String(id));
@@ -11468,115 +14166,30 @@ var IrisTable = vue.defineComponent({
11468
14166
  for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
11469
14167
  const col = leafColumns.value[ci];
11470
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;
11471
14173
  const align = col.align ?? "left";
11472
14174
  const cellSlot = slots[`cell.${col.key}`];
11473
- 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;
11474
14178
  let content;
11475
14179
  if (isEditing) {
11476
14180
  const editCellId = cellId(id, col.key);
11477
- const error = editError.value;
11478
- const input = vue.h("input", {
11479
- ref: (el) => {
11480
- editorInputRef.value = el ?? null;
11481
- },
11482
- type: col.editor === "number" ? "number" : "text",
11483
- value: editingDraft.value,
11484
- "data-iris-table-editor": "",
11485
- "aria-invalid": error ? "true" : void 0,
11486
- "aria-describedby": error ? `${editCellId}-error` : void 0,
11487
- onInput: (e) => {
11488
- editingDraft.value = e.target.value;
11489
- },
11490
- onKeydown: (e) => {
11491
- if (e.key === "Enter") {
11492
- e.preventDefault();
11493
- commitEdit(row, col, index);
11494
- } else if (e.key === "Escape") {
11495
- e.preventDefault();
11496
- cancelEdit();
11497
- }
11498
- },
11499
- onBlur: () => commitEdit(row, col, index),
11500
- onClick: (e) => e.stopPropagation(),
11501
- onDblclick: (e) => e.stopPropagation(),
11502
- style: {
11503
- width: "100%",
11504
- border: `1px solid ${error ? "var(--iris-danger)" : "var(--iris-primary)"}`,
11505
- borderRadius: "var(--iris-radius-sm)",
11506
- padding: "var(--iris-space-xxs, 4px) var(--iris-padding-sm, 6px)",
11507
- font: "inherit",
11508
- background: "var(--iris-background)",
11509
- color: "var(--iris-foreground)",
11510
- outline: "none",
11511
- boxShadow: "0 0 0 3px color-mix(in srgb, var(--iris-primary) 18%, transparent)"
11512
- }
11513
- });
11514
- content = error ? [
11515
- input,
11516
- vue.h(
11517
- "div",
11518
- {
11519
- id: `${editCellId}-error`,
11520
- role: "alert",
11521
- "data-iris-table-editor-error": "",
11522
- style: {
11523
- marginTop: "var(--iris-space-xxs, 4px)",
11524
- fontSize: "var(--iris-font-size-xs, 12px)",
11525
- color: "var(--iris-danger)"
11526
- }
11527
- },
11528
- error
11529
- )
11530
- ] : 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);
11531
14189
  } else {
11532
- 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);
11533
14191
  }
11534
- const treeIndent = treeMeta && ci === 0 ? vue.h(
11535
- "span",
11536
- {
11537
- "data-iris-table-tree-indent": "",
11538
- style: {
11539
- display: "inline-flex",
11540
- alignItems: "center",
11541
- flex: "none",
11542
- paddingLeft: `${treeMeta.depth * 16}px`
11543
- }
11544
- },
11545
- treeMeta.hasChildren ? [
11546
- vue.h(
11547
- "button",
11548
- {
11549
- type: "button",
11550
- "data-iris-table-tree-toggle": "",
11551
- "aria-expanded": treeMeta.expanded ? "true" : "false",
11552
- "aria-label": t(
11553
- treeMeta.expanded ? "treeSelect.collapse" : "treeSelect.expand"
11554
- ),
11555
- onClick: (e) => {
11556
- e.stopPropagation();
11557
- expansion.toggle(treeMeta.key);
11558
- },
11559
- style: {
11560
- border: "none",
11561
- background: "transparent",
11562
- cursor: "pointer",
11563
- padding: "0",
11564
- marginRight: "4px",
11565
- font: "inherit",
11566
- color: "var(--iris-foreground)",
11567
- transform: treeMeta.expanded ? "rotate(90deg)" : "none",
11568
- transition: "transform 150ms"
11569
- }
11570
- },
11571
- "\u25B6"
11572
- )
11573
- ] : [
11574
- vue.h("span", {
11575
- "aria-hidden": "true",
11576
- style: { display: "inline-block", width: "16px" }
11577
- })
11578
- ]
11579
- ) : null;
14192
+ const treeIndent = treeMeta && ci === 0 ? buildTreeIndent(treeMeta, row) : null;
11580
14193
  const cellChildren = treeIndent ? [treeIndent, ...Array.isArray(content) ? content : [content]] : content;
11581
14194
  cells.push(
11582
14195
  vue.h(
@@ -11588,6 +14201,7 @@ var IrisTable = vue.defineComponent({
11588
14201
  "data-iris-table-pinned": col.pinned,
11589
14202
  "data-editable": col.editable ? "" : void 0,
11590
14203
  "data-editing": isEditing ? "" : void 0,
14204
+ "data-iris-input-hint": patternHint ? "true" : void 0,
11591
14205
  // Grid keyboard navigation (opt-in): grid coords + roving tabindex
11592
14206
  // (exactly one cell is 0) + an onFocus that syncs the focused cell.
11593
14207
  ...props.keyboardNavigation ? {
@@ -11611,14 +14225,19 @@ var IrisTable = vue.defineComponent({
11611
14225
  }
11612
14226
  }
11613
14227
  } : {},
11614
- onDblclick: col.editable ? () => beginEdit(row, col, id) : void 0,
11615
- 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,
11616
14234
  style: {
11617
14235
  display: "flex",
11618
14236
  alignItems: "center",
11619
14237
  justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
11620
14238
  background: "var(--iris-cell-bg, transparent)",
11621
14239
  padding: isEditing ? "var(--iris-space-xxs, 4px)" : "var(--iris-space-xs, 8px) var(--iris-padding-md)",
14240
+ ...isEditing ? { flexWrap: "wrap" } : {},
11622
14241
  borderBottom: "1px solid var(--iris-border)",
11623
14242
  fontSize: "var(--iris-font-size-md, 14px)",
11624
14243
  whiteSpace: "nowrap",
@@ -11627,6 +14246,11 @@ var IrisTable = vue.defineComponent({
11627
14246
  cursor: col.editable ? "cell" : "default",
11628
14247
  ...props.cellRange && isInRange(index, ci) ? { background: "var(--iris-surface-selected, rgba(99,102,241,0.12))" } : {},
11629
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
+ } : {},
11630
14254
  ...pinnedStyle(col.key)
11631
14255
  }
11632
14256
  },
@@ -11648,6 +14272,8 @@ var IrisTable = vue.defineComponent({
11648
14272
  "aria-setsize": treeMeta ? treeMeta.setSize : void 0,
11649
14273
  "aria-posinset": treeMeta ? treeMeta.posInset : void 0,
11650
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,
11651
14277
  "data-state": selected ? "selected" : void 0,
11652
14278
  onClick: () => emit("rowClick", row, index),
11653
14279
  onDblclick: () => emit("rowDblclick", row, index),
@@ -11668,50 +14294,21 @@ var IrisTable = vue.defineComponent({
11668
14294
  textAlign: "center",
11669
14295
  color: "var(--iris-muted)"
11670
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
+ });
11671
14309
  let bodyNode;
11672
- if (props.error) {
11673
- bodyNode = vue.h("div", { role: "row", "data-iris-table-row": "error", style: stateRowStyle }, [
11674
- vue.h(
11675
- "span",
11676
- { style: { marginInlineEnd: props.onRetry ? "var(--iris-space-sm, 12px)" : "0px" } },
11677
- slots.error ? slots.error() : t("table.error")
11678
- ),
11679
- props.onRetry ? vue.h(
11680
- "button",
11681
- {
11682
- type: "button",
11683
- "data-iris-table-retry": "",
11684
- onClick: props.onRetry,
11685
- style: {
11686
- border: "1px solid var(--iris-border)",
11687
- background: "var(--iris-surface)",
11688
- color: "var(--iris-foreground)",
11689
- borderRadius: "var(--iris-radius-sm, 4px)",
11690
- padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
11691
- fontSize: "var(--iris-font-size-sm, 13px)",
11692
- cursor: "pointer"
11693
- }
11694
- },
11695
- t("table.retry")
11696
- ) : null
11697
- ]);
11698
- } else if (props.loading) {
11699
- bodyNode = vue.h(
11700
- "div",
11701
- {
11702
- role: "row",
11703
- "aria-busy": "true",
11704
- "data-iris-table-row": "loading",
11705
- style: stateRowStyle
11706
- },
11707
- slots.loading ? slots.loading() : t("table.loading")
11708
- );
11709
- } else if (bodyData.value.length === 0) {
11710
- bodyNode = vue.h(
11711
- "div",
11712
- { role: "row", "data-iris-table-row": "empty", style: stateRowStyle },
11713
- slots.empty ? slots.empty() : t("table.empty")
11714
- );
14310
+ if (stateNode) {
14311
+ bodyNode = stateNode;
11715
14312
  } else if (props.virtualScroll && (!treeMode.value || !hasDetail.value)) {
11716
14313
  bodyNode = vue.h(
11717
14314
  IrisVirtualScroll,
@@ -11775,127 +14372,105 @@ var IrisTable = vue.defineComponent({
11775
14372
  bodyChildren
11776
14373
  );
11777
14374
  }
11778
- let summaryRow = null;
11779
- if (!props.error && !props.loading && bodyData.value.length > 0 && leafColumns.value.some((c) => c.summary)) {
11780
- const summaryCells = [];
11781
- if (showSelection) {
11782
- summaryCells.push(
11783
- vue.h("div", {
11784
- key: "__selection",
11785
- role: "cell",
11786
- "data-iris-table-cell": "__selection",
11787
- style: {
11788
- display: "flex",
11789
- alignItems: "center",
11790
- justifyContent: "center",
11791
- padding: "8px 12px"
11792
- }
11793
- })
11794
- );
11795
- }
11796
- for (let ci = 0; ci < leafColumns.value.length; ci += 1) {
11797
- const col = leafColumns.value[ci];
11798
- if (visibleColSet.value && !visibleColSet.value.has(ci)) continue;
11799
- const align = col.align ?? "left";
11800
- const op = col.summary;
11801
- const value = op ? core.aggregate(bodyData.value, (r) => getCellValue2(r, col), op) : null;
11802
- const summaryContent = op != null && value != null ? col.renderSummary ? col.renderSummary(value, bodyData.value) : String(value) : "";
11803
- summaryCells.push(
11804
- vue.h(
11805
- "div",
11806
- {
11807
- key: col.key,
11808
- role: "cell",
11809
- "data-iris-table-cell": col.key,
11810
- "data-iris-table-summary-cell": op ? "" : void 0,
11811
- "data-iris-table-pinned": col.pinned,
11812
- style: {
11813
- display: "flex",
11814
- alignItems: "center",
11815
- justifyContent: align === "right" ? "flex-end" : align === "center" ? "center" : "flex-start",
11816
- padding: "var(--iris-space-xs, 8px) var(--iris-padding-md)",
11817
- fontSize: "var(--iris-font-size-md, 14px)",
11818
- whiteSpace: "nowrap",
11819
- overflow: "hidden",
11820
- textOverflow: "ellipsis",
11821
- ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
11822
- ...pinnedStyle(col.key)
11823
- }
11824
- },
11825
- summaryContent
11826
- )
11827
- );
11828
- }
11829
- 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(
11830
14402
  "div",
11831
14403
  {
11832
- role: "row",
11833
- "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,
11834
14432
  style: {
11835
- display: "grid",
11836
- gridTemplateColumns: gridTemplate.value,
11837
- fontWeight: "600",
11838
- borderTop: "2px solid var(--iris-border)",
11839
- 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 ?? {}
11840
14443
  }
11841
14444
  },
11842
- summaryCells
11843
- );
11844
- }
11845
- return vue.h(
11846
- "div",
11847
- {
11848
- ...attrs,
11849
- ref: (el) => {
11850
- 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
+ }
11851
14464
  },
11852
- // A keyboard-navigable hierarchical table is a `treegrid`; otherwise the
11853
- // grid/table role as before (treegrid implies managed cell focus).
11854
- role: props.keyboardNavigation ? treeMode.value ? "treegrid" : "grid" : "table",
11855
- "data-iris-table": "",
11856
- "data-virtual": props.virtualScroll ? "" : void 0,
11857
- "data-column-virtualized": props.columnVirtualization ? "true" : void 0,
11858
- onKeydown: props.keyboardNavigation || props.cellRange ? (e) => {
11859
- if (props.keyboardNavigation) handleGridKey(e);
11860
- if (props.cellRange) handleCellRangeKey(e);
11861
- } : void 0,
11862
- onScroll: props.columnVirtualization ? (e) => {
11863
- scrollLeft.value = e.currentTarget.scrollLeft;
11864
- } : void 0,
11865
- style: {
11866
- background: "var(--iris-background)",
11867
- color: "var(--iris-foreground)",
11868
- fontSize: "var(--iris-font-size-md, 14px)",
11869
- border: props.bordered ? "1px solid var(--iris-border)" : "none",
11870
- borderRadius: "var(--iris-radius-md)",
11871
- // Column virtualization turns the table into a horizontal scroll container.
11872
- overflow: props.columnVirtualization ? "auto" : "hidden",
11873
- ...attrs.style ?? {}
11874
- }
11875
- },
11876
- [headerRow, bodyNode, summaryRow]
11877
- );
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;
11878
14471
  };
11879
14472
  }
11880
14473
  });
11881
- function exportCsv(rows, columns) {
11882
- return core.toCsv(
11883
- rows,
11884
- columns.map((column) => ({
11885
- key: column.key,
11886
- title: column.title,
11887
- dataIndex: typeof column.dataIndex === "string" ? column.dataIndex : void 0
11888
- }))
11889
- );
11890
- }
11891
- async function downloadCsv(filename, csv) {
11892
- const BOM = String.fromCharCode(65279);
11893
- await core.downloadFile({
11894
- filename,
11895
- content: BOM + csv,
11896
- mimeType: "text/csv;charset=utf-8;"
11897
- });
11898
- }
11899
14474
  function exportExcel(rows, columns, options) {
11900
14475
  return core.toSpreadsheetXml(
11901
14476
  rows,
@@ -11916,7 +14491,7 @@ async function downloadExcel(filename, xml) {
11916
14491
  }
11917
14492
 
11918
14493
  // src/primitives/tabs/context.ts
11919
- var TabsContextKey = /* @__PURE__ */ Symbol("IrisTabs");
14494
+ var TabsContextKey = Symbol("IrisTabs");
11920
14495
 
11921
14496
  // src/primitives/tabs/Tabs.ts
11922
14497
  var IrisTabs = vue.defineComponent({
@@ -12216,7 +14791,7 @@ var IrisTabsContent = vue.defineComponent({
12216
14791
  });
12217
14792
 
12218
14793
  // src/primitives/menu/context.ts
12219
- var MenuContextKey = /* @__PURE__ */ Symbol("IrisMenu");
14794
+ var MenuContextKey = Symbol("IrisMenu");
12220
14795
 
12221
14796
  // src/primitives/menu/Menu.ts
12222
14797
  var IrisMenu = vue.defineComponent({
@@ -13063,6 +15638,29 @@ var TREE_STATE_STYLE = {
13063
15638
  color: "var(--iris-muted)",
13064
15639
  fontSize: "var(--iris-font-size-md, 14px)"
13065
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
13066
15664
  var IrisTree = vue.defineComponent({
13067
15665
  name: "IrisTree",
13068
15666
  inheritAttrs: false,
@@ -13292,21 +15890,6 @@ var IrisTree = vue.defineComponent({
13292
15890
  break;
13293
15891
  }
13294
15892
  }
13295
- const renderStateNode = () => {
13296
- 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");
13297
- return vue.h(
13298
- "div",
13299
- {
13300
- key: stateKey.value,
13301
- role: "presentation",
13302
- "data-iris-tree-state": state.value,
13303
- "aria-live": "polite",
13304
- ...stateProps.value,
13305
- style: TREE_STATE_STYLE
13306
- },
13307
- content
13308
- );
13309
- };
13310
15893
  const renderItems = () => {
13311
15894
  void checkedKeys.value;
13312
15895
  return flat.value.map((entry) => {
@@ -13439,7 +16022,15 @@ var IrisTree = vue.defineComponent({
13439
16022
  ...attrs.style ?? {}
13440
16023
  }
13441
16024
  },
13442
- 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
+ ]
13443
16034
  );
13444
16035
  }
13445
16036
  });
@@ -18672,12 +21263,12 @@ function defaultFilter(query, item) {
18672
21263
  if (!q) return 0;
18673
21264
  const haystacks = [item.label, ...item.keywords ?? []].map((s) => s.toLowerCase());
18674
21265
  let best = null;
18675
- for (const h124 of haystacks) {
21266
+ for (const h136 of haystacks) {
18676
21267
  let qi = 0;
18677
21268
  let lastIdx = -1;
18678
21269
  let score = 0;
18679
- for (let i = 0; i < h124.length && qi < q.length; i += 1) {
18680
- if (h124[i] === q[qi]) {
21270
+ for (let i = 0; i < h136.length && qi < q.length; i += 1) {
21271
+ if (h136[i] === q[qi]) {
18681
21272
  score += lastIdx === -1 ? i : i - lastIdx - 1;
18682
21273
  lastIdx = i;
18683
21274
  qi += 1;
@@ -19043,8 +21634,8 @@ var IrisTimePicker = vue.defineComponent({
19043
21634
  if (props.format === "24h") {
19044
21635
  return { h: value.value.hours, m: value.value.minutes };
19045
21636
  }
19046
- const h124 = value.value.hours % 12 === 0 ? 12 : value.value.hours % 12;
19047
- 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 };
19048
21639
  });
19049
21640
  const emitNew = (next) => {
19050
21641
  emit("update:modelValue", next);
@@ -19060,16 +21651,16 @@ var IrisTimePicker = vue.defineComponent({
19060
21651
  };
19061
21652
  const toggleMeridiem = () => {
19062
21653
  const isPM = meridiem.value === "PM";
19063
- const h124 = value.value.hours % 12;
19064
- const newH24 = isPM ? h124 : h124 + 12;
21654
+ const h1210 = value.value.hours % 12;
21655
+ const newH24 = isPM ? h1210 : h1210 + 12;
19065
21656
  emitNew({ hours: newH24, minutes: value.value.minutes });
19066
21657
  };
19067
21658
  const onHoursInput = (e) => {
19068
21659
  const v = parseInt(e.target.value || "0", 10);
19069
21660
  if (Number.isNaN(v)) return;
19070
21661
  if (props.format === "12h") {
19071
- const h124 = clamp5(v, 1, 12);
19072
- const wrap = h124 === 12 ? 0 : h124;
21662
+ const h1210 = clamp5(v, 1, 12);
21663
+ const wrap = h1210 === 12 ? 0 : h1210;
19073
21664
  const newH24 = meridiem.value === "PM" ? wrap + 12 : wrap;
19074
21665
  setHours24(newH24);
19075
21666
  } else {
@@ -20590,6 +23181,10 @@ Object.defineProperty(exports, "nodeAllowsRoles", {
20590
23181
  enumerable: true,
20591
23182
  get: function () { return core.nodeAllowsRoles; }
20592
23183
  });
23184
+ Object.defineProperty(exports, "parseCsv", {
23185
+ enumerable: true,
23186
+ get: function () { return core.parseCsv; }
23187
+ });
20593
23188
  Object.defineProperty(exports, "rgbToHex", {
20594
23189
  enumerable: true,
20595
23190
  get: function () { return core.rgbToHex; }