@vmscloud/moz-ui-grid-vue 0.2.8 → 0.2.10

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
@@ -1,8 +1,8 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { onBeforeUnmount, inject, ref, watch, nextTick, shallowRef, defineComponent, createElementBlock, openBlock, computed, provide, reactive, createApp, h, getCurrentInstance, mergeModels, useModel, useSlots, onMounted, toRef, Fragment, createElementVNode, renderSlot, createBlock, createCommentVNode, normalizeStyle, mergeProps, toHandlers, unref, createSlots, withCtx, normalizeProps, guardReactiveProps } from "vue";
5
- import { Popup, radio, select, button, GridChrome, WRAPPER_EXTRA_KEYS } from "@vmscloud/moz-ui-grid-wrapper";
4
+ import { onBeforeUnmount, inject, ref, watch, nextTick, shallowRef, defineComponent, createElementBlock, openBlock, computed, provide, reactive, createApp, h, getCurrentInstance, mergeModels, useModel, useSlots, onMounted, toRef, Fragment, createElementVNode, renderSlot, createBlock, createCommentVNode, normalizeStyle, markRaw, mergeProps, toHandlers, unref, createSlots, withCtx, normalizeProps, guardReactiveProps } from "vue";
5
+ import { stampPresetVersion, migrateGridPreset, applyPresetCoreConfig, Popup, radio, select, button, LatestSettingsCoordinator, GridChrome, WRAPPER_EXTRA_KEYS } from "@vmscloud/moz-ui-grid-wrapper";
6
6
  import { u as useGridNotificationStore } from "./chunks/notification.contract-BTOch6ed.js";
7
7
  import { G, p } from "./chunks/notification.contract-BTOch6ed.js";
8
8
  import { html, nothing } from "lit-html";
@@ -98,41 +98,55 @@ const useUserLayoutModule = () => inject(USER_LAYOUT_PROVIDER_KEY, {
98
98
  currentPreset: ref(null)
99
99
  });
