@iris-ui-kit/vue 0.2.36 → 0.2.38

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.
package/dist/index.js CHANGED
@@ -4,36 +4,38 @@ import { useBodyScrollLock, useFocusTrap } from './chunk-LQ2YAOYR.js';
4
4
  export { __getBodyScrollLockCount, __resetBodyScrollLock, useBodyScrollLock, useFocusTrap } from './chunk-LQ2YAOYR.js';
5
5
  export { IrisGrid } from './chunk-IN3DQ55U.js';
6
6
  import './chunk-4FQ7XF52.js';
7
- import { useDataState } from './chunk-S43IHHBU.js';
8
- export { DATA_STATE_CLASS, __resetDataStateStyles, installDataStateStyles, useDataState, usePrefersReducedMotion } from './chunk-S43IHHBU.js';
7
+ import { useDataState, usePrefersReducedMotion } from './chunk-U635XRYU.js';
8
+ export { DATA_STATE_CLASS, __resetDataStateStyles, installDataStateStyles, useDataState, usePrefersReducedMotion } from './chunk-U635XRYU.js';
9
9
  export { IrisProvider, PluginStoreContextKey, usePlugin, usePluginStore } from './chunk-MO7AAXA6.js';
10
10
  export { createClientFetcher, createResourceController, useResourceController } from './chunk-25NYWSXP.js';
11
11
  import { IrisCheckbox, IrisFormField, IrisInput, IrisButton } from './chunk-W64OMUBD.js';
12
12
  export { IrisAlert, IrisButton, IrisCheckbox, IrisDashboardTemplate, IrisDivider, IrisFormField, IrisInput, IrisLoginTemplate, IrisPasswordInput } from './chunk-W64OMUBD.js';
13
13
  export { IrisContainer, IrisDashboardCard, IrisDashboardGrid, IrisStack } from './chunk-3R3QDLJC.js';
14
14
  export { IrisSkinKey, SkinProvider, SkinResolutionError, applySkin, builtinSkins, createSkinCatalog, createSkinEngine, createSkinRegistry, darkSkin, lightSkin, loadSkin, localStorageSkinStorage, memorySkinStorage, renderSkinStyle, resolveSkin, skinBootScript, skinError, skinToCssEntries, useSkin, useSkinContext, validateSkin } from './chunk-UPC4GN3F.js';