100
100
  function useGridPreset(options) {
101
+ if (options.usePreset === false) return;
101
102
  const { register } = useUserLayoutModule();
102
103
  const exportDef = () => {
103
104
  var _a, _b;
104
105
  const grid = options.gridInstance.value;
105
106
  if (!grid) return null;
106
- return {
107
- ...grid.getConfig(),
107
+ const def = {
108
+ ...stampPresetVersion(grid.getConfig()),
108
109
  customCellConfig: ((_a = options.getCustomCellState) == null ? void 0 : _a.call(options)) ?? null,
109
110
  columnFormatConfig: ((_b = options.getColumnFormatState) == null ? void 0 : _b.call(options)) ?? null
110
111
  };
112
+ if (options.getViewOptionsState) def.viewOptionsConfig = options.getViewOptionsState() ?? null;
113
+ return def;
114
+ };
115
+ const applyExtra = (set, value) => {
116
+ if (value !== void 0) set == null ? void 0 : set(value);
111
117
  };
112
- const importDef = async (preset) => {
113
- var _a, _b, _c, _d;
118
+ const applyPreset = async (preset) => {
114
119
  const grid = options.gridInstance.value;
115
120
  if (!grid) return;
116
- if (!preset) {
117
- const defaultDef = layoutInstance.default;
118
- if (defaultDef) {
119
- await grid.applyConfig(defaultDef);
120
- if (defaultDef.customCellConfig) {
121
- (_a = options.setCustomCellState) == null ? void 0 : _a.call(options, defaultDef.customCellConfig);
122
- }
123
- if (defaultDef.columnFormatConfig) {
124
- (_b = options.setColumnFormatState) == null ? void 0 : _b.call(options, defaultDef.columnFormatConfig);
125
- }
126
- }
127
- return;
128
- }
129
- await grid.applyConfig(preset);
130
- if (preset.customCellConfig) {
131
- (_c = options.setCustomCellState) == null ? void 0 : _c.call(options, preset.customCellConfig);
132
- }
133
- if (preset.columnFormatConfig) {
134
- (_d = options.setColumnFormatState) == null ? void 0 : _d.call(options, preset.columnFormatConfig);
121
+ const baseline = layoutInstance.default ?? null;
122
+ const migrated = migrateGridPreset(preset ?? baseline, {
123
+ grid,
124
+ getSourceFields: options.getSourceFields,
125
+ baseline
126
+ });
127
+ if (migrated.diagnostics.length) {
128
+ if (options.onImportDiagnostics) options.onImportDiagnostics(migrated.diagnostics);
129
+ else console.warn("[Grid preset compatibility]", migrated.diagnostics);
135
130
  }
131
+ if (migrated.config) await applyPresetCoreConfig(grid, migrated.config, migrated.missing);
132
+ applyExtra(options.setColumnFormatState, migrated.columnFormatConfig);
133
+ applyExtra(options.setCustomCellState, migrated.customCellConfig);
134
+ applyExtra(options.setViewOptionsState, migrated.viewOptionsConfig);
135
+ };
136
+ let latestRequest = 0;
137
+ let queue = Promise.resolve();
138
+ const importDef = (preset) => {
139
+ const token = ++latestRequest;
140
+ const run = queue.then(async () => {
141
+ if (token !== latestRequest) return;
142
+ try {
143
+ await applyPreset(preset);
144
+ } catch (error) {
145
+ console.error("[Grid preset] import failed", error);
146
+ }
147
+ });
148
+ queue = run;
149
+ return run;
136
150
  };
137
151
  const layoutInstance = new UserLayoutInstance({
138
152
  id: options.name.value,
@@ -213,6 +227,8 @@ async function runClientExport(grid, config, notificationStore) {
213
227
  suggestedName: fileName,
214
228
  fallbackToDownload: true,
215
229
  sheetSplit: "auto",
230
+ // 화면에 보이는 그대로 — 고정 행을 화면 순서로 포함(core 기본값 false). clientExportOptions 로 끌 수 있음.
231
+ includePinnedRows: true,
216
232
  onProgress: (p2) => {
217
233
  if (!notificationStore) return;
218
234
  const target = notificationStore.eventMessages.find(
@@ -984,6 +1000,7 @@ const GRID_BULK_EDITOR_MODULE_KEY = Symbol("moz-grid-bulk-editor-module");
984
1000
  const _sfc_main$2 = /* @__PURE__ */ defineComponent({
985
1001
  __name: "MozGrid",
986
1002
  props: /* @__PURE__ */ mergeModels({
1003
+ sourceRevision: {},
987
1004
  height: { default: "100%" },
988
1005
  contextMenuConfig: {},
989
1006
  emptyState: {},
@@ -992,6 +1009,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
992
1009
  useMultipleChart: { type: Boolean },
993
1010
  chartIncludeGrandTotal: { type: Boolean },
994
1011
  chartMap: {},
1012
+ chartConfig: {},
995
1013
  useRowGroupSettings: { type: Boolean, default: true },
996
1014
  useFieldSettings: { type: Boolean, default: true },
997
1015
  useFormatSettings: { type: Boolean, default: true },
@@ -1009,7 +1027,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1009
1027
  useChart: { type: Boolean },
1010
1028
  loading: { type: Boolean },
1011
1029
  useBuiltinTooltip: { type: Boolean, default: true },
1012
- name: {}
1030
+ toolbarConfig: {},
1031
+ name: {},
1032
+ usePreset: { type: Boolean, default: true }
1013
1033
  }, {
1014
1034
  "coreConfig": { default: () => ({}) },
1015
1035
  "coreConfigModifiers": {}
@@ -1144,12 +1164,13 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1144
1164
  return { height: typeof h2 === "number" ? `${h2}px` : h2, width: "100%" };
1145
1165
  });
1146
1166
  function slotRenderer(name) {
1147
- return (container) => {
1167
+ return (container, ctx) => {
1148
1168
  const slotComponent = {
1149
1169
  // tool-items 는 toolItemsProps(ref)를 스코프 슬롯 props 로 — render 에서 .value 읽어 반응형 갱신.
1170
+ // option-menu-items 는 크롬이 넘긴 { close, mode }(WrapperOptionMenuItemsProps)를 스코프 슬롯 props 로.
1150
1171
  render: () => {
1151
- var _a, _b;
1152
- return (name === "tool-items" ? (_a = slots[name]) == null ? void 0 : _a.call(slots, toolItemsProps.value) : (_b = slots[name]) == null ? void 0 : _b.call(slots)) ?? null;
1172
+ var _a, _b, _c;
1173
+ return (name === "tool-items" ? (_a = slots[name]) == null ? void 0 : _a.call(slots, toolItemsProps.value) : name === "option-menu-items" ? (_b = slots[name]) == null ? void 0 : _b.call(slots, ctx.props) : (_c = slots[name]) == null ? void 0 : _c.call(slots)) ?? null;
1153
1174
  }
1154
1175
  };
1155
1176
  const key = `slot:${name}`;
@@ -1175,7 +1196,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1175
1196
  return core;
1176
1197
  }
1177
1198
  function buildChromeOptions() {
1178
- const CHROME_SLOTS = ["tool-left", "tool-items", "option-popup", "tool-bottom", "notification", "footer", "empty-state"];
1199
+ const CHROME_SLOTS = ["tool-left", "tool-items", "option-popup", "tool-bottom", "notification", "footer", "empty-state", "option-menu-items"];
1179
1200
  const slotMap = {};
1180
1201
  for (const name of CHROME_SLOTS) {
1181
1202
  if (slots[name]) slotMap[name] = slotRenderer(name);
@@ -1197,9 +1218,11 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1197
1218
  useFormatSettings: props.useFormatSettings,
1198
1219
  useLayoutSettings: props.useLayoutSettings,
1199
1220
  useAggregateSettings: props.useAggregateSettings,
1221
+ toolbarConfig: props.toolbarConfig,
1200
1222
  useMultipleChart: props.useMultipleChart,
1201
1223
  chartIncludeGrandTotal: props.chartIncludeGrandTotal,
1202
1224
  chartMap: props.chartMap,
1225
+ chartConfig: props.chartConfig,
1203
1226
  loading: props.loading,
1204
1227
  locale: resolvedLocale.value,
1205
1228
  sourceFields: props.sourceFields,
@@ -1256,10 +1279,19 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1256
1279
  }
1257
1280
  chromeUnsubs = [];
1258
1281
  }
1259
- function createChrome() {
1282
+ const reportedErrors = /* @__PURE__ */ new WeakSet();
1283
+ function reportInitializationError(error) {
1284
+ if (error && typeof error === "object") {
1285
+ if (reportedErrors.has(error)) return;
1286
+ reportedErrors.add(error);
1287
+ }
1288
+ console.error("[MozGrid] initialization failed", error);
1289
+ }
1290
+ function createChrome(snapshot) {
1260
1291
  if (!hostRef.value) return null;
1261
1292
  vueRenderer.setAppContext(appContext);
1262
- const c2 = new GridChrome(hostRef.value, buildCoreConfig(), buildChromeOptions());
1293
+ const c2 = markRaw(new GridChrome(hostRef.value, buildCoreConfig(), { ...buildChromeOptions(), sourceRevision: props.sourceRevision }, snapshot));
1294
+ markRaw(c2.grid);
1263
1295
  chrome = c2;
1264
1296
  gridInstance.value = c2.grid;
1265
1297
  containerRef.value = hostRef.value;
@@ -1272,56 +1304,56 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1272
1304
  c2.syncAppliedConfig(buildCoreConfig());
1273
1305
  })
1274
1306
  );
1307
+ void c2.ready().catch(reportInitializationError);
1275
1308
  return c2;
1276
1309
  }
1277
- async function mountChrome() {
1278
- const c2 = createChrome();
1279
- if (!c2) return;
1280
- try {
1281
- await c2.ready();
1282
- if (!isCurrentChrome(c2)) return;
1310
+ let readyChrome = null;
1311
+ const settingsCoordinator = new LatestSettingsCoordinator({
1312
+ waitUntilSettled: async (request, signal) => {
1313
+ if (chrome == null ? void 0 : chrome.waitForReinitSettled) await chrome.waitForReinitSettled(signal, chrome.isSourceReplacement(request.config, request.sourceRevision));
1314
+ else await (chrome == null ? void 0 : chrome.ready());
1315
+ },
1316
+ apply: async (request, context) => {
1317
+ if (!chrome) return;
1318
+ let c2 = chrome;
1319
+ const { reinitRequired, completion } = c2.reconcileCoreConfig(request.config, request.sourceRevision);
1320
+ if (completion) await completion;
1321
+ if (reinitRequired) {
1322
+ const captureHost = c2;
1323
+ const snapshot = captureHost.captureReinitStateAsync ? await captureHost.captureReinitStateAsync(request.config, request.sourceRevision) : c2.captureReinitState(request.config, request.sourceRevision);
1324
+ if (!context.isCurrent() || chrome !== c2) return;
1325
+ clearChromeUnsubs();
1326
+ c2.destroy();
1327
+ chrome = null;
1328
+ vueRenderer.unmountAll();
1329
+ const next = createChrome(snapshot);
1330
+ if (!next) return;
1331
+ c2 = next;
1332
+ await c2.ready();
1333
+ if (chrome !== c2) return;
1334
+ await c2.restoreReinitState(snapshot);
1335
+ } else {
1336
+ await c2.ready();
1337
+ }
1338
+ if (!context.isCurrent() || chrome !== c2 || readyChrome === c2) return;
1339
+ readyChrome = c2;
1283
1340
  toolItemsProps.value = c2.getToolItemsProps();
1284
1341
  emit("ready", c2.grid, c2);
1285
- } catch (error) {
1286
- if (chrome === c2) console.error("[MozGrid] initialization failed", error);
1287
- }
1288
- }
1342
+ },
1343
+ onError: reportInitializationError
1344
+ });
1289
1345
  function reconcile() {
1290
- if (!chrome) return;
1291
- const { reinitRequired } = chrome.reconcileCoreConfig(buildCoreConfig());
1292
- if (reinitRequired) void reinitChrome();
1293
- }
1294
- function isCurrentChrome(candidate) {
1295
- return chrome === candidate;
1296
- }
1297
- async function reinitChrome() {
1298
- if (!chrome) return;
1299
- const snapshot = chrome.captureReinitState();
1300
- clearChromeUnsubs();
1301
- chrome.destroy();
1302
- chrome = null;
1303
- vueRenderer.unmountAll();
1304
- const c2 = createChrome();
1305
- if (!c2) return;
1306
- try {
1307
- await c2.ready();
1308
- if (!isCurrentChrome(c2)) return;
1309
- await c2.restoreReinitState(snapshot);
1310
- if (!isCurrentChrome(c2)) return;
1311
- toolItemsProps.value = c2.getToolItemsProps();
1312
- emit("ready", c2.grid, c2);
1313
- } catch (error) {
1314
- if (chrome === c2) console.error("[MozGrid] initialization failed", error);
1315
- }
1346
+ settingsCoordinator.request({ config: buildCoreConfig(), sourceRevision: props.sourceRevision });
1316
1347
  }
1317
1348
  onMounted(() => {
1318
- void mountChrome();
1349
+ createChrome();
1350
+ reconcile();
1319
1351
  watch(
1320
1352
  () => {
1321
1353
  const all = coreConfig.value;
1322
1354
  void all.data;
1323
1355
  void all.fields;
1324
- const rest = { _locale: resolvedLocale.value, _timeZone: resolvedTimeZone.value };
1356
+ const rest = { _locale: resolvedLocale.value, _timeZone: resolvedTimeZone.value, _sourceRevision: props.sourceRevision };
1325
1357
  for (const k2 in all) {
1326
1358
  if (k2 === "data" || k2 === "fields") continue;
1327
1359
  rest[k2] = all[k2];
@@ -1332,23 +1364,33 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1332
1364
  { deep: true }
1333
1365
  );
1334
1366
  watch(
1335
- () => ({
1336
- useToolBox: props.useToolBox,
1337
- useToolBoxSetting: props.useToolBoxSetting,
1338
- useExtendFooter: props.useExtendFooter,
1339
- useSummaryFooter: props.useSummaryFooter,
1340
- useSort: props.useSort,
1341
- useCustomCell: props.useCustomCell,
1342
- useContextMenu: props.useContextMenu,
1343
- useBuiltinTooltip: props.useBuiltinTooltip,
1344
- usePivot: props.usePivot,
1345
- usePivotBar: props.usePivotBar,
1346
- useRowGroupSettings: props.useRowGroupSettings,
1347
- useFieldSettings: props.useFieldSettings,
1348
- useFormatSettings: props.useFormatSettings,
1349
- useLayoutSettings: props.useLayoutSettings,
1350
- useAggregateSettings: props.useAggregateSettings
1351
- }),
1367
+ () => {
1368
+ var _a, _b, _c, _d;
1369
+ return {
1370
+ useToolBox: props.useToolBox,
1371
+ useToolBoxSetting: props.useToolBoxSetting,
1372
+ useExtendFooter: props.useExtendFooter,
1373
+ useSummaryFooter: props.useSummaryFooter,
1374
+ useSort: props.useSort,
1375
+ useCustomCell: props.useCustomCell,
1376
+ useContextMenu: props.useContextMenu,
1377
+ useBuiltinTooltip: props.useBuiltinTooltip,
1378
+ usePivot: props.usePivot,
1379
+ usePivotBar: props.usePivotBar,
1380
+ useRowGroupSettings: props.useRowGroupSettings,
1381
+ useFieldSettings: props.useFieldSettings,
1382
+ useFormatSettings: props.useFormatSettings,
1383
+ useLayoutSettings: props.useLayoutSettings,
1384
+ useAggregateSettings: props.useAggregateSettings,
1385
+ // 툴바 필터 버튼 — 중첩 값을 직접 읽어 reactive 객체의 필드 변경도 추적(크롬이 값으로 비교).
1386
+ toolbarConfig: {
1387
+ filter: {
1388
+ disabled: (_b = (_a = props.toolbarConfig) == null ? void 0 : _a.filter) == null ? void 0 : _b.disabled,
1389
+ hidden: (_d = (_c = props.toolbarConfig) == null ? void 0 : _c.filter) == null ? void 0 : _d.hidden
1390
+ }
1391
+ }
1392
+ };
1393
+ },
1352
1394
  (next) => chrome == null ? void 0 : chrome.setUIOptions(next)
1353
1395
  );
1354
1396
  watch(
@@ -1366,8 +1408,17 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1366
1408
  (cfg) => chrome == null ? void 0 : chrome.setContextMenuConfig(cfg),
1367
1409
  { deep: true }
1368
1410
  );
1411
+ watch(
1412
+ () => props.chartConfig,
1413
+ (cfg) => {
1414
+ var _a;
1415
+ return (_a = chrome == null ? void 0 : chrome.setChartConfig) == null ? void 0 : _a.call(chrome, cfg);
1416
+ },
1417
+ { deep: true }
1418
+ );
1369
1419
  });
1370
1420
  onBeforeUnmount(() => {
1421
+ settingsCoordinator.destroy();
1371
1422
  clearChromeUnsubs();
1372
1423
  chrome == null ? void 0 : chrome.destroy();
1373
1424
  chrome = null;
@@ -1375,17 +1426,35 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1375
1426
  vueRenderer.unmountAll();
1376
1427
  });
1377
1428
  useGridPreset({
1429
+ usePreset: props.usePreset,
1378
1430
  name: toRef(props, "name"),
1379
1431
  type: coreConfig.value.mode === "pivot" ? "mozPivotGrid" : "mozFlatGrid",
1380
1432
  gridInstance,
1433
+ // 피벗 카탈로그(AvailableField)에는 컬럼 id 가 없다 — 선언 fields 의 field→id 로 실제 id 를 되찾는다.
1434
+ // (id 를 f.field 로 채우면 id≠field 그리드에서 Wijmo flat 변환·customCell 대상이 없는 id 로 해석된다.)
1435
+ getSourceFields: () => {
1436
+ const declared = coreConfig.value.fields ?? [];
1437
+ const idByField = new Map(declared.map((d2) => [d2.field ?? d2.id, d2.id]));
1438
+ return ((chrome == null ? void 0 : chrome.pivot.sourceFields) ?? []).map((f) => ({ id: idByField.get(f.field) ?? f.field, field: f.field, header: f.header, dataType: f.dataType }));
1439
+ },
1381
1440
  getCustomCellState: () => (chrome == null ? void 0 : chrome.customCell.getCustomCell()) ?? null,
1441
+ // null = 빈 상태로 초기화(프리셋 null 저장본·기본 보기 — F03). 컨트롤러 setter 가 null 을 빈 상태로 받는다.
1382
1442
  setCustomCellState: (s) => {
1383
- if (s && chrome) chrome.customCell.setCustomCell(s);
1443
+ chrome == null ? void 0 : chrome.customCell.setCustomCell(s ?? null);
1384
1444
  },
1385
1445
  // 컬럼 옵션 시각 서식(ColumnFormatController) — core getConfig 에 없는 wrapper 전용 상태라 별도 직렬화.
1386
1446
  getColumnFormatState: () => (chrome == null ? void 0 : chrome.columnFormat.getConfig()) ?? null,
1387
1447
  setColumnFormatState: (s) => {
1388
- if (s && chrome) chrome.columnFormat.setConfig(s);
1448
+ chrome == null ? void 0 : chrome.columnFormat.setConfig(s ?? null);
1449
+ },
1450
+ // 보기 설정 토글(툴바 펼침/푸터/그룹 패널) + 피벗 옵션 바 표시 — wrapper 소유 보기 상태.
1451
+ getViewOptionsState: () => {
1452
+ var _a;
1453
+ return ((_a = chrome == null ? void 0 : chrome.getPresetViewState) == null ? void 0 : _a.call(chrome)) ?? null;
1454
+ },
1455
+ setViewOptionsState: (s) => {
1456
+ var _a;
1457
+ (_a = chrome == null ? void 0 : chrome.setPresetViewState) == null ? void 0 : _a.call(chrome, s);
1389
1458
  }
1390
1459
  });
1391
1460
  const exposed = new Proxy({}, {
@@ -1422,7 +1491,7 @@ const _export_sfc = (sfc, props) => {
1422
1491
  }
1423
1492
  return target;
1424
1493
  };
1425
- const MozGrid = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-a719186c"]]);
1494
+ const MozGrid = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-3e1ac7f7"]]);
1426
1495
  const CellTemplate = defineComponent({
1427
1496
  name: "CellTemplate",
1428
1497
  props: {
@@ -1566,6 +1635,8 @@ const DetailTemplate = defineComponent({
1566
1635
  return () => null;
1567
1636
  }
1568
1637
  });
1638
+ const GROUP_TEMPLATE_DISPOSE_EVENT = "ps-group-template-dispose";
1639
+ const GROUP_TEMPLATE_HOST_CLASS = "ps-group-template-host";
1569
1640
  const GroupTemplate = defineComponent({
1570
1641
  name: "GroupTemplate",
1571
1642
  setup() {
@@ -1576,16 +1647,21 @@ const GroupTemplate = defineComponent({
1576
1647
  console.warn("[MozGridVue] GroupTemplate은 MozGridVue 내부에서 사용해야 합니다.");
1577
1648
  return () => null;
1578
1649
  }
1579
- onMounted(() => {
1580
- const grid = gridRef.value;
1581
- if (!grid || !slots.default) return;
1650
+ const instances = /* @__PURE__ */ new Map();
1651
+ const dispose = (container) => {
1652
+ const instance2 = instances.get(container);
1653
+ if (!instance2) return;
1654
+ instances.delete(container);
1655
+ instance2.destroy();
1656
+ };
1657
+ const register = (grid) => {
1658
+ if (!slots.default) return;
1582
1659
  const slot = slots.default;
1583
1660
  grid.formatRow.addHandler("_groupTemplate", (info) => {
1584
1661
  if (info.type !== "group-row") return;
1585
1662
  const ctx = info.ctx;
1586
1663
  const container = ctx.element;
1587
1664
  if (!container) return;
1588
- const key = `group:${ctx.viewIndex}`;
1589
1665
  const slotProps = {
1590
1666
  groupKey: ctx.column,
1591
1667
  groupValue: ctx.value,
@@ -1593,17 +1669,35 @@ const GroupTemplate = defineComponent({
1593
1669
  count: ctx.itemCount,
1594
1670
  isExpanded: !ctx.collapsed
1595
1671
  };
1596
- renderer.mount(key, container, {
1597
- render() {
1598
- return slot(slotProps);
1599
- }
1600
- }, slotProps);
1672
+ const existing = instances.get(container);
1673
+ if (existing && container.classList.contains(GROUP_TEMPLATE_HOST_CLASS)) {
1674
+ existing.update(slotProps);
1675
+ return;
1676
+ }
1677
+ if (existing) dispose(container);
1678
+ const instance2 = renderer.mountInstance(
1679
+ container,
1680
+ (props) => slot(props),
1681
+ slotProps
1682
+ );
1683
+ instances.set(container, instance2);
1684
+ container.addEventListener(GROUP_TEMPLATE_DISPOSE_EVENT, () => dispose(container), { once: true });
1601
1685
  });
1602
- });
1686
+ };
1687
+ const unregister = (grid) => {
1688
+ grid == null ? void 0 : grid.formatRow.removeHandler("_groupTemplate");
1689
+ for (const container of [...instances.keys()]) dispose(container);
1690
+ };
1691
+ let registeredGrid = null;
1692
+ watch(gridRef, (grid) => {
1693
+ if (grid === registeredGrid) return;
1694
+ unregister(registeredGrid);
1695
+ registeredGrid = grid ?? null;
1696
+ if (grid) register(grid);
1697
+ }, { immediate: true });
1603
1698
  onBeforeUnmount(() => {
1604
- var _a;
1605
- renderer == null ? void 0 : renderer.unmountByPrefix("group:");
1606
- (_a = gridRef.value) == null ? void 0 : _a.formatRow.removeHandler("_groupTemplate");
1699
+ unregister(registeredGrid);
1700
+ registeredGrid = null;
1607
1701
  });
1608
1702
  return () => null;
1609
1703
  }