15
- export { DropdownContextKey, IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisBreadcrumb, IrisBreadcrumbItem, IrisDropdown, IrisDropdownItem, IrisDropdownMenu, IrisDropdownSeparator, IrisDropdownTrigger, IrisIcon, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-FZI5BGWH.js';
15
+ export { DropdownContextKey, IrisAdminBreadcrumb, IrisAdminLayout, IrisAdminTabs, IrisBreadcrumb, IrisBreadcrumbItem, IrisDropdown, IrisDropdownItem, IrisDropdownMenu, IrisDropdownSeparator, IrisDropdownTrigger, IrisIcon, IrisNavMenu, createAdminPreferences, createTabsNav, filterNavByAccess, findNavNode, findNavPath, firstLeaf, flattenNav, isBranch, isClosable, localStorageAdminPreferencesStorage, nodeAllowsRoles, useAdminPreferences, useAdminShell, useTabsNav, visibleNav } from './chunk-IFRH33BT.js';
16
16
  import { useFloating, useDismiss } from './chunk-IPR5JAVF.js';
17
17
  export { useDismiss, useFloating } from './chunk-IPR5JAVF.js';
18
18
  import { useMachine } from './chunk-NNOF7KUH.js';
19
19
  export { useMachine } from './chunk-NNOF7KUH.js';
20
+ export { IrisThemeKey, ThemeProvider, useColorScheme, useDirection, useTheme, useThemeContext } from './chunk-ZTPIXL7Q.js';
20
21
  import { findFirstElement, mergeSlotProps, composeRefs } from './chunk-F77KIPOH.js';
21
22
  export { IrisSlot, composeRefs, findFirstElement, mergeSlotProps } from './chunk-F77KIPOH.js';
22
23
  export { IrisHeaderLayout, IrisSidebarLayout } from './chunk-3V46HGRC.js';
23
- export { IrisThemeKey, ThemeProvider, useColorScheme, useDirection, useTheme, useThemeContext } from './chunk-ZTPIXL7Q.js';
24
24
  export { useAsyncResource, usePaginatedResource } from './chunk-SJB5DIIY.js';
25
25
  import { useDrag } from './chunk-IOMKC5OI.js';
26
26
  export { IrisClickOutside, IrisCompose, IrisHotkey, IrisLongPress, IrisMovable, IrisResizable, useDrag } from './chunk-IOMKC5OI.js';
27
27
  export { IrisSortable, SORTABLE_ITEM_ATTR } from './chunk-W3B6E5GN.js';
28
28
  export { createClientDataSource, createDataSource, createGroupedView, useDataSource, useGroupedView } from './chunk-QBFYIDY7.js';
29
29
  export { IrisErrorBoundary } from './chunk-3547J6IN.js';
30
+ import { useI18n } from './chunk-TCAXRWT6.js';
31
+ export { I18nInjectionKey, IrisI18nProvider, useI18n } from './chunk-TCAXRWT6.js';
30
32
  export { FormInjectionKey, IrisForm, useField, useFieldArray, useForm, useFormContext } from './chunk-IAG64CHL.js';
33
+ import { useGridCore, useGridColumns, useGridSorting, useGridFiltering, useGridRows, useGridSelection, useGridExpansion, useGridEditing, useGridRange, useGridClipboard } from './chunk-5Q3JCKNE.js';
34
+ export { useGridClipboard, useGridColumns, useGridCore, useGridEditing, useGridExpansion, useGridFiltering, useGridPagination, useGridRange, useGridRows, useGridSelection, useGridSorting, useGridVirtual } from './chunk-5Q3JCKNE.js';
31
35
  import { useStore } from './chunk-75GSYEJS.js';
32
36
  export { useStore, useStoreSelector } from './chunk-75GSYEJS.js';
33
- import { useI18n } from './chunk-TCAXRWT6.js';
34
- export { I18nInjectionKey, IrisI18nProvider, useI18n } from './chunk-TCAXRWT6.js';
35
37
  import { defineComponent, computed, watch, ref, useId, provide, inject, h, nextTick, Teleport, onScopeDispose, Fragment, onMounted, watchEffect, onBeforeUnmount, onBeforeMount, shallowRef, toValue, Comment, onUnmounted } from 'vue';
36
- import { createFloatingMachine, createHoverIntent, createVirtualizer, createKeyboardNav, getPageRange, createSelectionModel, formatClock, flattenLeafColumns, buildHeaderMatrix, detectColumnType, seedFormValues, createExpansion, createAuditLog, flattenTree, withSortedChildren, mergeFormFilters, createTreeSelection, createCellRange, createSortable, compareStates, createStore, firstEnabledIndex, createCalendarNav, clampDate, startOfMonth, isSameMonth, buildMonthMatrix, getWeekdayNames, formatMonthYear, startOfDay, endOfMonth, isSameDay, isOutOfRange, formatLocalISO, hexToRgba, rgbaToHsva, hsvaToRgba, rgbToHex, addMonths, RESPONSIVE_NARROW_WIDTH, computeResponsiveColumns, createRemoteTableSource, memoizedFormulaValue, readTableViews, computeVirtualRange, toCsvRows, removeRowsFromList, tableDisplayText, aggregate, previewColumnsFromRows, nextEnabledIndex, copyText, clamp01, rowsFromCsv, compareValues, TABLE_VIEWS_SAVE_ITEM, uniqueTableTabs, leftPinnedCount, pinnedCountFromBudget, toCsv, createAutoDismiss, validateEditRulesAsync, writeClipboardText, serializeTableRange, nextGridCell, createReconnectingSource, createResilientFetcher, createDisposableScope, downloadFile, toSpreadsheetXml, generateId, buildFormValues, applyTableMask, writeTableViews } from '@iris-ui-kit/core';
38
+ import { createFloatingMachine, createHoverIntent, createVirtualizer, createKeyboardNav, getPageRange, createSelectionModel, formatClock, flattenLeafColumns, buildHeaderMatrix, detectColumnType, createRecentFilters, seedFormValues, createAuditLog, flattenTree, withSortedChildren, mergeFormFilters, flattenTreeSelectionNodes, createTreeSelection, createSortable, compareStates, createStore, firstEnabledIndex, createCalendarNav, clampDate, startOfMonth, isSameMonth, buildMonthMatrix, getWeekdayNames, formatMonthYear, startOfDay, endOfMonth, isSameDay, isOutOfRange, formatLocalISO, hexToRgba, rgbaToHsva, hsvaToRgba, rgbToHex, addMonths, RESPONSIVE_NARROW_WIDTH, computeResponsiveColumns, createRemoteTableSource, readTableViews, reconcileTreeRows, computeVirtualRange, toCsvRows, applyTableMask, tableDisplayText, aggregate, previewColumnsFromRows, reorderTreeRows, nextEnabledIndex, copyText, clamp01, rowsFromCsv, memoizedFormulaValue, compareValues, TABLE_VIEWS_SAVE_ITEM, uniqueTableTabs, createUndoStack, normalizeKeymap, matchTableKey, pinnedCountFromBudget, toCsv, splitSearchHits, createAutoDismiss, validateEditRulesAsync, writeClipboardText, nextGridCell, createReconnectingSource, createResilientFetcher, createDisposableScope, downloadFile, toSpreadsheetXml, generateId, buildFormValues, writeTableViews } from '@iris-ui-kit/core';
37
39
  export { addDays, addMonths, buildMonthMatrix, clamp01, clampDate, composeEventHandlers, createFloatingMachine, createMachine, createStore, endOfMonth, formatLocalISO, formatMonthYear, generateId, getPageRange, getWeekdayNames, hexToRgba, hsvToRgb, hsvaToRgba, isOutOfRange, isSameDay, isSameMonth, mergeProps, parseCsv, rgbToHex, rgbToHsv, rgbaToHsva, startOfDay, startOfMonth } from '@iris-ui-kit/core';
38
40
  import { getDirection } from '@iris-ui-kit/theme';
39
41
  export { applyTheme, createThemeStore, getCssVar, toCssVarName } from '@iris-ui-kit/theme';
@@ -3182,7 +3184,7 @@ function badgeStyle(variant, tone, size) {
3182
3184
  fontWeight: "500",
3183
3185
  lineHeight: "1",
3184
3186
  whiteSpace: "nowrap",
3185
- fontSize: size === "sm" ? "var(--iris-font-size-xs, 12px)" : "12px",
3187
+ fontSize: "var(--iris-font-size-xs, 12px)",
3186
3188
  padding: size === "sm" ? "2px 6px" : "3px 8px"
3187
3189
  };
3188
3190
  switch (variant) {
@@ -3482,7 +3484,7 @@ var CSS2 = `
3482
3484
  border-radius: 50%;
3483
3485
  }
3484
3486
  [data-iris-skeleton-shape="text"] {
3485
- border-radius: 4px;
3487
+ border-radius: var(--iris-radius-sm, 4px);
3486
3488
  }
3487
3489
  @media (prefers-reduced-motion: reduce) {
3488
3490
  [data-iris-skeleton][data-iris-skeleton-animated="true"] {
@@ -3539,8 +3541,8 @@ var IrisSkeleton = defineComponent({
3539
3541
  onMounted(installSkeletonStyles);
3540
3542
  const style = computed(() => {
3541
3543
  const w = props.width !== void 0 ? toCss(props.width) : defaultWidth(props.shape);
3542
- const h102 = props.height !== void 0 ? toCss(props.height) : defaultHeight(props.shape, props.width);
3543
- return { width: w, height: h102 };
3544
+ const h104 = props.height !== void 0 ? toCss(props.height) : defaultHeight(props.shape, props.width);
3545
+ return { width: w, height: h104 };
3544
3546
  });
3545
3547
  return () => h("div", {
3546
3548
  ...attrs,
@@ -4173,7 +4175,7 @@ var IrisCard = defineComponent({
4173
4175
  flexDirection: "column",
4174
4176
  background: "var(--iris-background)",
4175
4177
  color: "var(--iris-foreground)",
4176
- borderRadius: "var(--iris-radius-md, 8px)",
4178
+ borderRadius: "var(--iris-radius-md, 6px)",
4177
4179
  overflow: "hidden",
4178
4180
  transition: props.hover ? "transform 160ms ease, box-shadow 160ms ease" : "none"
4179
4181
  };
@@ -5194,7 +5196,7 @@ var IrisKbd = defineComponent({
5194
5196
  background: "var(--iris-surface)",
5195
5197
  color: "var(--iris-foreground)",
5196
5198
  border: "1px solid var(--iris-border)",
5197
- borderRadius: "4px",
5199
+ borderRadius: "var(--iris-radius-sm, 4px)",
5198
5200
  boxShadow: "0 1px 0 var(--iris-border)",
5199
5201
  lineHeight: "1",
5200
5202
  fontWeight: "500"
@@ -5930,6 +5932,13 @@ var tableProps = {
5930
5932
  tableTabs: { type: Array, default: void 0 },
5931
5933
  /** Show a draggable boundary for the leading left-pinned columns. */
5932
5934
  pinnedDrag: { type: Boolean, default: false },
5935
+ /** Controlled per-column pin state; explicit null overrides a static column pin. */
5936
+ pinnedColumns: {
5937
+ type: Object,
5938
+ default: void 0
5939
+ },
5940
+ /** Enable the built-in column-header pin menu; default off. */
5941
+ columnPinMenu: { type: Boolean, default: false },
5933
5942
  /** Called for each column whose pin side changes. */
5934
5943
  onColumnPinnedChange: {
5935
5944
  type: Function,
@@ -5943,6 +5952,14 @@ var tableProps = {
5943
5952
  /** Below 480px, greedily hide the lowest-priority top-level columns until
5944
5953
  * the natural width fits; pinned columns survive. */
5945
5954
  responsive: { type: Boolean, default: false },
5955
+ /**
5956
+ * External row sets for `table!field` formula references. The first row of
5957
+ * the named set is read; replace the object when referenced rows change.
5958
+ */
5959
+ formulaTables: {
5960
+ type: Object,
5961
+ default: void 0
5962
+ },
5946
5963
  /** Extra bare row sets appended as named CSV segments by the imperative handle. */
5947
5964
  exportNames: {
5948
5965
  type: Array,
@@ -5961,6 +5978,8 @@ var tableProps = {
5961
5978
  error: { type: Boolean, default: false },
5962
5979
  /** Print-friendly mode: marks the root so toolbar/form chrome is hidden by print CSS. */
5963
5980
  printable: { type: Boolean, default: false },
5981
+ /** Show a token-styled back-to-top button for the table's effective scroller. */
5982
+ scrollToTop: { type: Boolean, default: false },
5964
5983
  /** Show a confirmation preview before the toolbar CSV import callback. */
5965
5984
  importPreview: { type: Boolean, default: false },
5966
5985
  onRetry: { type: Function, default: void 0 },
@@ -5969,6 +5988,16 @@ var tableProps = {
5969
5988
  cellRange: { type: Boolean, default: false },
5970
5989
  /** Range clipboard copy; copyWithFormat uses column formatter output. */
5971
5990
  clipConfig: { type: Object, default: void 0 },
5991
+ /** Batch CK (iris 独有 — vxe has no inline search highlight): a
5992
+ * case-insensitive literal-substring search over each text cell's display
5993
+ * chain (mask → formatter ?? raw); every occurrence renders as an inline
5994
+ * `<mark data-iris-search-hit>` with the surface-selected token. Display-only:
5995
+ * no bar, no match state, no write-back. `render`/`html`/`link`/`autoLink`/
5996
+ * sparkline cells are untouched. Additive; default off (fail-closed).
5997
+ */
5998
+ searchHighlight: { type: String, default: "" },
5999
+ /** Enable built-in row-list undo/redo history; default off. */
6000
+ undo: { type: Boolean, default: false },
5972
6001
  renderDetail: {
5973
6002
  type: Function,
5974
6003
  default: void 0
@@ -6020,6 +6049,13 @@ var tableProps = {
6020
6049
  type: Object,
6021
6050
  default: void 0
6022
6051
  },
6052
+ /** Controlled top-level presentation order; omitted keys retain source order. */
6053
+ columnOrder: {
6054
+ type: Array,
6055
+ default: void 0
6056
+ },
6057
+ /** Animate controlled column show/hide transitions; disabled by default. */
6058
+ columnFade: { type: Boolean, default: false },
6023
6059
  filters: {
6024
6060
  type: Object,
6025
6061
  default: void 0
@@ -6028,6 +6064,8 @@ var tableProps = {
6028
6064
  type: Object,
6029
6065
  default: void 0
6030
6066
  },
6067
+ /** Show recently confirmed non-empty filter sets in the filter panel; default off. */
6068
+ recentFilters: { type: Boolean, default: false },
6031
6069
  onFilterValuesChange: {
6032
6070
  type: Function,
6033
6071
  default: void 0
@@ -6069,12 +6107,12 @@ var RESIZE_STEP = 16;
6069
6107
  function isEditableColumn(column) {
6070
6108
  return !!column.editable && !column.formula;
6071
6109
  }
6072
- function getCellValue(row, column) {
6073
- if (column.formula) return memoizedFormulaValue(column.formula, row);
6110
+ function getCellValue(row, column, formulaTables) {
6111
+ if (column.formula) return memoizedFormulaValue(column.formula, row, formulaTables);
6074
6112
  const key = column.dataIndex ?? column.key;
6075
6113
  return row[key];
6076
6114
  }
6077
- function withComputedFormulaCells(rows, columns) {
6115
+ function withComputedFormulaCells(rows, columns, formulaTables) {
6078
6116
  const formulaCols = columns.filter((c) => c.formula);
6079
6117
  if (formulaCols.length === 0) return rows;
6080
6118
  return rows.map((row) => {
@@ -6082,7 +6120,11 @@ function withComputedFormulaCells(rows, columns) {
6082
6120
  for (const col of formulaCols) {
6083
6121
  const key = col.dataIndex ?? col.key;
6084
6122
  const next = shadow ?? { ...row };
6085
- next[key] = memoizedFormulaValue(col.formula, row);
6123
+ next[key] = memoizedFormulaValue(
6124
+ col.formula,
6125
+ row,
6126
+ formulaTables
6127
+ );
6086
6128
  shadow = next;
6087
6129
  }
6088
6130
  return shadow;
@@ -6113,7 +6155,7 @@ function resolveInitialWidth(col) {
6113
6155
  }
6114
6156
  return DEFAULT_COL_WIDTH;
6115
6157
  }
6116
- function computeVisibleColSet(enabled, columns, scrollLeft, viewportWidth, widths) {
6158
+ function computeVisibleColSet(enabled, columns, scrollLeft, viewportWidth, widths, pinOf) {
6117
6159
  if (!enabled) return null;
6118
6160
  const range = computeVirtualRange({
6119
6161
  itemCount: columns.length,
@@ -6125,7 +6167,7 @@ function computeVisibleColSet(enabled, columns, scrollLeft, viewportWidth, width
6125
6167
  const visible = /* @__PURE__ */ new Set();
6126
6168
  for (let index = range.startIndex; index <= range.endIndex; index += 1) visible.add(index);
6127
6169
  columns.forEach((column, index) => {
6128
- if (column.pinned) visible.add(index);
6170
+ if (pinOf(column) !== null) visible.add(index);
6129
6171
  });
6130
6172
  return visible;
6131
6173
  }
@@ -6134,18 +6176,21 @@ function cellId(rowIdent, columnKey) {
6134
6176
  }
6135
6177
 
6136
6178
  // src/primitives/table/useTableSort.ts
6137
- function buildSorter(col) {
6179
+ function buildSorter(col, formulaTables) {
6138
6180
  if (col.sorter) return col.sorter;
6139
- return (a, b) => compareValues(getCellValue(a, col), getCellValue(b, col));
6181
+ return (a, b) => compareValues(getCellValue(a, col, formulaTables), getCellValue(b, col, formulaTables));
6140
6182
  }
6141
- function buildMultiSortComparator(leafColumns, state) {
6183
+ function buildMultiSortComparator(leafColumns, state, formulaTables) {
6142
6184
  if (state.length === 0) return null;
6143
6185
  const colMap = new Map(leafColumns.map((c) => [c.key, c]));
6144
6186
  const chain = [];
6145
6187
  for (const s of state) {
6146
6188
  const col = colMap.get(s.key);
6147
6189
  if (!col) continue;
6148
- chain.push({ dir: s.direction === "asc" ? 1 : -1, sorter: buildSorter(col) });
6190
+ chain.push({
6191
+ dir: s.direction === "asc" ? 1 : -1,
6192
+ sorter: buildSorter(col, formulaTables)
6193
+ });
6149
6194
  }
6150
6195
  if (chain.length === 0) return null;
6151
6196
  return (a, b) => {
@@ -6156,93 +6201,6 @@ function buildMultiSortComparator(leafColumns, state) {
6156
6201
  return 0;
6157
6202
  };
6158
6203
  }
6159
- function useTableSort(data, options) {
6160
- const sortProp = computed(() => options.sort === void 0 ? void 0 : toValue(options.sort));
6161
- const internalSortValue = ref(options.defaultSort ?? null);
6162
- const sortState = computed({
6163
- get: () => sortProp.value === void 0 ? internalSortValue.value : sortProp.value ?? null,
6164
- set: (val) => {
6165
- if (sortProp.value === void 0) internalSortValue.value = val;
6166
- options.onSortChange?.(val);
6167
- }
6168
- });
6169
- const multiEnabled = computed(() => toValue(options.multiSort) === true);
6170
- const multiSortProp = computed(
6171
- () => options.multiSortState === void 0 ? void 0 : toValue(options.multiSortState)
6172
- );
6173
- const internalMultiSort = ref(options.defaultMultiSort ?? []);
6174
- const multiSortState = computed({
6175
- get: () => multiSortProp.value === void 0 ? internalMultiSort.value : multiSortProp.value,
6176
- set: (val) => {
6177
- if (multiSortProp.value === void 0) internalMultiSort.value = val;
6178
- options.onMultiSortChange?.(val);
6179
- }
6180
- });
6181
- const sortComparator = computed(() => {
6182
- const s = sortState.value;
6183
- if (!s) return null;
6184
- const col = toValue(options.leafColumns).find((c) => c.key === s.key);
6185
- if (!col) return null;
6186
- const dir = s.direction === "asc" ? 1 : -1;
6187
- return (a, b) => buildSorter(col)(a, b) * dir;
6188
- });
6189
- const multiSortComparator = computed(
6190
- () => buildMultiSortComparator(toValue(options.leafColumns), multiSortState.value)
6191
- );
6192
- const sortedData = computed(() => {
6193
- const comparator = multiEnabled.value ? multiSortComparator.value : sortComparator.value;
6194
- if (!comparator) return data.value;
6195
- return [...data.value].sort(comparator);
6196
- });
6197
- function setSort(next) {
6198
- if (sortProp.value === void 0) internalSortValue.value = next;
6199
- options.onSortChange?.(next);
6200
- }
6201
- function cycleSort(col) {
6202
- if (!col.sortable) return;
6203
- const s = sortState.value;
6204
- if (!s || s.key !== col.key) {
6205
- setSort({ key: col.key, direction: "asc" });
6206
- return;
6207
- }
6208
- if (s.direction === "asc") {
6209
- setSort({ key: col.key, direction: "desc" });
6210
- return;
6211
- }
6212
- setSort(null);
6213
- }
6214
- function setMultiSort(next) {
6215
- if (multiSortProp.value === void 0) internalMultiSort.value = next;
6216
- options.onMultiSortChange?.(next);
6217
- }
6218
- function cycleMultiSort(col) {
6219
- if (!col.sortable) return;
6220
- const idx = multiSortState.value.findIndex((s) => s.key === col.key);
6221
- if (idx < 0) {
6222
- setMultiSort([...multiSortState.value, { key: col.key, direction: "asc" }]);
6223
- return;
6224
- }
6225
- const next = [...multiSortState.value];
6226
- if (next[idx].direction === "asc") {
6227
- next[idx] = { key: col.key, direction: "desc" };
6228
- setMultiSort(next);
6229
- return;
6230
- }
6231
- next.splice(idx, 1);
6232
- setMultiSort(next);
6233
- }
6234
- return {
6235
- sortState,
6236
- cycleSort,
6237
- setSort,
6238
- sortComparator,
6239
- sortedData,
6240
- multiSortState,
6241
- cycleMultiSort,
6242
- setMultiSort,
6243
- multiSortComparator
6244
- };
6245
- }
6246
6204
  function mergeFilterValues(filters, filterValues) {
6247
6205
  const next = { ...filters };
6248
6206
  for (const [key, values] of Object.entries(filterValues)) {
@@ -6359,6 +6317,37 @@ async function downloadCsv(filename, csv) {
6359
6317
  mimeType: "text/csv;charset=utf-8;"
6360
6318
  });
6361
6319
  }
6320
+ var undoButtonStyle = {
6321
+ border: "none",
6322
+ background: "transparent",
6323
+ cursor: "pointer",
6324
+ color: "var(--iris-muted)",
6325
+ fontSize: "var(--iris-font-size-md, 14px)"
6326
+ };
6327
+ var undoButton = (dataAttr, labelKey, active, onActivate, t) => h(
6328
+ "button",
6329
+ {
6330
+ type: "button",
6331
+ [dataAttr]: "",
6332
+ onClick: onActivate,
6333
+ disabled: !active,
6334
+ "aria-label": t(labelKey),
6335
+ title: t(labelKey),
6336
+ style: { ...undoButtonStyle, cursor: active ? "pointer" : "default" }
6337
+ },
6338
+ dataAttr === "data-iris-table-undo" ? "\u21B6" : "\u21B7"
6339
+ );
6340
+ function renderUndoToolbar(ctx) {
6341
+ if (!ctx.enabled) return [];
6342
+ const canUndo = ctx.controller.canUndo.value;
6343
+ const canRedo = ctx.controller.canRedo.value;
6344
+ return [
6345
+ undoButton("data-iris-table-undo", "table.undo", canUndo, ctx.controller.undo, ctx.t),
6346
+ undoButton("data-iris-table-redo", "table.redo", canRedo, ctx.controller.redo, ctx.t)
6347
+ ];
6348
+ }
6349
+
6350
+ // src/primitives/table/table-sections.ts
6362
6351
  function renderFormSection(ctx) {
6363
6352
  const fc = ctx.formConfig;
6364
6353
  if (!fc) return null;
@@ -6449,13 +6438,14 @@ var toolbarBtnStyle = {
6449
6438
  };
6450
6439
  function renderToolbarSection(ctx) {
6451
6440
  const tb = ctx.toolbar;
6452
- if (!tb && !ctx.densityToggle && !ctx.auditLog) return null;
6441
+ if (!tb && !ctx.densityToggle && !ctx.auditLog && !ctx.undo.enabled) return null;
6453
6442
  const toolChildren = [];
6454
6443
  if (tb?.title) {
6455
6444
  toolChildren.push(
6456
6445
  h("span", { style: { fontWeight: 600, color: "var(--iris-foreground)" } }, tb.title)
6457
6446
  );
6458
6447
  }
6448
+ toolChildren.push(...renderUndoToolbar(ctx.undo));
6459
6449
  toolChildren.push(h("div", { style: { flex: "1" } }));
6460
6450
  if (tb?.onRefresh) {
6461
6451
  toolChildren.push(
@@ -6731,6 +6721,15 @@ function renderContextMenuSection(ctx) {
6731
6721
  );
6732
6722
  return h(Teleport, { to: "body" }, [node]);
6733
6723
  }
6724
+ function recentFilterLabel(entry, columns) {
6725
+ const col = columns.find((column) => column.key === entry.key);
6726
+ if (!col) return entry.values.join(", ");
6727
+ const labels = entry.values.map((value) => {
6728
+ const option = col.filterOptions?.find((item) => item.value === value);
6729
+ return option ? option.label : value;
6730
+ });
6731
+ return `${col.title ?? entry.key}: ${labels.join(", ")}`;
6732
+ }
6734
6733
  function renderFilterPanelSection(ctx) {
6735
6734
  const st = ctx.state.value;
6736
6735
  if (!st || !st.open) return null;
@@ -6763,6 +6762,49 @@ function renderFilterPanelSection(ctx) {
6763
6762
  }
6764
6763
  },
6765
6764
  [
6765
+ ...ctx.recent.length > 0 ? [
6766
+ h(
6767
+ "div",
6768
+ {
6769
+ "data-iris-filter-recent-title": "",
6770
+ style: {
6771
+ color: "var(--iris-muted)",
6772
+ fontSize: "var(--iris-font-size-xs, 11px)",
6773
+ marginTop: "var(--iris-space-xxs, 4px)"
6774
+ }
6775
+ },
6776
+ ctx.t("table.recentFilters")
6777
+ ),
6778
+ ...ctx.recent.map(
6779
+ (entry, index) => h(
6780
+ "button",
6781
+ {
6782
+ key: `${entry.key}:${index}`,
6783
+ type: "button",
6784
+ "data-iris-filter-recent": index,
6785
+ onClick: () => ctx.onApplyRecent(entry),
6786
+ style: {
6787
+ border: "none",
6788
+ background: "transparent",
6789
+ color: "var(--iris-foreground)",
6790
+ cursor: "pointer",
6791
+ font: "inherit",
6792
+ fontSize: "var(--iris-font-size-sm, 13px)",
6793
+ padding: "var(--iris-space-xxs, 4px) var(--iris-space-xs, 8px)",
6794
+ borderRadius: "var(--iris-radius-sm, 4px)",
6795
+ textAlign: "start"
6796
+ }
6797
+ },
6798
+ recentFilterLabel(entry, ctx.columns)
6799
+ )
6800
+ ),
6801
+ h("div", {
6802
+ style: {
6803
+ borderTop: "1px solid var(--iris-border)",
6804
+ margin: "var(--iris-space-xxs, 4px) 0"
6805
+ }
6806
+ })
6807
+ ] : [],
6766
6808
  ...options.map(
6767
6809
  (opt) => h(
6768
6810
  "div",
@@ -7246,6 +7288,7 @@ function renderTableSummaryRow(ctx) {
7246
7288
  if (ctx.visibleColSet && !ctx.visibleColSet.has(columnIndex)) continue;
7247
7289
  const align = column.align ?? "left";
7248
7290
  const operation = column.summary;
7291
+ const fadeStyle = ctx.columnFadeStyle(column);
7249
7292
  const value = operation ? aggregate(ctx.bodyData, (row) => ctx.getCellValue(row, column), operation) : null;
7250
7293
  const content = operation != null && value != null ? column.renderSummary ? column.renderSummary(value, ctx.bodyData) : String(value) : "";
7251
7294
  summaryCells.push(
@@ -7256,7 +7299,10 @@ function renderTableSummaryRow(ctx) {
7256
7299
  role: "cell",
7257
7300
  "data-iris-table-cell": column.key,
7258
7301
  "data-iris-table-summary-cell": operation ? "" : void 0,
7259
- "data-iris-table-pinned": column.pinned,
7302
+ "data-iris-table-pinned": ctx.pinOf(column),
7303
+ "data-iris-column-fade": ctx.columnFadeAttr(column),
7304
+ "aria-hidden": fadeStyle ? "true" : void 0,
7305
+ inert: fadeStyle ? "" : void 0,
7260
7306
  style: {
7261
7307
  display: "flex",
7262
7308
  alignItems: "center",
@@ -7267,6 +7313,7 @@ function renderTableSummaryRow(ctx) {
7267
7313
  overflow: "hidden",
7268
7314
  textOverflow: "ellipsis",
7269
7315
  ...ctx.visibleColSet ? { gridColumnStart: String(ctx.colTrack(columnIndex)) } : {},
7316
+ ...fadeStyle ?? {},
7270
7317
  ...ctx.pinnedStyle(column.key)
7271
7318
  }
7272
7319
  },
@@ -7290,6 +7337,187 @@ function renderTableSummaryRow(ctx) {
7290
7337
  summaryCells
7291
7338
  );
7292
7339
  }
7340
+ var FADE_DURATION_MS = 200;
7341
+ function createTableColumnFade(options) {
7342
+ const fadeOverlay = shallowRef({});
7343
+ const previousVisibility = ref({ ...options.visibility() });
7344
+ let fadeFlipRaf = null;
7345
+ let fadeCommitTimer = null;
7346
+ let fadeFocusCandidate = null;
7347
+ let disposed = false;
7348
+ const topLevelColumn = (key) => options.columns.value.find((column) => column.key === key);
7349
+ const fadeFlip = (current) => {
7350
+ let changed = false;
7351
+ const next = {};
7352
+ for (const [key, entry] of Object.entries(current)) {
7353
+ if (entry.phase === "pending") {
7354
+ next[key] = { dir: entry.dir, phase: "run" };
7355
+ changed = true;
7356
+ } else {
7357
+ next[key] = entry;
7358
+ }
7359
+ }
7360
+ return changed ? next : void 0;
7361
+ };
7362
+ const fadeCommit = (current) => {
7363
+ let changed = false;
7364
+ const next = {};
7365
+ const visibility = options.visibility();
7366
+ for (const [key, entry] of Object.entries(current)) {
7367
+ const visible = visibility[key] !== false;
7368
+ const done = entry.dir === "out" ? !visible : visible;
7369
+ if (done) changed = true;
7370
+ else next[key] = entry;
7371
+ }
7372
+ return changed ? next : void 0;
7373
+ };
7374
+ const cancelFadeSchedule = () => {
7375
+ fadeFocusCandidate = null;
7376
+ if (fadeFlipRaf !== null) {
7377
+ if (typeof globalThis.cancelAnimationFrame === "function") {
7378
+ globalThis.cancelAnimationFrame(fadeFlipRaf);
7379
+ }
7380
+ fadeFlipRaf = null;
7381
+ }
7382
+ if (fadeCommitTimer !== null) {
7383
+ clearTimeout(fadeCommitTimer);
7384
+ fadeCommitTimer = null;
7385
+ }
7386
+ };
7387
+ const recoverFocus = (candidate) => {
7388
+ if (disposed || typeof document === "undefined" || typeof HTMLElement === "undefined" || !(candidate instanceof HTMLElement))
7389
+ return;
7390
+ const hidden = candidate.closest("[data-iris-column-fade][inert]");
7391
+ if (!hidden) return;
7392
+ const root = hidden.closest("[data-iris-table]");
7393
+ const rowIndex = hidden.dataset.gridRow;
7394
+ if (root && rowIndex !== void 0) {
7395
+ const currentColumn = Number(hidden.dataset.gridCol);
7396
+ const alternatives = Array.from(
7397
+ root.querySelectorAll("[data-grid-row][data-grid-col]")
7398
+ ).filter((cell) => cell.dataset.gridRow === rowIndex && !cell.hasAttribute("inert")).sort(
7399
+ (a, b) => Math.abs(Number(a.dataset.gridCol) - currentColumn) - Math.abs(Number(b.dataset.gridCol) - currentColumn)
7400
+ );
7401
+ if (alternatives[0]) {
7402
+ alternatives[0].focus();
7403
+ return;
7404
+ }
7405
+ }
7406
+ if (document.activeElement === candidate || hidden.contains(document.activeElement)) {
7407
+ candidate.blur();
7408
+ }
7409
+ };
7410
+ watch(
7411
+ [options.visibility, options.enabled, options.reducedMotion],
7412
+ ([visibility, fadeEnabled, motion]) => {
7413
+ const next = visibility;
7414
+ const previous = previousVisibility.value;
7415
+ previousVisibility.value = { ...next };
7416
+ if (!fadeEnabled || motion) {
7417
+ cancelFadeSchedule();
7418
+ fadeOverlay.value = {};
7419
+ return;
7420
+ }
7421
+ const overlay = { ...fadeOverlay.value };
7422
+ const activeElement = typeof document !== "undefined" ? document.activeElement : null;
7423
+ const changedKeys = /* @__PURE__ */ new Set();
7424
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)])) {
7425
+ if (!topLevelColumn(key)) continue;
7426
+ const wasVisible = previous[key] !== false;
7427
+ const isVisible = next[key] !== false;
7428
+ if (wasVisible === isVisible) continue;
7429
+ overlay[key] = { dir: isVisible ? "in" : "out", phase: "pending" };
7430
+ changedKeys.add(key);
7431
+ }
7432
+ if (changedKeys.size === 0) return;
7433
+ fadeOverlay.value = overlay;
7434
+ fadeFocusCandidate = activeElement;
7435
+ if (typeof window === "undefined" || typeof window.requestAnimationFrame !== "function") {
7436
+ cancelFadeSchedule();
7437
+ fadeOverlay.value = {};
7438
+ return;
7439
+ }
7440
+ if (fadeFlipRaf === null) {
7441
+ fadeFlipRaf = window.requestAnimationFrame(() => {
7442
+ if (disposed) return;
7443
+ fadeFlipRaf = null;
7444
+ fadeFlipRaf = window.requestAnimationFrame(() => {
7445
+ if (disposed) return;
7446
+ fadeFlipRaf = null;
7447
+ const candidate = fadeFocusCandidate;
7448
+ fadeOverlay.value = fadeFlip(fadeOverlay.value) ?? fadeOverlay.value;
7449
+ void nextTick(() => recoverFocus(candidate));
7450
+ });
7451
+ });
7452
+ }
7453
+ if (fadeCommitTimer !== null) clearTimeout(fadeCommitTimer);
7454
+ fadeCommitTimer = setTimeout(() => {
7455
+ if (disposed) return;
7456
+ fadeCommitTimer = null;
7457
+ fadeOverlay.value = fadeCommit(fadeOverlay.value) ?? fadeOverlay.value;
7458
+ }, FADE_DURATION_MS);
7459
+ }
7460
+ );
7461
+ onBeforeUnmount(() => {
7462
+ disposed = true;
7463
+ cancelFadeSchedule();
7464
+ });
7465
+ const effectiveVisibility = computed(() => {
7466
+ if (Object.keys(fadeOverlay.value).length === 0) return options.visibility();
7467
+ const merged = { ...options.visibility() };
7468
+ for (const key of Object.keys(fadeOverlay.value)) merged[key] = true;
7469
+ return merged;
7470
+ });
7471
+ const displayColumns = computed(() => {
7472
+ const visibility = effectiveVisibility.value;
7473
+ if (Object.keys(visibility).length === 0) return options.columns.value;
7474
+ return options.columns.value.filter((column) => visibility[column.key] !== false);
7475
+ });
7476
+ const fadeByLeaf = computed(() => {
7477
+ if (Object.keys(fadeOverlay.value).length === 0) return {};
7478
+ const out = {};
7479
+ for (const [key, entry] of Object.entries(fadeOverlay.value)) {
7480
+ const top = topLevelColumn(key);
7481
+ if (!top) continue;
7482
+ const leaves = top.children && top.children.length > 0 ? flattenLeafColumns([top]) : [top];
7483
+ for (const leaf of leaves) out[leaf.key] = entry;
7484
+ }
7485
+ return out;
7486
+ });
7487
+ const columnFadeAttr = (column) => (fadeOverlay.value[column.key] ?? fadeByLeaf.value[column.key])?.dir;
7488
+ const columnFadeStyle = (column) => {
7489
+ const entry = fadeOverlay.value[column.key] ?? fadeByLeaf.value[column.key];
7490
+ if (!entry) return null;
7491
+ const hidden = entry.dir === "out" ? entry.phase === "run" : entry.phase === "pending";
7492
+ return hidden ? { opacity: "0" } : null;
7493
+ };
7494
+ const columnFadeAttrs = (column) => {
7495
+ const hidden = columnFadeStyle(column) !== null;
7496
+ return {
7497
+ "data-iris-column-fade": columnFadeAttr(column),
7498
+ "aria-hidden": hidden ? "true" : void 0,
7499
+ inert: hidden ? "" : void 0
7500
+ };
7501
+ };
7502
+ const columnFadeActive = computed(
7503
+ () => options.enabled() === true && Object.keys(fadeByLeaf.value).length > 0
7504
+ );
7505
+ const isCollapsed = (key) => {
7506
+ const entry = fadeByLeaf.value[key];
7507
+ return Boolean(
7508
+ entry && (entry.dir === "out" && entry.phase === "run" || entry.dir === "in" && entry.phase === "pending")
7509
+ );
7510
+ };
7511
+ return {
7512
+ displayColumns,
7513
+ fadeByLeaf,
7514
+ columnFadeAttr,
7515
+ columnFadeStyle,
7516
+ columnFadeAttrs,
7517
+ columnFadeActive,
7518
+ isCollapsed
7519
+ };
7520
+ }
7293
7521
  function renderTableStateRow(ctx) {
7294
7522
  if (ctx.error) {
7295
7523
  return h("div", { role: "row", "data-iris-table-row": "error", style: ctx.stateRowStyle }, [
@@ -7399,24 +7627,25 @@ function createTableKeyboard(options) {
7399
7627
  const range = activeCellRange();
7400
7628
  const clip = options.clipConfig();
7401
7629
  if (!range || clip?.copy === false) return;
7402
- const rows = withComputedFormulaCells(options.rows(), options.columns());
7403
- void writeClipboardText(
7404
- serializeTableRange(
7405
- rows,
7406
- options.columns(),
7407
- range,
7408
- clip?.copyFormat,
7409
- clip?.copyWithFormat === true
7410
- )
7411
- );
7630
+ const text = options.serializeRange(clip?.copyFormat, clip?.copyWithFormat === true);
7631
+ if (text !== null) void writeClipboardText(text);
7412
7632
  };
7413
7633
  const handleClipboardKey = (event) => {
7414
7634
  const clip = options.clipConfig();
7415
7635
  if (!options.cellRange() || !clip || event.defaultPrevented) return;
7416
- if (event.key.toLowerCase() !== "c" || !event.ctrlKey && !event.metaKey) return;
7417
- if (clip.copy === false || !activeCellRange()) return;
7418
- event.preventDefault();
7419
- copyActiveRange();
7636
+ if (!event.ctrlKey && !event.metaKey) return;
7637
+ const key = event.key.toLowerCase();
7638
+ const range = activeCellRange();
7639
+ if (!range) return;
7640
+ if (key === "c") {
7641
+ if (clip.copy === false) return;
7642
+ event.preventDefault();
7643
+ copyActiveRange();
7644
+ } else if (key === "v") {
7645
+ if (clip.paste === false) return;
7646
+ event.preventDefault();
7647
+ options.pasteRange(range);
7648
+ }
7420
7649
  };
7421
7650
  return {
7422
7651
  handleRootKeyDown: (event) => {
@@ -7429,11 +7658,11 @@ function createTableKeyboard(options) {
7429
7658
  copyActiveRange
7430
7659
  };
7431
7660
  }
7432
- function computeResponsiveTableColumns(columns, containerWidth, leadingWidth, widthOf) {
7661
+ function computeResponsiveTableColumns(columns, containerWidth, leadingWidth, widthOf, pinOf) {
7433
7662
  if (containerWidth <= 0 || containerWidth >= RESPONSIVE_NARROW_WIDTH) {
7434
7663
  return { columns, overflow: false };
7435
7664
  }
7436
- const isPinned = (column) => column.children && column.children.length > 0 ? column.children.some(isPinned) : column.pinned !== void 0;
7665
+ const isPinned = (column) => column.children && column.children.length > 0 ? column.children.some(isPinned) : pinOf(column) !== null;
7437
7666
  const fitted = computeResponsiveColumns(columns, Math.max(1, containerWidth - leadingWidth), {
7438
7667
  widthOf: (column) => widthOf(column),
7439
7668
  isPinned: (column) => isPinned(column),
@@ -7448,6 +7677,7 @@ function computeResponsiveTableColumns(columns, containerWidth, leadingWidth, wi
7448
7677
 
7449
7678
  // src/primitives/table/table-styles.ts
7450
7679
  var TABLE_STYLE_ID = "iris-table-row-styles";
7680
+ var TABLE_FADE_STYLE_ID = "iris-table-column-fade-styles-vue";
7451
7681
  var TABLE_STYLES = `
7452
7682
  [data-iris-table] [role="row"]:hover {
7453
7683
  --iris-cell-bg: var(--iris-surface-hover);
@@ -7469,15 +7699,52 @@ var TABLE_STYLES = `
7469
7699
  background: color-mix(in srgb, var(--iris-primary) 18%, var(--iris-background));
7470
7700
  }
7471
7701
  `;
7472
- function ensureTableStyles() {
7473
- if (typeof document === "undefined" || document.getElementById(TABLE_STYLE_ID)) return;
7474
- const style = document.createElement("style");
7475
- style.id = TABLE_STYLE_ID;
7476
- style.textContent = TABLE_STYLES;
7477
- document.head.appendChild(style);
7702
+ var TABLE_FADE_STYLES = `
7703
+ [data-iris-column-fade-active] [data-iris-column-fade] {
7704
+ transition: opacity var(--iris-duration-md, 200ms) ease;
7705
+ }
7706
+ [data-iris-column-fade-active] [role="row"],
7707
+ [data-iris-column-fade-active][role="row"] {
7708
+ transition: grid-template-columns var(--iris-duration-md, 200ms) ease;
7709
+ }
7710
+ @media (prefers-reduced-motion: reduce) {
7711
+ [data-iris-column-fade-active] [data-iris-column-fade],
7712
+ [data-iris-column-fade-active][data-iris-column-fade],
7713
+ [data-iris-column-fade-active] [role="row"],
7714
+ [data-iris-column-fade-active][role="row"] {
7715
+ transition: none !important;
7716
+ }
7717
+ }
7718
+ `;
7719
+ function ensureTableStyles(includeFade = false) {
7720
+ if (typeof document === "undefined") return;
7721
+ if (!document.getElementById(TABLE_STYLE_ID)) {
7722
+ const style = document.createElement("style");
7723
+ style.id = TABLE_STYLE_ID;
7724
+ style.textContent = TABLE_STYLES;
7725
+ document.head.appendChild(style);
7726
+ }
7727
+ if (!includeFade) return;
7728
+ if (!document.getElementById(TABLE_FADE_STYLE_ID)) {
7729
+ const style = document.createElement("style");
7730
+ style.id = TABLE_FADE_STYLE_ID;
7731
+ style.textContent = TABLE_FADE_STYLES;
7732
+ document.head.appendChild(style);
7733
+ }
7478
7734
  }
7479
7735
 
7480
7736
  // src/primitives/table/table-columns.ts
7737
+ function applyTableColumnOrder(columns, order) {
7738
+ if (!order || order.length === 0) return columns;
7739
+ const orderIndex = /* @__PURE__ */ new Map();
7740
+ order.forEach((key, index) => {
7741
+ if (!orderIndex.has(key)) orderIndex.set(key, index);
7742
+ });
7743
+ const ordered = columns.filter((column) => orderIndex.has(column.key));
7744
+ const rest = columns.filter((column) => !orderIndex.has(column.key));
7745
+ ordered.sort((left, right) => orderIndex.get(left.key) - orderIndex.get(right.key));
7746
+ return [...ordered, ...rest];
7747
+ }
7481
7748
  function applyDetectedTableTypes(columns, detectedTypes) {
7482
7749
  const apply = (column) => {
7483
7750
  const kind = detectedTypes[column.key];
@@ -7575,16 +7842,24 @@ function createTableRowTarget(root) {
7575
7842
  };
7576
7843
  return { find, scrollTo, goTo, dispose };
7577
7844
  }
7845
+ function leftPinnedCount(columns, pinOf, cap) {
7846
+ let count = 0;
7847
+ for (let index = 0; index < cap; index += 1) {
7848
+ if (pinOf(columns[index]) === "left") count = index + 1;
7849
+ else break;
7850
+ }
7851
+ return count;
7852
+ }
7578
7853
  function createTablePinnedDrag(options) {
7579
7854
  const firstRightPinnedIndex = computed(() => {
7580
- const index = options.columns().findIndex((column) => column.pinned === "right");
7855
+ const index = options.columns().findIndex((column) => options.pinOf(column) === "right");
7581
7856
  return index < 0 ? options.columns().length : index;
7582
7857
  });
7583
7858
  const pinnedBoundaryColumn = computed(() => {
7584
7859
  if (!options.enabled()) return null;
7585
7860
  for (let index = firstRightPinnedIndex.value - 1; index >= 0; index -= 1) {
7586
7861
  const column = options.columns()[index];
7587
- if (column?.pinned === "left") return column;
7862
+ if (column && options.pinOf(column) === "left") return column;
7588
7863
  }
7589
7864
  return null;
7590
7865
  });
@@ -7603,7 +7878,7 @@ function createTablePinnedDrag(options) {
7603
7878
  const resolvePinnedCount = (dx) => {
7604
7879
  const columns = options.columns();
7605
7880
  const cap = firstRightPinnedIndex.value;
7606
- const current = leftPinnedCount(columns, cap);
7881
+ const current = leftPinnedCount(columns, options.pinOf, cap);
7607
7882
  let currentWidth = 0;
7608
7883
  for (let index = 0; index < current; index += 1) {
7609
7884
  const column = columns[index];
@@ -7621,14 +7896,14 @@ function createTablePinnedDrag(options) {
7621
7896
  const columns = options.columns();
7622
7897
  const cap = firstRightPinnedIndex.value;
7623
7898
  const clamped = Math.max(0, Math.min(cap, count));
7624
- const current = leftPinnedCount(columns, cap);
7899
+ const current = leftPinnedCount(columns, options.pinOf, cap);
7625
7900
  if (clamped === current) return;
7626
7901
  for (let index = 0; index < cap; index += 1) {
7627
7902
  const column = columns[index];
7628
7903
  if (!column) continue;
7629
7904
  const target = index < clamped ? "left" : null;
7630
- if (column.pinned === target) continue;
7631
- options.onColumnPinnedChange?.(column.key, target);
7905
+ if (options.pinOf(column) === target) continue;
7906
+ options.setPinned(column.key, target);
7632
7907
  }
7633
7908
  options.onPinnedCountChange?.(clamped);
7634
7909
  };
@@ -7708,6 +7983,218 @@ function createTablePinnedDrag(options) {
7708
7983
  );
7709
7984
  };
7710
7985
  }
7986
+ function isInsideTableRoot(root, target) {
7987
+ return target !== null && typeof target === "object" && "nodeType" in target && root() !== null && root().contains(target);
7988
+ }
7989
+ function isTextControl(target) {
7990
+ if (!target || typeof target !== "object") return false;
7991
+ const element = target;
7992
+ return element.tagName === "INPUT" || element.tagName === "TEXTAREA" || element.tagName === "SELECT" || element.dataset?.irisTableEditor !== void 0;
7993
+ }
7994
+ function registerScopedKeydownListener(gate, handler) {
7995
+ let listening = false;
7996
+ let stopGateWatch = null;
7997
+ const syncListener = () => {
7998
+ if (typeof window === "undefined") return;
7999
+ if (gate() && !listening) {
8000
+ window.addEventListener("keydown", handler);
8001
+ listening = true;
8002
+ } else if (!gate() && listening) {
8003
+ window.removeEventListener("keydown", handler);
8004
+ listening = false;
8005
+ }
8006
+ };
8007
+ onMounted(() => {
8008
+ syncListener();
8009
+ stopGateWatch = watch(gate, syncListener);
8010
+ });
8011
+ onBeforeUnmount(() => {
8012
+ stopGateWatch?.();
8013
+ stopGateWatch = null;
8014
+ if (listening && typeof window !== "undefined") {
8015
+ window.removeEventListener("keydown", handler);
8016
+ listening = false;
8017
+ }
8018
+ });
8019
+ }
8020
+
8021
+ // src/primitives/table/table-undo.ts
8022
+ function createCommittedList(source) {
8023
+ let current = source();
8024
+ return {
8025
+ list: () => {
8026
+ const latest = source();
8027
+ if (latest !== current) current = latest;
8028
+ return current;
8029
+ },
8030
+ sync: (next) => {
8031
+ current = next;
8032
+ }
8033
+ };
8034
+ }
8035
+ function replaceTableCell(rows, key, columnKey, value, rowKeyOf) {
8036
+ const index = rows.findIndex((candidate, i) => rowKeyOf(candidate, i) === key);
8037
+ if (index < 0) return rows;
8038
+ const next = rows.slice();
8039
+ next[index] = { ...next[index], [columnKey]: value };
8040
+ return next;
8041
+ }
8042
+ function sameRowList(a, b) {
8043
+ if (a === b) return true;
8044
+ if (a.length !== b.length) return false;
8045
+ for (let i = 0; i < a.length; i += 1) {
8046
+ if (a[i] !== b[i]) return false;
8047
+ }
8048
+ return true;
8049
+ }
8050
+ function rebaselineOnExternalChange(ctx, stack, bump) {
8051
+ let lastExternalRows = ctx.sourceRows();
8052
+ watch(ctx.sourceRows, (next) => {
8053
+ if (next === lastExternalRows) return;
8054
+ lastExternalRows = next;
8055
+ if (ctx.enabled() && !stack.canUndo() && !stack.canRedo()) {
8056
+ stack.clear();
8057
+ stack.push([...next ?? []]);
8058
+ bump();
8059
+ }
8060
+ });
8061
+ }
8062
+ function replayStep(ctx, commit, setRestoring) {
8063
+ return (rows, type) => {
8064
+ if (rows === void 0) return;
8065
+ const before = ctx.selection.current();
8066
+ if (ctx.selection.enabled() && before.length > 0) {
8067
+ const keys = /* @__PURE__ */ new Set();
8068
+ rows.forEach((row, index) => keys.add(ctx.selection.keyOf(row, index)));
8069
+ const vanished = before.filter((key) => !keys.has(key));
8070
+ if (vanished.length > 0) {
8071
+ ctx.selection.rebase();
8072
+ ctx.selection.set(before.filter((key) => !vanished.includes(key)));
8073
+ }
8074
+ }
8075
+ setRestoring(true);
8076
+ try {
8077
+ commit(rows, type);
8078
+ } finally {
8079
+ setRestoring(false);
8080
+ }
8081
+ };
8082
+ }
8083
+ function undoKeydownBindings(ctx, onUndo, onRedo) {
8084
+ const bindings = normalizeKeymap();
8085
+ return (event) => {
8086
+ if (!ctx.enabled() || event.defaultPrevented) return;
8087
+ if (!isInsideTableRoot(ctx.root, event.target)) return;
8088
+ if (isTextControl(event.target) || ctx.isEditing()) return;
8089
+ if (matchTableKey(event, bindings.undo)) {
8090
+ event.preventDefault();
8091
+ onUndo();
8092
+ } else if (matchTableKey(event, bindings.redo)) {
8093
+ event.preventDefault();
8094
+ onRedo();
8095
+ }
8096
+ };
8097
+ }
8098
+ function createTableUndoRuntime(ctx) {
8099
+ const stack = createUndoStack({
8100
+ maxHistory: 100,
8101
+ initial: [...ctx.initialRows() ?? []],
8102
+ equals: sameRowList
8103
+ });
8104
+ const tick = ref(0);
8105
+ let restoring = false;
8106
+ const bump = () => {
8107
+ tick.value += 1;
8108
+ };
8109
+ const record = (rows) => {
8110
+ if (!ctx.enabled() || restoring) return;
8111
+ stack.push([...rows ?? []]);
8112
+ bump();
8113
+ };
8114
+ const commit = (rows, type = "edit") => {
8115
+ record(rows);
8116
+ ctx.recordAudit(rows, type);
8117
+ ctx.setRows(rows);
8118
+ ctx.onDataChange(rows);
8119
+ };
8120
+ return {
8121
+ stack,
8122
+ bump,
8123
+ readTick: () => void tick.value,
8124
+ record,
8125
+ commit,
8126
+ setRestoring: (value) => restoring = value
8127
+ };
8128
+ }
8129
+ function createUndoRedoActions(stack, bump, replay) {
8130
+ const undo = () => {
8131
+ const rows = stack.undo();
8132
+ if (rows !== void 0) {
8133
+ bump();
8134
+ replay(rows, "undo");
8135
+ }
8136
+ };
8137
+ const redo = () => {
8138
+ const rows = stack.redo();
8139
+ if (rows !== void 0) {
8140
+ bump();
8141
+ replay(rows, "redo");
8142
+ }
8143
+ };
8144
+ return { undo, redo };
8145
+ }
8146
+ function createTableUndoController(enabled, initialRows, sourceRows, setRows, recordAudit, onDataChange, root, isEditing, selection) {
8147
+ const ctx = {
8148
+ enabled,
8149
+ initialRows,
8150
+ sourceRows,
8151
+ setRows,
8152
+ recordAudit,
8153
+ onDataChange,
8154
+ root,
8155
+ isEditing,
8156
+ selection
8157
+ };
8158
+ const runtime = createTableUndoRuntime(ctx);
8159
+ const { stack, bump, record, commit } = runtime;
8160
+ const canUndo = computed(() => {
8161
+ runtime.readTick();
8162
+ return stack.canUndo();
8163
+ });
8164
+ const canRedo = computed(() => {
8165
+ runtime.readTick();
8166
+ return stack.canRedo();
8167
+ });
8168
+ rebaselineOnExternalChange(ctx, stack, bump);
8169
+ const replay = replayStep(ctx, commit, runtime.setRestoring);
8170
+ const { undo, redo } = createUndoRedoActions(stack, bump, replay);
8171
+ const handleKeydown = undoKeydownBindings(ctx, undo, redo);
8172
+ registerScopedKeydownListener(() => ctx.enabled(), handleKeydown);
8173
+ return { stack, canUndo, canRedo, record, commit, undo, redo, handleKeydown };
8174
+ }
8175
+ var SEARCH_HIT_STYLE = {
8176
+ background: "var(--iris-surface-selected, rgba(99,102,241,0.12))",
8177
+ color: "inherit",
8178
+ borderRadius: "var(--iris-radius-sm, 4px)",
8179
+ padding: "0 var(--iris-space-xxs, 4px)"
8180
+ };
8181
+ function applySearchHighlight(node, query) {
8182
+ if (!query || typeof node !== "string") return node;
8183
+ const text = String(node);
8184
+ const segments = splitSearchHits(text, query);
8185
+ if (!segments) return node;
8186
+ return segments.map(
8187
+ (segment, index) => index % 2 === 1 ? h(
8188
+ "mark",
8189
+ {
8190
+ key: index,
8191
+ "data-iris-search-hit": "",
8192
+ style: SEARCH_HIT_STYLE
8193
+ },
8194
+ segment
8195
+ ) : segment
8196
+ );
8197
+ }
7711
8198
  function useTableImport(isPreviewEnabled, getOnImport) {
7712
8199
  const importFileInput = ref(null);
7713
8200
  const importPreviewRows = ref(null);
@@ -7837,7 +8324,7 @@ function renderCellEditContent(ctx, row, col, index, editCellId) {
7837
8324
  "aria-invalid": error ? "true" : void 0,
7838
8325
  "aria-describedby": error ? `${editCellId}-error` : void 0,
7839
8326
  onInput: (e) => {
7840
- ctx.editingDraft.value = e.target.value;
8327
+ ctx.setEditingDraft(e.target.value);
7841
8328
  },
7842
8329
  onKeydown: (e) => {
7843
8330
  if (e.key === "Enter") {
@@ -7945,6 +8432,7 @@ function renderGroupedHeader(ctx, matrix) {
7945
8432
  const isLeaf = !col.children || col.children.length === 0;
7946
8433
  const sortable = isLeaf && col.sortable;
7947
8434
  const align = col.align ?? "left";
8435
+ const fadeStyle = ctx.columnFadeStyle(col);
7948
8436
  const headerSlot = ctx.slots[`header.${col.key}`];
7949
8437
  const title = headerSlot?.({ column: col }) ?? col.title;
7950
8438
  cells.push(
@@ -7955,8 +8443,13 @@ function renderGroupedHeader(ctx, matrix) {
7955
8443
  role: "columnheader",
7956
8444
  "data-iris-table-header": col.key,
7957
8445
  "data-iris-table-header-group": isLeaf ? void 0 : "",
8446
+ "data-iris-table-pinned": isLeaf && (ctx.pinnedColumnsControlled || ctx.columnPinMenu) ? ctx.pinOf(col) : void 0,
8447
+ "data-iris-column-fade": ctx.columnFadeAttr(col),
8448
+ "aria-hidden": fadeStyle ? "true" : void 0,
8449
+ inert: fadeStyle ? "" : void 0,
7958
8450
  "aria-colspan": cell.colSpan,
7959
8451
  onClick: sortable ? () => ctx.onHeaderClick(col) : void 0,
8452
+ ...isLeaf && ctx.onHeaderContextMenu ? { onContextmenu: (event) => ctx.onHeaderContextMenu(event, col) } : {},
7960
8453
  "aria-sort": sortable ? ctx.ariaSortFor(col) : void 0,
7961
8454
  style: {
7962
8455
  gridColumn: `${lead + cell.colStart} / span ${cell.colSpan}`,
@@ -7974,7 +8467,9 @@ function renderGroupedHeader(ctx, matrix) {
7974
8467
  color: "var(--iris-foreground)",
7975
8468
  whiteSpace: "nowrap",
7976
8469
  overflow: "hidden",
7977
- textOverflow: "ellipsis"
8470
+ textOverflow: "ellipsis",
8471
+ ...fadeStyle ?? {},
8472
+ ...isLeaf && (ctx.pinnedColumnsControlled || ctx.columnPinMenu) && ctx.pinOf(col) !== null ? { ...ctx.pinnedStyle(col.key), background: "var(--iris-surface)" } : {}
7978
8473
  }
7979
8474
  },
7980
8475
  [
@@ -8222,6 +8717,47 @@ function renderTableTabs(tabs, activeTab, onApply) {
8222
8717
  }
8223
8718
 
8224
8719
  // src/primitives/table/Table.ts
8720
+ var SCROLL_TOP_VISIBLE_PX = 200;
8721
+ var PIN_LEFT_MENU_KEY = "__iris-pin-left";
8722
+ var UNPIN_MENU_KEY = "__iris-unpin";
8723
+ var BACK_TOP_ANCHOR_STYLE = {
8724
+ position: "sticky",
8725
+ insetBlockEnd: "0px",
8726
+ height: "0px",
8727
+ pointerEvents: "none",
8728
+ zIndex: "3"
8729
+ };
8730
+ var BACK_TOP_BUTTON_STYLE = {
8731
+ position: "absolute",
8732
+ insetBlockEnd: "24px",
8733
+ insetInlineEnd: "24px",
8734
+ width: "40px",
8735
+ height: "40px",
8736
+ borderRadius: "50%",
8737
+ border: "1px solid var(--iris-border)",
8738
+ background: "var(--iris-surface, var(--iris-background))",
8739
+ color: "var(--iris-foreground)",
8740
+ cursor: "pointer",
8741
+ boxShadow: "var(--iris-shadow-md)",
8742
+ display: "inline-flex",
8743
+ alignItems: "center",
8744
+ justifyContent: "center",
8745
+ fontSize: "var(--iris-font-size-xl, 18px)",
8746
+ pointerEvents: "auto"
8747
+ };
8748
+ async function readClipboardText() {
8749
+ if (typeof navigator === "undefined") return null;
8750
+ const nav = navigator;
8751
+ if (!nav.clipboard?.readText) return null;
8752
+ try {
8753
+ return await nav.clipboard.readText();
8754
+ } catch {
8755
+ return null;
8756
+ }
8757
+ }
8758
+ function prefersReducedMotion() {
8759
+ return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
8760
+ }
8225
8761
  var IrisTable = defineComponent({
8226
8762
  name: "IrisTable",
8227
8763
  inheritAttrs: false,
@@ -8234,13 +8770,22 @@ var IrisTable = defineComponent({
8234
8770
  "update:columnWidths": (_value) => true,
8235
8771
  /** Controlled columnVisibility channel (parent owns the map). */
8236
8772
  "update:columnVisibility": (_value) => true,
8773
+ /** Controlled top-level column-order proposal channel. */
8774
+ "update:columnOrder": (_value) => true,
8237
8775
  rowClick: (_row, _index) => true,
8238
8776
  rowDblclick: (_row, _index) => true,
8239
8777
  cellEdit: (_payload) => true,
8240
8778
  expandedRowsChange: (_keys) => true
8241
8779
  },
8242
8780
  setup(props, { slots, attrs, emit, expose }) {
8243
- ensureTableStyles();
8781
+ ensureTableStyles(props.columnFade);
8782
+ watch(
8783
+ () => props.columnFade,
8784
+ (enabled) => {
8785
+ if (enabled) ensureTableStyles(true);
8786
+ }
8787
+ );
8788
+ const getCellValue2 = (row, column) => getCellValue(row, column, props.formulaTables);
8244
8789
  const { t } = useI18n();
8245
8790
  const densityState = ref("comfortable");
8246
8791
  const effectiveDensity = computed(() => {
@@ -8254,17 +8799,102 @@ var IrisTable = defineComponent({
8254
8799
  () => props.importPreview,
8255
8800
  () => props.toolbar?.onImport
8256
8801
  );
8257
- const responsiveWidth = ref(0);
8258
- const internalWidths = ref({});
8259
- const effectiveWidths = computed(() => {
8260
- if (props.columnWidths) return props.columnWidths;
8261
- return internalWidths.value;
8802
+ const gridCore = useGridCore();
8803
+ const columnsFeature = useGridColumns(gridCore, {
8804
+ pinned: props.pinnedColumns,
8805
+ onVisibilityChange: (next) => emit("update:columnVisibility", next),
8806
+ onOrderChange: (next) => emit("update:columnOrder", next),
8807
+ onWidthsChange: (next) => emit("update:columnWidths", next),
8808
+ onPinnedChange: (key, side) => props.onColumnPinnedChange?.(key, side)
8262
8809
  });
8263
- const sourceDisplayColumns = computed(() => {
8264
- const vis = props.columnVisibility;
8265
- if (!vis) return props.columns;
8266
- return props.columns.filter((c) => vis[c.key] !== false);
8810
+ const pinnedPropControlled = ref(props.pinnedColumns !== void 0);
8811
+ const pinOf = (column) => {
8812
+ const pinned = props.pinnedColumns;
8813
+ if (pinned !== void 0) {
8814
+ if (Object.prototype.hasOwnProperty.call(pinned, column.key)) {
8815
+ return pinned[column.key] ?? null;
8816
+ }
8817
+ return column.pinned ?? null;
8818
+ }
8819
+ const internal = columnsFeature.state.value.pinned;
8820
+ if (Object.prototype.hasOwnProperty.call(internal, column.key)) {
8821
+ return internal[column.key] ?? null;
8822
+ }
8823
+ return column.pinned ?? null;
8824
+ };
8825
+ watch(
8826
+ () => props.pinnedColumns,
8827
+ (next) => {
8828
+ if (next !== void 0) {
8829
+ pinnedPropControlled.value = true;
8830
+ columnsFeature.model.syncPinned(next);
8831
+ } else {
8832
+ if (pinnedPropControlled.value) columnsFeature.model.syncPinned({});
8833
+ pinnedPropControlled.value = false;
8834
+ }
8835
+ },
8836
+ { immediate: true, flush: "sync" }
8837
+ );
8838
+ const orderPropControlled = ref(props.columnOrder !== void 0);
8839
+ watch(
8840
+ () => props.columnOrder,
8841
+ (next) => {
8842
+ if (next !== void 0) {
8843
+ orderPropControlled.value = true;
8844
+ columnsFeature.model.syncOrder(next);
8845
+ } else {
8846
+ if (orderPropControlled.value) columnsFeature.model.syncOrder([]);
8847
+ orderPropControlled.value = false;
8848
+ }
8849
+ },
8850
+ { immediate: true, flush: "sync" }
8851
+ );
8852
+ const effectiveColumnOrder = computed(
8853
+ () => orderPropControlled.value ? props.columnOrder ?? [] : columnsFeature.state.value.order
8854
+ );
8855
+ const uncontrolledWidths = ref({});
8856
+ const widthPropControlled = ref(props.columnWidths !== void 0);
8857
+ watch(
8858
+ () => props.columnVisibility,
8859
+ (next) => columnsFeature.model.syncVisibility(next ?? {}),
8860
+ { immediate: true }
8861
+ );
8862
+ watch(
8863
+ () => props.columnWidths,
8864
+ (next) => {
8865
+ if (next !== void 0) {
8866
+ columnsFeature.model.syncWidths(next);
8867
+ } else if (widthPropControlled.value) {
8868
+ columnsFeature.model.syncWidths(uncontrolledWidths.value);
8869
+ }
8870
+ widthPropControlled.value = next !== void 0;
8871
+ },
8872
+ { immediate: true }
8873
+ );
8874
+ const responsiveWidth = ref(0);
8875
+ const effectiveWidths = computed(
8876
+ () => props.columnWidths !== void 0 ? props.columnWidths : columnsFeature.state.value.widths
8877
+ );
8878
+ const effectiveVisibility = computed(
8879
+ () => columnsFeature.state.value.visibility
8880
+ );
8881
+ const columnFadeEnabled = computed(() => props.columnFade);
8882
+ const reducedMotion = usePrefersReducedMotion(columnFadeEnabled);
8883
+ const columnFade = createTableColumnFade({
8884
+ visibility: () => effectiveVisibility.value,
8885
+ enabled: () => columnFadeEnabled.value,
8886
+ reducedMotion,
8887
+ columns: computed(() => props.columns)
8267
8888
  });
8889
+ const {
8890
+ displayColumns: sourceDisplayColumns,
8891
+ columnFadeAttr,
8892
+ columnFadeStyle,
8893
+ columnFadeAttrs,
8894
+ columnFadeActive,
8895
+ fadeByLeaf,
8896
+ isCollapsed: isColumnFadeCollapsed
8897
+ } = columnFade;
8268
8898
  const detectedTypes = ref({});
8269
8899
  const detectTypesDone = ref(false);
8270
8900
  const detectedDisplayColumns = computed(() => {
@@ -8280,13 +8910,17 @@ var IrisTable = defineComponent({
8280
8910
  const width = effectiveWidths.value[column.key] ?? resolveInitialWidth(column);
8281
8911
  return Number.isFinite(width) && width >= 0 ? width : resolveInitialWidth(column);
8282
8912
  };
8913
+ const orderedDisplayColumns = computed(
8914
+ () => applyTableColumnOrder(detectedDisplayColumns.value, effectiveColumnOrder.value)
8915
+ );
8283
8916
  const responsiveResult = computed(
8284
8917
  () => props.responsive ? computeResponsiveTableColumns(
8285
- detectedDisplayColumns.value,
8918
+ orderedDisplayColumns.value,
8286
8919
  responsiveWidth.value,
8287
8920
  responsiveLeadingWidth.value,
8288
- responsiveWidthOf
8289
- ) : { columns: detectedDisplayColumns.value, overflow: false }
8921
+ responsiveWidthOf,
8922
+ pinOf
8923
+ ) : { columns: orderedDisplayColumns.value, overflow: false }
8290
8924
  );
8291
8925
  const responsiveDisplayColumns = computed(
8292
8926
  () => responsiveResult.value.columns
@@ -8339,6 +8973,7 @@ var IrisTable = defineComponent({
8339
8973
  const tableData = computed(
8340
8974
  () => proxyCtrl.proxy.value ? proxyCtrl.liveData.value : localRowsOverride.value ?? props.data ?? []
8341
8975
  );
8976
+ const committedList = createCommittedList(() => tableData.value);
8342
8977
  watch(
8343
8978
  [() => props.autoDetectTypes, tableData, () => props.columns],
8344
8979
  () => {
@@ -8348,38 +8983,76 @@ var IrisTable = defineComponent({
8348
8983
  for (const column of flattenLeafColumns(props.columns)) {
8349
8984
  if (column.formula) continue;
8350
8985
  next[column.key] = detectColumnType(
8351
- tableData.value.map((row) => getCellValue(row, column))
8986
+ tableData.value.map((row) => getCellValue2(row, column))
8352
8987
  );
8353
8988
  }
8354
8989
  detectedTypes.value = next;
8355
8990
  },
8356
8991
  { immediate: true }
8357
8992
  );
8358
- const {
8359
- sortState: internalSort,
8360
- cycleSort,
8361
- setSort,
8362
- sortComparator,
8363
- sortedData: sortedRows,
8364
- multiSortState,
8365
- cycleMultiSort
8366
- } = useTableSort(tableData, {
8367
- leafColumns,
8368
- sort: computed(() => props.sort),
8993
+ let recordUndoRows = null;
8994
+ let suppressUndoRecord = false;
8995
+ const readRowChildren = (row) => {
8996
+ if (props.lazyLoad !== void 0) {
8997
+ const children = row.children;
8998
+ if (Array.isArray(children)) return children;
8999
+ }
9000
+ return props.getSubRows?.(row);
9001
+ };
9002
+ const writeLazyChildren = props.lazyLoad === void 0 ? void 0 : (row, children) => ({
9003
+ ...row,
9004
+ children
9005
+ });
9006
+ const sorting = useGridSorting(gridCore, {
9007
+ mode: props.multiSort ? "multiple" : "single",
8369
9008
  defaultSort: props.defaultSort,
9009
+ defaultMultiSort: props.defaultMultiSort,
8370
9010
  onSortChange: (next) => {
8371
9011
  emit("update:sort", next);
8372
9012
  if (remoteSort.value) proxyCtrl.setParams({ sort: next });
8373
9013
  },
8374
- multiSort: () => props.multiSort,
8375
- multiSortState: () => props.multiSortState,
8376
- defaultMultiSort: props.defaultMultiSort,
8377
9014
  onMultiSortChange: (next) => {
8378
9015
  emit("update:multiSortState", next);
8379
9016
  emit("multiSortChange", next);
8380
9017
  if (remoteSort.value) proxyCtrl.setParams({ sorts: next });
8381
9018
  }
8382
9019
  });
9020
+ watch(
9021
+ () => props.sort,
9022
+ (next) => next !== void 0 && sorting.model.syncSort(next ?? null),
9023
+ { immediate: true }
9024
+ );
9025
+ watch(
9026
+ () => props.multiSortState,
9027
+ (next) => next !== void 0 && sorting.model.syncMultiSort(next ?? []),
9028
+ { immediate: true }
9029
+ );
9030
+ const internalSort = computed(
9031
+ () => props.sort !== void 0 ? props.sort ?? null : sorting.sort.value
9032
+ );
9033
+ const multiSortState = computed(
9034
+ () => props.multiSortState !== void 0 ? props.multiSortState : sorting.multiSort.value
9035
+ );
9036
+ const setSort = (next) => sorting.model.setSort(next);
9037
+ const cycleSort = (column) => {
9038
+ if (column.sortable) sorting.model.cycleSort(column.key);
9039
+ };
9040
+ const cycleMultiSort = (column) => {
9041
+ if (column.sortable) sorting.model.cycleMultiSort(column.key);
9042
+ };
9043
+ const sortComparator = computed(
9044
+ () => buildMultiSortComparator(
9045
+ leafColumns.value,
9046
+ internalSort.value ? [internalSort.value] : [],
9047
+ props.formulaTables
9048
+ )
9049
+ );
9050
+ const multiSortComparator = computed(() => buildMultiSortComparator(leafColumns.value, multiSortState.value, props.formulaTables));
9051
+ const sortedRows = computed(() => {
9052
+ if (remoteSort.value) return tableData.value;
9053
+ const compare = props.multiSort ? multiSortComparator.value : sortComparator.value;
9054
+ return compare ? [...tableData.value].sort(compare) : tableData.value;
9055
+ });
8383
9056
  const tableViews = createTableViewsController({
8384
9057
  config: () => props.views,
8385
9058
  sort: internalSort,
@@ -8387,6 +9060,34 @@ var IrisTable = defineComponent({
8387
9060
  onActiveViewChange: (key) => props.onActiveViewChange?.(key)
8388
9061
  });
8389
9062
  const sortedData = computed(() => remoteSort.value ? tableData.value : sortedRows.value);
9063
+ const filtering = useGridFiltering(gridCore, {
9064
+ defaultFilters: props.filters,
9065
+ defaultFilterValues: props.filterValues,
9066
+ onFilterValuesChange: (next) => props.onFilterValuesChange?.(next)
9067
+ });
9068
+ watch(
9069
+ () => props.filters,
9070
+ (next) => next !== void 0 && filtering.model.syncFilters(next),
9071
+ { immediate: true }
9072
+ );
9073
+ watch(
9074
+ () => props.filterValues,
9075
+ (next) => next !== void 0 && filtering.model.syncFilterValues(next),
9076
+ { immediate: true }
9077
+ );
9078
+ const effectiveFilters = computed(
9079
+ () => props.filters !== void 0 ? props.filters : filtering.filters.value
9080
+ );
9081
+ const effectiveFilterValues = computed(
9082
+ () => props.filterValues !== void 0 ? props.filterValues : filtering.filterValues.value
9083
+ );
9084
+ const recent = createRecentFilters();
9085
+ const recentEntries = shallowRef(recent.list());
9086
+ onScopeDispose(
9087
+ recent.subscribe(() => {
9088
+ recentEntries.value = recent.list();
9089
+ })
9090
+ );
8390
9091
  watch([internalSort, () => props.multiSort, remoteSort, () => proxyCtrl.proxy.value], () => {
8391
9092
  if (!proxyCtrl.proxy.value || !remoteSort.value || props.multiSort) return;
8392
9093
  proxyCtrl.setParams({ sort: internalSort.value });
@@ -8412,7 +9113,7 @@ var IrisTable = defineComponent({
8412
9113
  if (formDraft.value[key] === value) return;
8413
9114
  formDraft.value = { ...formDraft.value, [key]: value };
8414
9115
  };
8415
- const mergedProxyFilters = (form) => mergeFilterValues(mergeFormFilters(props.filters ?? {}, form), props.filterValues ?? {});
9116
+ const mergedProxyFilters = (form) => mergeFilterValues(mergeFormFilters(effectiveFilters.value, form), effectiveFilterValues.value);
8416
9117
  const handleFormSubmit = () => {
8417
9118
  const values = buildFormValues(props.formConfig?.fields, formDraft.value);
8418
9119
  props.formConfig?.onSearch?.(values);
@@ -8434,7 +9135,7 @@ var IrisTable = defineComponent({
8434
9135
  }
8435
9136
  };
8436
9137
  watch(
8437
- [formApplied, () => props.filters, () => props.filterValues],
9138
+ [formApplied, effectiveFilters, effectiveFilterValues],
8438
9139
  () => {
8439
9140
  if (proxyCtrl.proxy.value && remoteFilter.value) {
8440
9141
  proxyCtrl.setParams({
@@ -8444,22 +9145,39 @@ var IrisTable = defineComponent({
8444
9145
  },
8445
9146
  { immediate: true }
8446
9147
  );
9148
+ const gridRows = useGridRows(gridCore, tableData.value, {
9149
+ getRowKey: (row, index) => rowId(row, index),
9150
+ getChildren: props.getSubRows !== void 0 || props.lazyLoad !== void 0 ? readRowChildren : void 0,
9151
+ setChildren: writeLazyChildren,
9152
+ onRowsChange: (transaction) => {
9153
+ const next = [...transaction.rows];
9154
+ committedList.sync(next);
9155
+ if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = next;
9156
+ else localRowsOverride.value = next;
9157
+ if (!suppressUndoRecord) recordUndoRows?.(next);
9158
+ }
9159
+ });
9160
+ watch(tableData, (rows) => {
9161
+ committedList.sync(rows);
9162
+ gridRows.model.sync(rows);
9163
+ });
8447
9164
  const selControlled = computed(() => props.selection !== void 0);
8448
- const selectionModel = createSelectionModel({
8449
- defaultSelected: props.selection ?? props.defaultSelection ?? [],
9165
+ const { model: selectionModel, selection: selectedKeys } = useGridSelection(gridCore, {
9166
+ mode: "multiple",
9167
+ value: props.selection,
9168
+ defaultValue: props.defaultSelection,
8450
9169
  onChange: (keys) => emit("update:selection", keys)
8451
9170
  });
8452
- const selectedKeys = shallowRef(selectionModel.get());
8453
- onBeforeUnmount(
8454
- selectionModel.store.subscribe((keys) => {
8455
- selectedKeys.value = keys;
8456
- })
8457
- );
8458
9171
  watch(
8459
9172
  () => props.selection,
8460
- (sel) => {
8461
- if (sel !== void 0) selectionModel.sync(sel);
8462
- }
9173
+ (selection) => {
9174
+ if (selection === void 0) return;
9175
+ const current = selectionModel.get();
9176
+ if (current.length !== selection.length || current.some((key, index) => !Object.is(key, selection[index]))) {
9177
+ selectionModel.sync(selection);
9178
+ }
9179
+ },
9180
+ { immediate: true }
8463
9181
  );
8464
9182
  const displaySelection = computed(
8465
9183
  () => selControlled.value ? props.selection : selectedKeys.value
@@ -8468,17 +9186,11 @@ var IrisTable = defineComponent({
8468
9186
  if (selControlled.value) selectionModel.sync(props.selection);
8469
9187
  };
8470
9188
  const hasDetail = computed(() => props.renderDetail !== void 0);
8471
- const expansion = createExpansion({
9189
+ const { model: expansion, expandedKeys } = useGridExpansion(gridCore, {
8472
9190
  mode: "multiple",
8473
- defaultExpanded: (props.defaultExpandedRowKeys ?? []).map(String),
9191
+ defaultValue: (props.defaultExpandedRowKeys ?? []).map(String),
8474
9192
  onChange: (keys) => emit("expandedRowsChange", keys)
8475
9193
  });
8476
- const expandedKeys = shallowRef(expansion.get());
8477
- onBeforeUnmount(
8478
- expansion.store.subscribe((keys) => {
8479
- expandedKeys.value = keys;
8480
- })
8481
- );
8482
9194
  const isRowExpandable = (row, idx) => hasDetail.value && (props.rowExpandable ? props.rowExpandable(row, idx) : true);
8483
9195
  const rowId = (row, index) => {
8484
9196
  const v = row[props.rowKey];
@@ -8497,21 +9209,17 @@ var IrisTable = defineComponent({
8497
9209
  auditRowsRef.value = next;
8498
9210
  };
8499
9211
  const treeMode = computed(() => props.getSubRows !== void 0 || props.lazyLoad !== void 0);
8500
- const lazyChildren = ref(/* @__PURE__ */ new Map());
8501
9212
  const lazyLoading = ref(/* @__PURE__ */ new Set());
8502
9213
  let lazyEpoch = 0;
8503
9214
  watch(
8504
9215
  () => proxyCtrl.proxy.value ? proxyCtrl.state.value.data : props.data,
8505
9216
  () => {
8506
- lazyChildren.value = /* @__PURE__ */ new Map();
8507
9217
  lazyLoading.value = /* @__PURE__ */ new Set();
8508
9218
  lazyEpoch += 1;
8509
9219
  }
8510
9220
  );
8511
- const lazyChildrenOf = (row) => {
8512
- const key = String(row[props.rowKey]);
8513
- return lazyChildren.value.get(key) ?? props.getSubRows?.(row);
8514
- };
9221
+ const lazyChildrenOf = readRowChildren;
9222
+ const hasLazyChildren = (row) => props.lazyLoad !== void 0 && Array.isArray(row.children);
8515
9223
  const flatTree = computed(
8516
9224
  () => treeMode.value ? flattenTree(sortedData.value, {
8517
9225
  getKey: (r) => String(r[props.rowKey]),
@@ -8524,9 +9232,9 @@ var IrisTable = defineComponent({
8524
9232
  );
8525
9233
  const filteredData = computed(() => {
8526
9234
  if (remoteFilter.value) return sortedData.value;
8527
- const merged = proxyCtrl.proxy.value ? props.filters ?? {} : mergeFormFilters(props.filters ?? {}, formApplied.value);
9235
+ const merged = proxyCtrl.proxy.value ? effectiveFilters.value : mergeFormFilters(effectiveFilters.value, formApplied.value);
8528
9236
  const active = Object.entries(merged).filter(([, v]) => v != null && v !== "");
8529
- const checkedEntries = Object.entries(props.filterValues ?? {}).filter(
9237
+ const checkedEntries = Object.entries(effectiveFilterValues.value).filter(
8530
9238
  ([, values]) => values.length > 0
8531
9239
  );
8532
9240
  if (active.length === 0 && checkedEntries.length === 0) return sortedData.value;
@@ -8534,12 +9242,12 @@ var IrisTable = defineComponent({
8534
9242
  const textOk = active.every(([key, value]) => {
8535
9243
  const col = displayColumns.value.find((c) => c.key === key);
8536
9244
  if (!col) return true;
8537
- return String(getCellValue(row, col) ?? "").toLowerCase().includes(value.toLowerCase());
9245
+ return String(getCellValue2(row, col) ?? "").toLowerCase().includes(value.toLowerCase());
8538
9246
  });
8539
9247
  const setsOk = checkedEntries.every(([key, values]) => {
8540
9248
  const col = displayColumns.value.find((c) => c.key === key);
8541
9249
  if (!col) return true;
8542
- return values.includes(String(getCellValue(row, col) ?? ""));
9250
+ return values.includes(String(getCellValue2(row, col) ?? ""));
8543
9251
  });
8544
9252
  return textOk && setsOk;
8545
9253
  });
@@ -8547,28 +9255,48 @@ var IrisTable = defineComponent({
8547
9255
  const bodyData = computed(
8548
9256
  () => flatTree.value ? flatTree.value.map((t2) => t2.row) : filteredData.value
8549
9257
  );
9258
+ const reconcileClipboardRows = (sourceRows, previousRows, rows) => {
9259
+ const visibleKeys = /* @__PURE__ */ new Map();
9260
+ bodyData.value.forEach((row, index) => {
9261
+ visibleKeys.set(row, rowId(row, index));
9262
+ });
9263
+ const keyOf = (row, index, source) => {
9264
+ const visibleKey = visibleKeys.get(row);
9265
+ if (visibleKey !== void 0) return visibleKey;
9266
+ const sourceIndex = source?.indexOf(row) ?? -1;
9267
+ return rowId(row, sourceIndex >= 0 ? sourceIndex : index);
9268
+ };
9269
+ const patches = /* @__PURE__ */ new Map();
9270
+ rows.forEach((row, index) => {
9271
+ if (Object.is(row, previousRows[index])) return;
9272
+ const previous = previousRows[index];
9273
+ if (!previous) return;
9274
+ const sourceIndex = sourceRows.indexOf(previous);
9275
+ patches.set(keyOf(previous, sourceIndex >= 0 ? sourceIndex : index, sourceRows), row);
9276
+ });
9277
+ if (props.getSubRows !== void 0 || props.lazyLoad !== void 0) {
9278
+ return reconcileTreeRows(sourceRows, patches, {
9279
+ getRowKey: (row, index) => keyOf(row, index),
9280
+ getChildren: readRowChildren,
9281
+ setChildren: writeLazyChildren
9282
+ });
9283
+ }
9284
+ return sourceRows.map((row, index) => patches.get(keyOf(row, index, sourceRows)) ?? row);
9285
+ };
8550
9286
  const cascadingTreeSelection = computed(
8551
9287
  () => props.treeSelectionCascade && props.selectable === "multi" && treeMode.value
8552
9288
  );
8553
9289
  const treeSelectionNodes = computed(() => {
8554
- const getChildren = props.getSubRows;
9290
+ const getChildren = props.getSubRows !== void 0 || props.lazyLoad !== void 0 ? readRowChildren : void 0;
8555
9291
  if (!cascadingTreeSelection.value || !getChildren) return [];
8556
- const nodes = [];
8557
- const seen = /* @__PURE__ */ new Set();
8558
9292
  let rowIndex = 0;
8559
- const walk = (rows, parentKey) => {
8560
- for (const row of rows) {
8561
- const key = rowId(row, rowIndex);
8562
- rowIndex += 1;
8563
- if (seen.has(key)) continue;
8564
- seen.add(key);
8565
- nodes.push({ key, parentKey });
8566
- const children = getChildren(row);
8567
- if (children && children.length > 0) walk(children, key);
8568
- }
8569
- };
8570
- walk(sortedData.value);
8571
- return nodes;
9293
+ return flattenTreeSelectionNodes(sortedData.value, {
9294
+ // Keep the legacy fallback key's global pre-order ordinal. The core
9295
+ // helper owns recursion and duplicate/cycle protection; this closure
9296
+ // only supplies the table's existing row-id policy.
9297
+ getKey: (row) => rowId(row, rowIndex++),
9298
+ getChildren
9299
+ });
8572
9300
  });
8573
9301
  const compactTreeSelectionSeed = (keys) => {
8574
9302
  const selected = new Set(keys);
@@ -8639,51 +9367,8 @@ var IrisTable = defineComponent({
8639
9367
  }
8640
9368
  selectionModel.set([...allRowIds.value]);
8641
9369
  };
8642
- const editingCellId = ref(null);
8643
- const editingColumnKey = ref(null);
8644
- const editingDraft = ref("");
8645
- const editError = ref(null);
8646
9370
  const editorInputRef = ref(null);
8647
- const beginEdit = (row, column, rowIdent) => {
8648
- if (!isEditableColumn(column)) return;
8649
- editingCellId.value = cellId(rowIdent, column.key);
8650
- editingColumnKey.value = column.key;
8651
- const current = getCellValue(row, column);
8652
- editingDraft.value = current == null ? "" : String(current);
8653
- editError.value = null;
8654
- void nextTick(() => editorInputRef.value?.focus());
8655
- };
8656
- const commitEdit = (row, column, rowIndex) => {
8657
- if (editingCellId.value === null) return;
8658
- const oldValue = getCellValue(row, column);
8659
- const draft = editingDraft.value;
8660
- const newValue = column.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? oldValue : Number(draft) : draft;
8661
- if (column.editRules && column.editRules.length > 0) {
8662
- const context = { rows: tableData.value, columnKey: column.key };
8663
- validateEditRulesAsync(column.editRules, draft, row, false, context).then((r) => {
8664
- if (!r.valid) {
8665
- editError.value = r.messages[0] ?? null;
8666
- return;
8667
- }
8668
- finishCommit(row, column, rowIndex, oldValue, newValue);
8669
- });
8670
- return;
8671
- }
8672
- if (column.validate) {
8673
- const error = column.validate(newValue, row);
8674
- if (error) {
8675
- editError.value = error;
8676
- return;
8677
- }
8678
- }
8679
- finishCommit(row, column, rowIndex, oldValue, newValue);
8680
- editError.value = null;
8681
- editingCellId.value = null;
8682
- if (newValue !== oldValue) {
8683
- emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
8684
- }
8685
- };
8686
- const writeCellValue = (row, column, rowIndex, oldValue, newValue) => {
9371
+ const recordCellCommit = (row, column, rowIndex, oldValue, newValue) => {
8687
9372
  if (newValue === oldValue) return;
8688
9373
  if (props.auditLog) {
8689
9374
  audit.push({
@@ -8695,43 +9380,95 @@ var IrisTable = defineComponent({
8695
9380
  });
8696
9381
  }
8697
9382
  emit("cellEdit", { row, column, oldValue, newValue, rowIndex });
8698
- if (proxyCtrl.proxy.value) {
8699
- const id = rowId(row, rowIndex);
8700
- const idx = proxyCtrl.liveData.value.findIndex((r, i) => rowId(r, i) === id);
8701
- if (idx >= 0) {
8702
- const key = column.dataIndex ?? column.key;
8703
- const next = { ...proxyCtrl.liveData.value[idx], [key]: newValue };
8704
- proxyCtrl.liveData.value = [
8705
- ...proxyCtrl.liveData.value.slice(0, idx),
8706
- next,
8707
- ...proxyCtrl.liveData.value.slice(idx + 1)
8708
- ];
9383
+ };
9384
+ const cellEditing = useGridEditing(gridCore, {
9385
+ getRowKey: (row, index) => rowId(row, index),
9386
+ getRowIndex: (rowKey) => {
9387
+ const index = bodyData.value.findIndex(
9388
+ (row, rowIndex) => Object.is(rowId(row, rowIndex), rowKey)
9389
+ );
9390
+ return index >= 0 ? index : void 0;
9391
+ },
9392
+ getRules: (columnKey) => leafColumns.value.find((column) => column.key === columnKey)?.editRules,
9393
+ getValue: (row, columnKey) => {
9394
+ const column = leafColumns.value.find((candidate) => candidate.key === columnKey);
9395
+ return column ? getCellValue2(row, column) : row[columnKey];
9396
+ },
9397
+ setValue: (row, columnKey, value) => {
9398
+ const column = leafColumns.value.find((candidate) => candidate.key === columnKey);
9399
+ const key = column?.dataIndex ?? column?.key ?? columnKey;
9400
+ return { ...row, [key]: value };
9401
+ },
9402
+ coerce: (draft, row, columnKey) => {
9403
+ const column = leafColumns.value.find((candidate) => candidate.key === columnKey);
9404
+ if (column?.editor !== "number") return draft;
9405
+ const text = String(draft ?? "");
9406
+ if (text === "" || Number.isNaN(Number(text))) {
9407
+ return column ? getCellValue2(row, column) : draft;
8709
9408
  }
9409
+ return Number(text);
9410
+ },
9411
+ validate: (value, row, columnKey) => {
9412
+ const column = leafColumns.value.find((candidate) => candidate.key === columnKey);
9413
+ return column?.validate?.(value, row) ?? null;
9414
+ },
9415
+ isEditable: (_row, columnKey) => {
9416
+ const column = leafColumns.value.find((candidate) => candidate.key === columnKey);
9417
+ return Boolean(column && isEditableColumn(column));
9418
+ },
9419
+ onCommit: (commit) => {
9420
+ const column = leafColumns.value.find((candidate) => candidate.key === commit.columnKey);
9421
+ if (column)
9422
+ recordCellCommit(commit.row, column, commit.rowIndex, commit.oldValue, commit.value);
9423
+ }
9424
+ });
9425
+ const editingState = cellEditing.state;
9426
+ const editingCellId = computed(() => {
9427
+ const target = editingState.value.editing;
9428
+ return target ? cellId(target.rowKey, target.columnKey) : null;
9429
+ });
9430
+ const editingColumnKey = computed(() => editingState.value.editing?.columnKey ?? null);
9431
+ const editingDraft = computed(() => String(editingState.value.draft ?? ""));
9432
+ const editError = computed(() => editingState.value.error);
9433
+ const setEditingDraft = (draft) => cellEditing.setCellDraft(draft);
9434
+ const beginEdit = (row, column, rowIdent) => {
9435
+ if (!isEditableColumn(column)) return;
9436
+ const current = getCellValue2(row, column);
9437
+ if (cellEditing.startCellEdit(rowIdent, column.key, current == null ? "" : String(current))) {
9438
+ void nextTick(() => editorInputRef.value?.focus());
8710
9439
  }
8711
9440
  };
8712
- const finishCommit = (row, column, rowIndex, oldValue, newValue) => {
8713
- editError.value = null;
8714
- editingCellId.value = null;
8715
- editingColumnKey.value = null;
8716
- writeCellValue(row, column, rowIndex, oldValue, newValue);
9441
+ const commitEdit = (_row, _column, _rowIndex) => {
9442
+ cellEditing.commitCellEdit();
8717
9443
  };
8718
- const cancelEdit = () => {
8719
- editError.value = null;
8720
- editingCellId.value = null;
8721
- editingColumnKey.value = null;
9444
+ const cancelEdit = () => cellEditing.cancelCellEdit();
9445
+ const writeCellValue = (row, column, rowIndex, oldValue, newValue) => {
9446
+ if (newValue === oldValue) return;
9447
+ recordCellCommit(row, column, rowIndex, oldValue, newValue);
9448
+ const current = committedList.list();
9449
+ const key = column.dataIndex ?? column.key;
9450
+ const next = replaceTableCell(current, rowId(row, rowIndex), key, newValue, rowId);
9451
+ if (next !== current) {
9452
+ undoController.record(next);
9453
+ setTableRows(next);
9454
+ }
8722
9455
  };
8723
9456
  const rowMode = computed(() => props.editConfig?.mode === "row");
8724
9457
  const rowSessions = ref(/* @__PURE__ */ new Map());
8725
9458
  const rowEditing = ref(null);
8726
9459
  const rowEditorRefs = /* @__PURE__ */ new Map();
8727
- const currentRowFor = (k) => bodyData.value.find((r, i) => rowId(r, i) === k);
9460
+ const currentRowFor = (k) => (
9461
+ // Static tree children are indexed by the shared Core rows model. Keep
9462
+ // the visible-body fallback for proxy/lazy rows owned by this adapter.
9463
+ committedList.list().find((r, i) => rowId(r, i) === k) ?? gridRows.model.find(k) ?? bodyData.value.find((r, i) => rowId(r, i) === k)
9464
+ );
8728
9465
  const beginRowEdit = (row, rowIndex, focusColKey) => {
8729
9466
  const k = rowId(row, rowIndex);
8730
9467
  const editableCols = leafColumns.value.filter(isEditableColumn);
8731
9468
  if (editableCols.length === 0) return;
8732
9469
  const sessions = /* @__PURE__ */ new Map();
8733
9470
  for (const col of editableCols) {
8734
- const current = getCellValue(row, col);
9471
+ const current = getCellValue2(row, col);
8735
9472
  sessions.set(cellId(k, col.key), {
8736
9473
  draft: current == null ? "" : String(current),
8737
9474
  error: null
@@ -8747,7 +9484,7 @@ var IrisTable = defineComponent({
8747
9484
  if (!session) return;
8748
9485
  const row = currentRowFor(k);
8749
9486
  if (!row) return;
8750
- const oldValue = getCellValue(row, column);
9487
+ const oldValue = getCellValue2(row, column);
8751
9488
  const draft = session.draft;
8752
9489
  const newValue = column.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? oldValue : Number(draft) : draft;
8753
9490
  if (column.editRules && column.editRules.length > 0) {
@@ -8799,7 +9536,7 @@ var IrisTable = defineComponent({
8799
9536
  if (rowEditing.value?.k === k) {
8800
9537
  const id = cellId(k, col.key);
8801
9538
  if (isEditableColumn(col) && !rowSessions.value.has(id)) {
8802
- const current = getCellValue(row, col);
9539
+ const current = getCellValue2(row, col);
8803
9540
  rowSessions.value.set(id, {
8804
9541
  draft: current == null ? "" : String(current),
8805
9542
  error: null
@@ -8814,6 +9551,7 @@ var IrisTable = defineComponent({
8814
9551
  rowEditorRefs,
8815
9552
  editorInputRef,
8816
9553
  editingDraft,
9554
+ setEditingDraft,
8817
9555
  editError,
8818
9556
  commitEdit,
8819
9557
  cancelEdit,
@@ -8821,7 +9559,7 @@ var IrisTable = defineComponent({
8821
9559
  cancelRowEdit,
8822
9560
  editPreview: props.editPreview,
8823
9561
  previewValue: (row, col, draft) => {
8824
- const raw = col.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? getCellValue(row, col) : Number(draft) : draft;
9562
+ const raw = col.editor === "number" ? draft === "" || Number.isNaN(Number(draft)) ? getCellValue2(row, col) : Number(draft) : draft;
8825
9563
  return col.formatter?.(applyTableMask(raw, col), row) ?? "";
8826
9564
  }
8827
9565
  };
@@ -8838,7 +9576,7 @@ var IrisTable = defineComponent({
8838
9576
  paddingLeft: `${treeMeta.depth * 16}px`
8839
9577
  }
8840
9578
  },
8841
- treeMeta.hasChildren || props.lazyLoad !== void 0 && !lazyChildren.value.has(treeMeta.key) ? [
9579
+ treeMeta.hasChildren || props.lazyLoad !== void 0 && !hasLazyChildren(row) ? [
8842
9580
  h(
8843
9581
  "button",
8844
9582
  {
@@ -8864,8 +9602,18 @@ var IrisTable = defineComponent({
8864
9602
  const epoch = lazyEpoch;
8865
9603
  props.lazyLoad(row, (children) => {
8866
9604
  if (epoch !== lazyEpoch) return;
8867
- lazyChildren.value.set(treeMeta.key, children);
8868
- if (children && children.length > 0) {
9605
+ const rawKey = row[props.rowKey];
9606
+ const lazyKey = typeof rawKey === "string" || typeof rawKey === "number" ? rawKey : rowId(row, Math.max(0, treeMeta.posInset - 1));
9607
+ suppressUndoRecord = true;
9608
+ let committed = false;
9609
+ try {
9610
+ committed = gridRows.model.setChildren(lazyKey, children, {
9611
+ reason: "lazy-load"
9612
+ });
9613
+ } finally {
9614
+ suppressUndoRecord = false;
9615
+ }
9616
+ if (committed && children && children.length > 0) {
8869
9617
  expansion.toggle(treeMeta.key);
8870
9618
  }
8871
9619
  clearLoading();
@@ -8898,21 +9646,30 @@ var IrisTable = defineComponent({
8898
9646
  watch(
8899
9647
  () => leafColumns.value,
8900
9648
  (cols) => {
8901
- const seeded = { ...internalWidths.value };
8902
- let changed = false;
9649
+ const seeded = { ...uncontrolledWidths.value };
9650
+ let snapshotChanged = false;
8903
9651
  for (const col of cols) {
8904
9652
  if (seeded[col.key] === void 0) {
8905
9653
  seeded[col.key] = resolveInitialWidth(col);
8906
- changed = true;
9654
+ snapshotChanged = true;
9655
+ }
9656
+ }
9657
+ if (snapshotChanged) uncontrolledWidths.value = seeded;
9658
+ const modelWidths = { ...columnsFeature.state.value.widths };
9659
+ let modelChanged = false;
9660
+ for (const col of cols) {
9661
+ if (modelWidths[col.key] === void 0) {
9662
+ modelWidths[col.key] = resolveInitialWidth(col);
9663
+ modelChanged = true;
8907
9664
  }
8908
9665
  }
8909
- if (changed) internalWidths.value = seeded;
9666
+ if (modelChanged) columnsFeature.model.syncWidths(modelWidths);
8910
9667
  },
8911
9668
  { immediate: true, deep: false }
8912
9669
  );
8913
9670
  const setColumnWidths = (next) => {
8914
- if (props.columnWidths === void 0) internalWidths.value = next;
8915
- emit("update:columnWidths", next);
9671
+ if (props.columnWidths === void 0) uncontrolledWidths.value = next;
9672
+ columnsFeature.setWidths(next);
8916
9673
  };
8917
9674
  const onHeaderClick = (column) => {
8918
9675
  if (props.multiSort) cycleMultiSort(column);
@@ -8948,7 +9705,7 @@ var IrisTable = defineComponent({
8948
9705
  const renderFilterTrigger = (col, leaf) => renderTableFilterTrigger({
8949
9706
  column: col,
8950
9707
  leaf,
8951
- active: (props.filterValues?.[col.key]?.length ?? 0) > 0,
9708
+ active: (effectiveFilterValues.value[col.key]?.length ?? 0) > 0,
8952
9709
  open: filterPanelState.value?.open === true && filterPanelState.value.colKey === col.key,
8953
9710
  label: t("table.filter"),
8954
9711
  onOpen: openFilterPanel
@@ -8961,18 +9718,136 @@ var IrisTable = defineComponent({
8961
9718
  if (hasDetail.value) parts.push(`${EXPAND_COL_WIDTH}px`);
8962
9719
  if (props.selectable !== "none") parts.push(`${SELECTION_COL_WIDTH}px`);
8963
9720
  for (const col of leafColumns.value) {
9721
+ if (isColumnFadeCollapsed(col.key)) {
9722
+ parts.push("0px");
9723
+ continue;
9724
+ }
8964
9725
  parts.push(`${effectiveWidths.value[col.key] ?? resolveInitialWidth(col)}px`);
8965
9726
  }
8966
9727
  return parts.join(" ");
8967
9728
  });
8968
- const cellRangeCtrl = createCellRange();
8969
- const cellRangeState = shallowRef(cellRangeCtrl.getState());
8970
- onBeforeUnmount(
8971
- cellRangeCtrl.subscribe((s) => {
8972
- cellRangeState.value = s;
8973
- })
8974
- );
9729
+ const { model: cellRangeCtrl, state: cellRangeState } = useGridRange(gridCore);
9730
+ const { serialize: serializeGridRange, paste: pasteGridRange } = useGridClipboard(gridCore, {
9731
+ getRows: () => bodyData.value,
9732
+ getColumns: () => leafColumns.value,
9733
+ rowKeyField: props.rowKey,
9734
+ resolveValue: (row, column) => getCellValue2(row, column),
9735
+ setValue: (row, column, value) => ({
9736
+ ...row,
9737
+ [column.dataIndex ?? column.key]: value
9738
+ }),
9739
+ isCellEditable: (_row, column) => !column.formula,
9740
+ reconcileRows: reconcileClipboardRows,
9741
+ onPaste: (change) => {
9742
+ const rows = [...change.rows];
9743
+ recordAudit(rows, "paste");
9744
+ props.onDataChange?.(rows);
9745
+ }
9746
+ });
8975
9747
  const rootRef = ref(null);
9748
+ let tableDisposed = false;
9749
+ onScopeDispose(() => {
9750
+ tableDisposed = true;
9751
+ });
9752
+ const setTableRows = (rows) => {
9753
+ committedList.sync(rows);
9754
+ suppressUndoRecord = true;
9755
+ try {
9756
+ gridRows.model.commit(rows);
9757
+ } finally {
9758
+ suppressUndoRecord = false;
9759
+ }
9760
+ };
9761
+ const undoController = createTableUndoController(
9762
+ () => props.undo,
9763
+ () => props.data ?? [],
9764
+ () => proxyCtrl.proxy.value ? proxyCtrl.state.value.data : props.data ?? [],
9765
+ setTableRows,
9766
+ recordAudit,
9767
+ (rows) => props.onDataChange?.(rows),
9768
+ () => rootRef.value,
9769
+ () => editingCellId.value !== null || rowEditing.value !== null,
9770
+ {
9771
+ current: () => displaySelection.value,
9772
+ enabled: () => props.selectable !== "none",
9773
+ keyOf: rowId,
9774
+ rebase: rebaseToProp,
9775
+ set: (keys) => selectionModel.set(keys)
9776
+ }
9777
+ );
9778
+ recordUndoRows = undoController.record;
9779
+ const scrollTopShown = ref(false);
9780
+ let scrollTopListeners = [];
9781
+ const clearScrollTopListeners = () => {
9782
+ for (const el of scrollTopListeners) el.removeEventListener("scroll", onScrollTop);
9783
+ scrollTopListeners = [];
9784
+ };
9785
+ const onScrollTop = () => {
9786
+ const root = rootRef.value;
9787
+ if (!root) return;
9788
+ const viewport = root.querySelector("[data-iris-virtual-scroll]");
9789
+ const scroller = viewport ?? root;
9790
+ scrollTopShown.value = scroller.scrollTop >= SCROLL_TOP_VISIBLE_PX;
9791
+ };
9792
+ const armScrollTop = () => {
9793
+ clearScrollTopListeners();
9794
+ scrollTopShown.value = false;
9795
+ if (!props.scrollToTop || !rootRef.value) return;
9796
+ const root = rootRef.value;
9797
+ const viewport = root.querySelector("[data-iris-virtual-scroll]");
9798
+ root.addEventListener("scroll", onScrollTop);
9799
+ scrollTopListeners.push(root);
9800
+ if (viewport) {
9801
+ viewport.addEventListener("scroll", onScrollTop);
9802
+ scrollTopListeners.push(viewport);
9803
+ }
9804
+ onScrollTop();
9805
+ };
9806
+ onMounted(armScrollTop);
9807
+ watch(
9808
+ [
9809
+ () => props.scrollToTop,
9810
+ () => Boolean(props.virtualScroll),
9811
+ bodyData,
9812
+ tableLoading,
9813
+ tableError
9814
+ ],
9815
+ armScrollTop,
9816
+ { flush: "post" }
9817
+ );
9818
+ onBeforeUnmount(clearScrollTopListeners);
9819
+ const scrollToTopOfTable = () => {
9820
+ const root = rootRef.value;
9821
+ if (!root) return;
9822
+ const viewport = root.querySelector("[data-iris-virtual-scroll]");
9823
+ const scroller = viewport ?? root;
9824
+ const behavior = prefersReducedMotion() ? "auto" : "smooth";
9825
+ if (typeof scroller.scrollTo === "function") {
9826
+ try {
9827
+ scroller.scrollTo({ top: 0, behavior });
9828
+ return;
9829
+ } catch {
9830
+ }
9831
+ }
9832
+ scroller.scrollTop = 0;
9833
+ };
9834
+ const buildBackTopSection = () => {
9835
+ if (!props.scrollToTop || !scrollTopShown.value || props.printable) return null;
9836
+ return h("div", { "data-iris-back-top-anchor": "", style: BACK_TOP_ANCHOR_STYLE }, [
9837
+ h(
9838
+ "button",
9839
+ {
9840
+ type: "button",
9841
+ "data-iris-back-top-table": "",
9842
+ "aria-label": t("backTop.label"),
9843
+ title: t("backTop.label"),
9844
+ onClick: scrollToTopOfTable,
9845
+ style: BACK_TOP_BUTTON_STYLE
9846
+ },
9847
+ "\u2191"
9848
+ )
9849
+ ]);
9850
+ };
8976
9851
  let responsiveObserver = null;
8977
9852
  const measureResponsiveWidth = () => {
8978
9853
  responsiveWidth.value = props.responsive && rootRef.value ? rootRef.value.clientWidth : 0;
@@ -9005,7 +9880,17 @@ var IrisTable = defineComponent({
9005
9880
  root: () => rootRef.value,
9006
9881
  focused: focusedCell,
9007
9882
  range: cellRangeCtrl,
9008
- rangeState: cellRangeState
9883
+ rangeState: cellRangeState,
9884
+ serializeRange: serializeGridRange,
9885
+ pasteRange: (range) => {
9886
+ const clipAtStart = props.clipConfig;
9887
+ if (!clipAtStart || clipAtStart.paste === false) return;
9888
+ void readClipboardText().then((text) => {
9889
+ const clip = props.clipConfig;
9890
+ if (text === null || tableDisposed || !clip || clip.paste === false) return;
9891
+ pasteGridRange(text, range);
9892
+ });
9893
+ }
9009
9894
  });
9010
9895
  const { handleRootKeyDown, isInRange, activeCellRange, copyActiveRange } = keyboard;
9011
9896
  const scrollLeft = ref(0);
@@ -9027,21 +9912,28 @@ var IrisTable = defineComponent({
9027
9912
  ro = null;
9028
9913
  });
9029
9914
  }
9030
- const visibleColSet = computed(
9031
- () => computeVisibleColSet(
9915
+ const visibleColSet = computed(() => {
9916
+ const visible = computeVisibleColSet(
9032
9917
  props.columnVirtualization,
9033
9918
  leafColumns.value,
9034
9919
  scrollLeft.value,
9035
9920
  viewportWidth.value,
9036
- effectiveWidths.value
9037
- )
9038
- );
9921
+ effectiveWidths.value,
9922
+ pinOf
9923
+ );
9924
+ if (!visible) return null;
9925
+ const next = new Set(visible);
9926
+ leafColumns.value.forEach((column, index) => {
9927
+ if (fadeByLeaf.value[column.key]) next.add(index);
9928
+ });
9929
+ return next;
9930
+ });
9039
9931
  const pinnedOffsets = computed(() => {
9040
9932
  const map = {};
9041
9933
  const widthOf = (col) => effectiveWidths.value[col.key] ?? resolveInitialWidth(col);
9042
9934
  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);
9043
9935
  for (const col of leafColumns.value) {
9044
- if (col.pinned === "left") {
9936
+ if (pinOf(col) === "left") {
9045
9937
  map[col.key] = { side: "left", offset: left };
9046
9938
  left += widthOf(col);
9047
9939
  }
@@ -9049,7 +9941,7 @@ var IrisTable = defineComponent({
9049
9941
  let right = 0;
9050
9942
  for (let i = leafColumns.value.length - 1; i >= 0; i -= 1) {
9051
9943
  const col = leafColumns.value[i];
9052
- if (col?.pinned === "right") {
9944
+ if (col && pinOf(col) === "right") {
9053
9945
  map[col.key] = { side: "right", offset: right };
9054
9946
  right += widthOf(col);
9055
9947
  }
@@ -9095,12 +9987,24 @@ var IrisTable = defineComponent({
9095
9987
  }
9096
9988
  });
9097
9989
  };
9990
+ const setColumnPinned = (key, side) => {
9991
+ if (props.pinnedColumns !== void 0) {
9992
+ columnsFeature.model.syncPinned(props.pinnedColumns);
9993
+ }
9994
+ columnsFeature.setPinned(key, side);
9995
+ };
9098
9996
  const pinnedDragHandle = createTablePinnedDrag({
9099
9997
  enabled: () => props.pinnedDrag === true,
9100
9998
  columns: () => leafColumns.value,
9101
9999
  widthOf: (column) => effectiveWidths.value[column.key] ?? resolveInitialWidth(column),
9102
- onColumnPinnedChange: props.onColumnPinnedChange,
9103
- onPinnedCountChange: props.onPinnedCountChange
10000
+ pinOf,
10001
+ setPinned: (key, side) => {
10002
+ if (pinnedPropControlled.value) {
10003
+ columnsFeature.model.syncPinned(props.pinnedColumns ?? {});
10004
+ columnsFeature.setPinned(key, side);
10005
+ } else props.onColumnPinnedChange?.(key, side);
10006
+ },
10007
+ onPinnedCountChange: (count) => props.onPinnedCountChange?.(count)
9104
10008
  });
9105
10009
  const rowDragCtrl = createSortable();
9106
10010
  const rowDragState = shallowRef(rowDragCtrl.getState());
@@ -9149,16 +10053,66 @@ var IrisTable = defineComponent({
9149
10053
  }
9150
10054
  const { activeId, overId } = rowDragCtrl.end();
9151
10055
  if (activeId !== null && overId !== null && activeId !== overId) {
9152
- const rows = [...bodyData.value];
9153
- const from = rows.findIndex((r, i) => String(rowId(r, i)) === activeId);
9154
- const to = rows.findIndex((r, i) => String(rowId(r, i)) === overId);
9155
- if (from >= 0 && to >= 0 && from !== to) {
9156
- const [moved] = rows.splice(from, 1);
9157
- rows.splice(to, 0, moved);
9158
- if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
9159
- else localRowsOverride.value = rows;
9160
- props.onDataChange?.(rows);
9161
- props.rowDrag.onReorder(rows);
10056
+ const visibleRows = bodyData.value;
10057
+ const fromVisible = visibleRows.findIndex(
10058
+ (row, index) => String(rowId(row, index)) === activeId
10059
+ );
10060
+ const toVisible = visibleRows.findIndex(
10061
+ (row, index) => String(rowId(row, index)) === overId
10062
+ );
10063
+ const fromRow = fromVisible >= 0 ? visibleRows[fromVisible] : void 0;
10064
+ const toRow = toVisible >= 0 ? visibleRows[toVisible] : void 0;
10065
+ const fromKey = fromRow === void 0 ? void 0 : rowId(fromRow, fromVisible);
10066
+ const toKey = toRow === void 0 ? void 0 : rowId(toRow, toVisible);
10067
+ const modelFrom = fromKey === void 0 ? void 0 : gridRows.model.find(fromKey);
10068
+ const modelTo = toKey === void 0 ? void 0 : gridRows.model.find(toKey);
10069
+ const useRowsModel = fromKey !== void 0 && toKey !== void 0 && modelFrom === fromRow && modelTo === toRow;
10070
+ if (useRowsModel) {
10071
+ const position = fromVisible < toVisible ? "after" : "before";
10072
+ if (gridRows.model.reorder(fromKey, toKey, { reason: "row-drag", position })) {
10073
+ const rows = gridRows.model.get();
10074
+ props.onDataChange?.(rows);
10075
+ props.rowDrag.onReorder(rows);
10076
+ rowRectsRef.value = [];
10077
+ return;
10078
+ }
10079
+ }
10080
+ if (props.getSubRows !== void 0 || props.lazyLoad !== void 0) {
10081
+ const visibleKeys = new Map(
10082
+ visibleRows.map((row, index) => [row, String(rowId(row, index))])
10083
+ );
10084
+ if (fromVisible >= 0 && toVisible >= 0) {
10085
+ const result = reorderTreeRows(
10086
+ gridRows.model.get(),
10087
+ activeId,
10088
+ overId,
10089
+ {
10090
+ // Every drop target is visible; leave hidden descendants keyless
10091
+ // so a synthetic sibling index cannot mask a visible target.
10092
+ getRowKey: (row) => visibleKeys.get(row),
10093
+ getChildren: readRowChildren,
10094
+ setChildren: writeLazyChildren
10095
+ },
10096
+ fromVisible < toVisible ? "after" : "before"
10097
+ );
10098
+ if (result.changed) {
10099
+ const rows = result.rows;
10100
+ gridRows.model.commit(rows, { reason: "row-drag" });
10101
+ props.onDataChange?.(rows);
10102
+ props.rowDrag.onReorder(rows);
10103
+ }
10104
+ }
10105
+ } else {
10106
+ const rows = [...bodyData.value];
10107
+ const from = rows.findIndex((r, i) => String(rowId(r, i)) === activeId);
10108
+ const to = rows.findIndex((r, i) => String(rowId(r, i)) === overId);
10109
+ if (from >= 0 && to >= 0 && from !== to) {
10110
+ const [moved] = rows.splice(from, 1);
10111
+ rows.splice(to, 0, moved);
10112
+ gridRows.model.commit(rows, { reason: "row-drag" });
10113
+ props.onDataChange?.(rows);
10114
+ props.rowDrag.onReorder(rows);
10115
+ }
9162
10116
  }
9163
10117
  }
9164
10118
  rowRectsRef.value = [];
@@ -9207,6 +10161,9 @@ var IrisTable = defineComponent({
9207
10161
  const [moved] = next.splice(from, 1);
9208
10162
  next.splice(to, 0, moved);
9209
10163
  props.columnDrag.onReorder(next);
10164
+ if (!grouped.value && orderPropControlled.value) {
10165
+ columnsFeature.setOrder(next.map((column) => column.key));
10166
+ }
9210
10167
  }
9211
10168
  }
9212
10169
  colRectsRef.value = [];
@@ -9215,8 +10172,7 @@ var IrisTable = defineComponent({
9215
10172
  const tableExpose = {
9216
10173
  loadData: (rows) => {
9217
10174
  recordAudit(rows, "edit");
9218
- if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
9219
- else localRowsOverride.value = rows;
10175
+ gridRows.model.loadData(rows);
9220
10176
  props.onDataChange?.(rows);
9221
10177
  },
9222
10178
  reloadData: () => {
@@ -9231,13 +10187,13 @@ var IrisTable = defineComponent({
9231
10187
  return { page: s.params.page, pageSize: s.params.pageSize, total: s.total };
9232
10188
  },
9233
10189
  removeRows: (keys) => {
9234
- const { rows, removedKeys } = removeRowsFromList(tableData.value, props.rowKey, keys);
9235
- if (removedKeys.size === 0) return;
10190
+ const removedKeys = gridRows.model.removeMany(keys);
10191
+ if (removedKeys.length === 0) return;
10192
+ const rows = gridRows.model.get();
9236
10193
  recordAudit(rows, "remove");
9237
- if (proxyCtrl.proxy.value) proxyCtrl.liveData.value = rows;
9238
- else localRowsOverride.value = rows;
9239
10194
  const selected = displaySelection.value;
9240
- const nextSelection = selected.filter((key) => !removedKeys.has(key));
10195
+ const removed = new Set(removedKeys);
10196
+ const nextSelection = selected.filter((key) => !removed.has(key));
9241
10197
  if (nextSelection.length !== selected.length) {
9242
10198
  rebaseToProp();
9243
10199
  selectionModel.set(nextSelection);
@@ -9246,12 +10202,12 @@ var IrisTable = defineComponent({
9246
10202
  },
9247
10203
  getFilteredData: () => [...bodyData.value],
9248
10204
  exportCurrentViewCsv: () => exportCsv(
9249
- withComputedFormulaCells(bodyData.value, leafColumns.value),
10205
+ withComputedFormulaCells(bodyData.value, leafColumns.value, props.formulaTables),
9250
10206
  leafColumns.value
9251
10207
  ),
9252
10208
  exportMultiCsv: () => {
9253
10209
  const current = exportCsv(
9254
- withComputedFormulaCells(bodyData.value, leafColumns.value),
10210
+ withComputedFormulaCells(bodyData.value, leafColumns.value, props.formulaTables),
9255
10211
  leafColumns.value
9256
10212
  );
9257
10213
  const names = props.exportNames;
@@ -9339,6 +10295,7 @@ ${refCsv}` : ""}`);
9339
10295
  densityToggle: props.densityToggle,
9340
10296
  effectiveDensity: effectiveDensity.value,
9341
10297
  onDensityToggle: cycleDensity,
10298
+ undo: { enabled: props.undo, controller: undoController, t },
9342
10299
  // Batch EN: the built-in audit trigger rides the toolbar (react
9343
10300
  // parity — the toolbar gate admits auditLog on its own).
9344
10301
  auditLog: props.auditLog,
@@ -9353,23 +10310,80 @@ ${refCsv}` : ""}`);
9353
10310
  const closeContextMenu = () => {
9354
10311
  if (contextMenuState.value) contextMenuState.value.open = false;
9355
10312
  };
10313
+ const virtualCursorAnchor = (event) => ({
10314
+ getBoundingClientRect: () => ({
10315
+ left: event.clientX,
10316
+ top: event.clientY,
10317
+ right: event.clientX,
10318
+ bottom: event.clientY,
10319
+ width: 0,
10320
+ height: 0,
10321
+ x: event.clientX,
10322
+ y: event.clientY,
10323
+ toJSON() {
10324
+ }
10325
+ })
10326
+ });
10327
+ const pinMenuState = ref(null);
10328
+ const pinMenuAnchorRef = ref(null);
10329
+ const pinMenuRef = ref(null);
10330
+ const closePinMenu = () => {
10331
+ if (pinMenuState.value) pinMenuState.value.open = false;
10332
+ };
10333
+ watch(
10334
+ () => props.columnPinMenu,
10335
+ (enabled) => {
10336
+ if (!enabled) closePinMenu();
10337
+ },
10338
+ { flush: "sync" }
10339
+ );
10340
+ const pinMenuItemsFor = (column) => pinOf(column) === null ? [{ key: PIN_LEFT_MENU_KEY, label: t("table.pinLeft") }] : [{ key: UNPIN_MENU_KEY, label: t("table.unpin") }];
10341
+ const handleHeaderContextMenu = (event, column) => {
10342
+ if (!props.columnPinMenu) return;
10343
+ event.preventDefault();
10344
+ event.stopPropagation();
10345
+ closeContextMenu();
10346
+ pinMenuAnchorRef.value = virtualCursorAnchor(event);
10347
+ const params = {
10348
+ row: void 0,
10349
+ column,
10350
+ rowIndex: -1,
10351
+ columnIndex: leafColumns.value.findIndex((candidate) => candidate.key === column.key)
10352
+ };
10353
+ pinMenuState.value = { open: true, items: pinMenuItemsFor(column), params };
10354
+ };
10355
+ const pinMenuConfig = {
10356
+ // The renderer receives the snapshot items above. This required config
10357
+ // member is intentionally empty: the pin menu has no caller items.
10358
+ items: () => [],
10359
+ onSelect: (key, params) => {
10360
+ const current = pinOf(params.column);
10361
+ if (key === PIN_LEFT_MENU_KEY && current === null) {
10362
+ setColumnPinned(params.column.key, "left");
10363
+ } else if (key === UNPIN_MENU_KEY && current !== null) {
10364
+ setColumnPinned(params.column.key, null);
10365
+ }
10366
+ }
10367
+ };
10368
+ watch(
10369
+ () => {
10370
+ const state = pinMenuState.value;
10371
+ if (!state?.open) return null;
10372
+ const side = pinOf(state.params.column);
10373
+ const label = t(side === null ? "table.pinLeft" : "table.unpin");
10374
+ return `${side ?? "none"}\0${label}`;
10375
+ },
10376
+ () => {
10377
+ const state = pinMenuState.value;
10378
+ if (state?.open) state.items = pinMenuItemsFor(state.params.column);
10379
+ },
10380
+ { flush: "sync" }
10381
+ );
9356
10382
  const handleContextMenu = (e, row, col, idx, ci) => {
9357
10383
  if (!props.contextMenu) return;
9358
10384
  e.preventDefault();
9359
- contextAnchorRef.value = {
9360
- getBoundingClientRect: () => ({
9361
- left: e.clientX,
9362
- top: e.clientY,
9363
- right: e.clientX,
9364
- bottom: e.clientY,
9365
- width: 0,
9366
- height: 0,
9367
- x: e.clientX,
9368
- y: e.clientY,
9369
- toJSON() {
9370
- }
9371
- })
9372
- };
10385
+ closePinMenu();
10386
+ contextAnchorRef.value = virtualCursorAnchor(e);
9373
10387
  const params = {
9374
10388
  row,
9375
10389
  column: col,
@@ -9411,6 +10425,41 @@ ${refCsv}` : ""}`);
9411
10425
  close: closeContextMenu,
9412
10426
  contextMenu: props.contextMenu
9413
10427
  });
10428
+ const pinMenuOpen = computed(() => props.columnPinMenu && pinMenuState.value?.open === true);
10429
+ const { floatingStyles: pinMenuStyles } = useFloating({
10430
+ anchor: pinMenuAnchorRef,
10431
+ floating: pinMenuRef,
10432
+ open: pinMenuOpen,
10433
+ placement: "bottom-start",
10434
+ // The zero-size virtual anchor must remain at the cursor; clamping it
10435
+ // would move the context menu away from the requested coordinates.
10436
+ flip: false,
10437
+ shift: false
10438
+ });
10439
+ useDismiss({
10440
+ enabled: pinMenuOpen,
10441
+ exclude: [pinMenuRef],
10442
+ onDismiss: closePinMenu
10443
+ });
10444
+ watch(pinMenuOpen, (open) => {
10445
+ if (typeof document === "undefined") return;
10446
+ if (open) document.addEventListener("scroll", closePinMenu, true);
10447
+ else document.removeEventListener("scroll", closePinMenu, true);
10448
+ });
10449
+ onScopeDispose(() => {
10450
+ if (typeof document === "undefined") return;
10451
+ document.removeEventListener("scroll", closePinMenu, true);
10452
+ });
10453
+ const buildPinMenuSection = () => {
10454
+ if (!props.columnPinMenu) return null;
10455
+ return renderContextMenuSection({
10456
+ state: pinMenuState,
10457
+ styles: pinMenuStyles,
10458
+ menuRef: pinMenuRef,
10459
+ close: closePinMenu,
10460
+ contextMenu: pinMenuConfig
10461
+ });
10462
+ };
9414
10463
  const filterPanelState = ref(null);
9415
10464
  const filterAnchorRef = ref(null);
9416
10465
  const filterPanelRef = ref(null);
@@ -9421,7 +10470,7 @@ ${refCsv}` : ""}`);
9421
10470
  const openFilterPanel = (e, colKey) => {
9422
10471
  e.stopPropagation();
9423
10472
  filterAnchorRef.value = e.currentTarget;
9424
- filterDraft.value = [...props.filterValues?.[colKey] ?? []];
10473
+ filterDraft.value = [...effectiveFilterValues.value[colKey] ?? []];
9425
10474
  filterPanelState.value = { open: true, colKey };
9426
10475
  };
9427
10476
  const filterPanelOpen = computed(() => filterPanelState.value?.open === true);
@@ -9446,12 +10495,15 @@ ${refCsv}` : ""}`);
9446
10495
  document.removeEventListener("scroll", closeFilterPanel, true);
9447
10496
  });
9448
10497
  const applyFilterValues = (colKey, values) => {
9449
- props.onFilterValuesChange?.({ ...props.filterValues ?? {}, [colKey]: values });
10498
+ filtering.model.setFilterValues({ ...effectiveFilterValues.value, [colKey]: values });
10499
+ if (props.recentFilters && values.length > 0) recent.record(colKey, values);
10500
+ };
10501
+ const applyRecentFilter = (entry) => {
10502
+ applyFilterValues(entry.key, entry.values);
10503
+ closeFilterPanel();
9450
10504
  };
9451
10505
  const clearFilterValues = (colKey) => {
9452
- const next = { ...props.filterValues ?? {} };
9453
- delete next[colKey];
9454
- props.onFilterValuesChange?.(next);
10506
+ filtering.model.clearColumnFilterValues(colKey);
9455
10507
  };
9456
10508
  const toggleFilterDraft = (value) => {
9457
10509
  filterDraft.value = filterDraft.value.includes(value) ? filterDraft.value.filter((v) => v !== value) : [...filterDraft.value, value];
@@ -9462,12 +10514,14 @@ ${refCsv}` : ""}`);
9462
10514
  panelRef: filterPanelRef,
9463
10515
  filterDraft,
9464
10516
  columns: displayColumns.value,
9465
- filterValues: props.filterValues,
10517
+ filterValues: effectiveFilterValues.value,
9466
10518
  t,
9467
10519
  close: closeFilterPanel,
9468
10520
  toggle: toggleFilterDraft,
9469
10521
  apply: applyFilterValues,
9470
- clear: clearFilterValues
10522
+ clear: clearFilterValues,
10523
+ recent: props.recentFilters ? recentEntries.value : [],
10524
+ onApplyRecent: applyRecentFilter
9471
10525
  });
9472
10526
  return () => {
9473
10527
  const showSelection = props.selectable !== "none";
@@ -9596,6 +10650,13 @@ ${refCsv}` : ""}`);
9596
10650
  multiSortSeq,
9597
10651
  renderFilterTrigger,
9598
10652
  pinnedDragHandle,
10653
+ pinOf,
10654
+ pinnedColumnsControlled: props.pinnedColumns !== void 0,
10655
+ columnPinMenu: props.columnPinMenu,
10656
+ pinnedStyle,
10657
+ onHeaderContextMenu: props.columnPinMenu ? handleHeaderContextMenu : void 0,
10658
+ columnFadeAttr,
10659
+ columnFadeStyle,
9599
10660
  gridTemplate
9600
10661
  },
9601
10662
  matrix
@@ -9696,7 +10757,8 @@ ${refCsv}` : ""}`);
9696
10757
  key: col.key,
9697
10758
  role: "columnheader",
9698
10759
  "data-iris-table-header": col.key,
9699
- "data-iris-table-pinned": col.pinned,
10760
+ "data-iris-table-pinned": pinOf(col),
10761
+ ...columnFadeAttrs(col),
9700
10762
  // Column drag-sort (vxe columnDragConfig parity, batch Y): the
9701
10763
  // header cell is the press target; grouped headers are NOT
9702
10764
  // supported (documented simplification — the reorder maps leaf
@@ -9705,6 +10767,7 @@ ${refCsv}` : ""}`);
9705
10767
  "data-iris-col-drag-over": props.columnDrag && colDragState.value.overId === col.key ? "true" : void 0,
9706
10768
  onPointerdown: props.columnDrag && !grouped.value ? (e) => handleColDragPointerDown(e, col.key) : void 0,
9707
10769
  onClick: () => onHeaderClick(col),
10770
+ ...props.columnPinMenu ? { onContextmenu: (event) => handleHeaderContextMenu(event, col) } : {},
9708
10771
  style: {
9709
10772
  position: "relative",
9710
10773
  display: "flex",
@@ -9721,8 +10784,9 @@ ${refCsv}` : ""}`);
9721
10784
  whiteSpace: "nowrap",
9722
10785
  overflow: "hidden",
9723
10786
  textOverflow: "ellipsis",
10787
+ ...columnFadeStyle(col) ?? {},
9724
10788
  ...visibleColSet.value ? { gridColumnStart: String(colTrack(ci)) } : {},
9725
- ...col.pinned ? { ...pinnedStyle(col.key), background: "var(--iris-surface)" } : {}
10789
+ ...pinOf(col) !== null ? { ...pinnedStyle(col.key), background: "var(--iris-surface)" } : {}
9726
10790
  },
9727
10791
  "aria-sort": ariaSortFor(col)
9728
10792
  },
@@ -9900,7 +10964,7 @@ ${refCsv}` : ""}`);
9900
10964
  const cellSlot = slots[`cell.${col.key}`];
9901
10965
  const isRowEditing = rowMode.value && rowEditing.value !== null && rowEditing.value.k === id && rowSessions.value.has(cellId(id, col.key));
9902
10966
  const isEditing = isRowEditing || editingCellId.value === cellId(id, col.key);
9903
- const patternHint = (props.pattern || props.patternFill) && !rowMode.value && editingColumnKey.value === col.key && !isEditing && editingDraft.value !== "" && String(getCellValue(row, col) ?? "") === editingDraft.value;
10967
+ const patternHint = (props.pattern || props.patternFill) && !rowMode.value && editingColumnKey.value === col.key && !isEditing && editingDraft.value !== "" && String(getCellValue2(row, col) ?? "") === editingDraft.value;
9904
10968
  let content;
9905
10969
  if (isEditing) {
9906
10970
  const editCellId = cellId(id, col.key);
@@ -9913,7 +10977,11 @@ ${refCsv}` : ""}`);
9913
10977
  rowSessions.value.get(editCellId)
9914
10978
  ) : buildCellEditContent(row, col, index, editCellId);
9915
10979
  } else {
9916
- content = cellSlot?.({ row, index, value: getCellValue(row, col) }) ?? tableDisplayText(row, col, getCellValue);
10980
+ content = cellSlot?.({ row, index, value: getCellValue2(row, col) }) ?? (props.searchHighlight ? (() => {
10981
+ const displayValue = applyTableMask(getCellValue2(row, col), col);
10982
+ const renderedValue = col.formatter ? col.formatter(displayValue, row) : displayValue;
10983
+ return applySearchHighlight(renderedValue, props.searchHighlight);
10984
+ })() : tableDisplayText(row, col, getCellValue2));
9917
10985
  }
9918
10986
  const treeIndent = treeMeta && ci === 0 ? buildTreeIndent(treeMeta, row) : null;
9919
10987
  const cellChildren = treeIndent ? [treeIndent, ...Array.isArray(content) ? content : [content]] : content;
@@ -9924,7 +10992,8 @@ ${refCsv}` : ""}`);
9924
10992
  key: col.key,
9925
10993
  role: "cell",
9926
10994
  "data-iris-table-cell": col.key,
9927
- "data-iris-table-pinned": col.pinned,
10995
+ "data-iris-table-pinned": pinOf(col),
10996
+ ...columnFadeAttrs(col),
9928
10997
  "data-editable": isEditableColumn(col) ? "" : void 0,
9929
10998
  "data-editing": isEditing ? "" : void 0,
9930
10999
  "data-iris-input-hint": patternHint ? "true" : void 0,
@@ -9977,7 +11046,8 @@ ${refCsv}` : ""}`);
9977
11046
  ...patternHint ? {
9978
11047
  backgroundImage: "linear-gradient(var(--iris-input-hint, rgba(251, 191, 36, 0.16)), var(--iris-input-hint, rgba(251, 191, 36, 0.16)))"
9979
11048
  } : {},
9980
- ...pinnedStyle(col.key)
11049
+ ...pinnedStyle(col.key),
11050
+ ...columnFadeStyle(col) ?? {}
9981
11051
  }
9982
11052
  },
9983
11053
  cellChildren
@@ -10007,7 +11077,7 @@ ${refCsv}` : ""}`);
10007
11077
  display: "grid",
10008
11078
  gridTemplateColumns: gridTemplate.value,
10009
11079
  background: selected ? "var(--iris-surface-hover)" : "transparent",
10010
- transition: "background-color 120ms ease",
11080
+ transition: columnFadeActive.value ? "background-color 120ms ease, grid-template-columns var(--iris-duration-md, 200ms) ease" : "background-color 120ms ease",
10011
11081
  cursor: "default",
10012
11082
  ...style
10013
11083
  }
@@ -10104,8 +11174,11 @@ ${refCsv}` : ""}`);
10104
11174
  visibleColSet: visibleColSet.value,
10105
11175
  gridTemplate: gridTemplate.value,
10106
11176
  leadingCells: leadSummaryCells(),
11177
+ columnFadeAttr,
11178
+ columnFadeStyle,
10107
11179
  colTrack,
10108
- getCellValue,
11180
+ getCellValue: getCellValue2,
11181
+ pinOf,
10109
11182
  pinnedStyle
10110
11183
  }) : null;
10111
11184
  const rootNodes = [
@@ -10135,6 +11208,7 @@ ${refCsv}` : ""}`);
10135
11208
  // grid/table role as before (treegrid implies managed cell focus).
10136
11209
  role: props.keyboardNavigation ? treeMode.value ? "treegrid" : "grid" : "table",
10137
11210
  "data-iris-table": "",
11211
+ "data-iris-column-fade-active": columnFadeActive.value ? "true" : void 0,
10138
11212
  "data-density": effectiveDensity.value,
10139
11213
  "data-printable": props.printable ? "true" : void 0,
10140
11214
  "data-virtual": props.virtualScroll ? "" : void 0,
@@ -10168,7 +11242,7 @@ ${refCsv}` : ""}`);
10168
11242
  ...attrs.style ?? {}
10169
11243
  }
10170
11244
  },
10171
- [headerRow, bodyNode, summaryRow, buildPagerSection()]
11245
+ [headerRow, bodyNode, summaryRow, buildPagerSection(), buildBackTopSection()]
10172
11246
  ),
10173
11247
  props.responsive && responsiveOverflow.value && !props.printable ? h(
10174
11248
  "div",
@@ -10191,6 +11265,7 @@ ${refCsv}` : ""}`);
10191
11265
  [h("span", { "aria-hidden": "true" }, "\u21C6"), h("span", t("table.scrollHint"))]
10192
11266
  ) : null,
10193
11267
  buildContextMenuSection(),
11268
+ buildPinMenuSection(),
10194
11269
  buildFilterPanelSection(),
10195
11270
  buildAuditPanelSection()
10196
11271
  ].filter((n) => n !== null);
@@ -11482,16 +12557,11 @@ var IrisTree = defineComponent({
11482
12557
  return out;
11483
12558
  });
11484
12559
  const checkNodes = computed(() => {
11485
- const out = [];
11486
- const walk = (nodes, parentKey) => {
11487
- for (const node of nodes) {
11488
- out.push({ key: node.id, parentKey, disabled: node.disabled });
11489
- const kids = node.children ?? lazyCache.value.get(node.id);
11490
- if (kids && kids.length > 0) walk(kids, node.id);
11491
- }
11492
- };
11493
- walk(props.nodes, void 0);
11494
- return out;
12560
+ return flattenTreeSelectionNodes(props.nodes, {
12561
+ getKey: (node) => node.id,
12562
+ getChildren: (node) => node.children ?? lazyCache.value.get(node.id),
12563
+ isDisabled: (node) => node.disabled === true
12564
+ });
11495
12565
  });
11496
12566
  let checkModel = createTreeSelection({
11497
12567
  nodes: checkNodes.value,
@@ -11899,7 +12969,7 @@ var ARROW_BTN = {
11899
12969
  fontSize: "var(--iris-font-size-xl, 18px)",
11900
12970
  lineHeight: "1"
11901
12971
  };
11902
- function prefersReducedMotion() {
12972
+ function prefersReducedMotion2() {
11903
12973
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
11904
12974
  }
11905
12975
  function flattenSlides(nodes) {
@@ -11947,7 +13017,7 @@ var IrisCarousel = defineComponent({
11947
13017
  };
11948
13018
  const start = () => {
11949
13019
  stop();
11950
- if (!props.autoplay || prefersReducedMotion()) return;
13020
+ if (!props.autoplay || prefersReducedMotion2()) return;
11951
13021
  timer = setInterval(() => {
11952
13022
  if (props.pauseOnHover && hovered.value || focusedWithin.value) return;
11953
13023
  const list = flattenSlides(slots.default?.() ?? []);
@@ -12721,7 +13791,7 @@ var IrisWatermark = defineComponent({
12721
13791
  );
12722
13792
  }
12723
13793
  });
12724
- function prefersReducedMotion2() {
13794
+ function prefersReducedMotion3() {
12725
13795
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
12726
13796
  }
12727
13797
  var resolve = (target) => (target ? target() : window) ?? window;
@@ -12759,7 +13829,7 @@ var IrisBackTop = defineComponent({
12759
13829
  });
12760
13830
  const scrollToTop = () => {
12761
13831
  if (!el) return;
12762
- const b = prefersReducedMotion2() ? "auto" : props.behavior;
13832
+ const b = prefersReducedMotion3() ? "auto" : props.behavior;
12763
13833
  if (typeof el.scrollTo === "function") {
12764
13834
  el.scrollTo({ top: 0, behavior: b });
12765
13835
  } else {
@@ -14247,7 +15317,7 @@ var IrisFloatButton = defineComponent({
14247
15317
  };
14248
15318
  }
14249
15319
  });
14250
- function prefersReducedMotion3() {
15320
+ function prefersReducedMotion4() {
14251
15321
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
14252
15322
  }
14253
15323
  var resolve3 = (target) => (target ? target() : window) ?? window;
@@ -14291,7 +15361,7 @@ var IrisAnchor = defineComponent({
14291
15361
  if (!node) return;
14292
15362
  if (typeof node.scrollIntoView === "function") {
14293
15363
  node.scrollIntoView({
14294
- behavior: prefersReducedMotion3() ? "auto" : "smooth",
15364
+ behavior: prefersReducedMotion4() ? "auto" : "smooth",
14295
15365
  block: "start"
14296
15366
  });
14297
15367
  }
@@ -15462,7 +16532,7 @@ var IrisGauge = defineComponent({
15462
16532
  };
15463
16533
  }
15464
16534
  });
15465
- function prefersReducedMotion4() {
16535
+ function prefersReducedMotion5() {
15466
16536
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
15467
16537
  }
15468
16538
  var IrisMarquee = defineComponent({
@@ -15481,7 +16551,7 @@ var IrisMarquee = defineComponent({
15481
16551
  let anim = null;
15482
16552
  onMounted(() => {
15483
16553
  const el = trackEl.value;
15484
- if (!el || typeof el.animate !== "function" || prefersReducedMotion4()) return;
16554
+ if (!el || typeof el.animate !== "function" || prefersReducedMotion5()) return;
15485
16555
  const frames = props.direction === "left" ? [{ transform: "translateX(0%)" }, { transform: "translateX(-50%)" }] : [{ transform: "translateX(-50%)" }, { transform: "translateX(0%)" }];
15486
16556
  anim = el.animate(frames, {
15487
16557
  duration: Math.max(1, props.duration) * 1e3,
@@ -16762,7 +17832,7 @@ var IrisColorPicker = defineComponent({
16762
17832
  color: "var(--iris-foreground)",
16763
17833
  border: "1px solid var(--iris-border)",
16764
17834
  borderRadius: "var(--iris-radius-sm, 4px)",
16765
- fontSize: "12px",
17835
+ fontSize: "var(--iris-font-size-xs, 12px)",
16766
17836
  fontFamily: "inherit",
16767
17837
  textAlign: "center"
16768
17838
  };
@@ -16856,7 +17926,7 @@ var IrisColorPicker = defineComponent({
16856
17926
  height: "4px",
16857
17927
  transform: "translateY(-50%)",
16858
17928
  border: "2px solid #fff",
16859
- borderRadius: "2px",
17929
+ borderRadius: "var(--iris-radius-sm, 4px)",
16860
17930
  boxShadow: "0 0 0 1px rgba(0,0,0,.4)",
16861
17931
  pointerEvents: "none"
16862
17932
  }
@@ -16908,7 +17978,7 @@ var IrisColorPicker = defineComponent({
16908
17978
  display: "flex",
16909
17979
  gap: "6px",
16910
17980
  alignItems: "center",
16911
- fontSize: "12px"
17981
+ fontSize: "var(--iris-font-size-xs, 12px)"
16912
17982
  }
16913
17983
  },
16914
17984
  [
@@ -16990,12 +18060,12 @@ function defaultFilter(query, item) {
16990
18060
  if (!q) return 0;
16991
18061
  const haystacks = [item.label, ...item.keywords ?? []].map((s) => s.toLowerCase());
16992
18062
  let best = null;
16993
- for (const h102 of haystacks) {
18063
+ for (const h104 of haystacks) {
16994
18064
  let qi = 0;
16995
18065
  let lastIdx = -1;
16996
18066
  let score = 0;
16997
- for (let i = 0; i < h102.length && qi < q.length; i += 1) {
16998
- if (h102[i] === q[qi]) {
18067
+ for (let i = 0; i < h104.length && qi < q.length; i += 1) {
18068
+ if (h104[i] === q[qi]) {
16999
18069
  score += lastIdx === -1 ? i : i - lastIdx - 1;
17000
18070
  lastIdx = i;
17001
18071
  qi += 1;