@vmscloud/moz-ui-grid-vue 0.2.9 → 0.2.11

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 { stampPresetVersion, migrateGridPreset, applyPresetCoreConfig, 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";
@@ -24,9 +24,23 @@ class UserLayoutInstance {
24
24
  __publicField(this, "_default");
25
25
  __publicField(this, "_importDef");
26
26
  __publicField(this, "_exportDef");
27
- __publicField(this, "_pendingDef");
28
27
  __publicField(this, "_pending");
29
- __publicField(this, "dispose", () => {
28
+ __publicField(this, "_disposed", false);
29
+ __publicField(this, "_sequence", 0);
30
+ __publicField(this, "_active");
31
+ __publicField(this, "_queued");
32
+ __publicField(this, "_lastApplyResult");
33
+ __publicField(this, "_disposeCallback", () => {
34
+ });
35
+ /** 요청 접수 당시 preset에 완료를 귀속하는 registry 내부 hook. */
36
+ __publicField(this, "captureApplied");
37
+ __publicField(this, "disposeInstance", () => {
38
+ if (this._disposed) return;
39
+ this._disposed = true;
40
+ if (this._active) this.finish(this._active, "disposed");
41
+ if (this._queued) this.finish(this._queued, "disposed");
42
+ this._queued = void 0;
43
+ this._disposeCallback();
30
44
  });
31
45
  __publicField(this, "setApplied", () => {
32
46
  });
@@ -39,7 +53,6 @@ class UserLayoutInstance {
39
53
  this._exportDef = exportDef;
40
54
  this._default = exportDef();
41
55
  this._pending = false;
42
- this._pendingDef = null;
43
56
  this.strict = strict ?? false;
44
57
  this.dependencies = dependencies ?? null;
45
58
  if (!manualLifeCycle) {
@@ -48,21 +61,30 @@ class UserLayoutInstance {
48
61
  });
49
62
  }
50
63
  }
64
+ get disposed() {
65
+ return this._disposed;
66
+ }
67
+ get applying() {
68
+ return !!this._active || !!this._queued;
69
+ }
70
+ get lastApplyResult() {
71
+ return this._lastApplyResult;
72
+ }
73
+ get requestId() {
74
+ return this._sequence;
75
+ }
76
+ get dispose() {
77
+ return this.disposeInstance;
78
+ }
79
+ set dispose(callback) {
80
+ this._disposeCallback = callback;
81
+ }
51
82
  get pending() {
52
83
  return this._pending;
53
84
  }
54
85
  set pending(value) {
55
86
  this._pending = value;
56
- if (!value && this._pendingDef) {
57
- try {
58
- this._importDef(this._pendingDef);
59
- this.setApplied(true);
60
- } catch (e2) {
61
- console.warn(`[UserLayoutInstance] id="${this._id}" importDef 실패:`, e2);
62
- } finally {
63
- this._pendingDef = null;
64
- }
65
- }
87
+ this.drain();
66
88
  }
67
89
  get id() {
68
90
  return this._id;
@@ -80,12 +102,84 @@ class UserLayoutInstance {
80
102
  return this._exportDef();
81
103
  }
82
104
  set current(value) {
83
- if (this.pending) {
84
- this._pendingDef = value;
85
- return;
105
+ void this.apply(value);
106
+ }
107
+ /** 취소는 import 자체를 중단하지 않으며, 겹친 import는 직렬 실행합니다. */
108
+ apply(value) {
109
+ const requestId = ++this._sequence;
110
+ if (this._active) this.finish(this._active, "superseded");
111
+ if (this._queued) this.finish(this._queued, "superseded");
112
+ this._queued = void 0;
113
+ return new Promise((resolve) => {
114
+ var _a;
115
+ const request = {
116
+ requestId,
117
+ value,
118
+ resolve,
119
+ settled: false,
120
+ applied: this.setApplied
121
+ };
122
+ if (this._disposed) {
123
+ this.finish(request, "disposed");
124
+ return;
125
+ }
126
+ try {
127
+ request.applied = ((_a = this.captureApplied) == null ? void 0 : _a.call(this)) ?? this.setApplied;
128
+ } catch (error) {
129
+ this.finish(request, "failed", error);
130
+ return;
131
+ }
132
+ this._lastApplyResult = void 0;
133
+ this._queued = request;
134
+ this.drain();
135
+ });
136
+ }
137
+ finish(request, status, error) {
138
+ if (request.settled) return;
139
+ request.settled = true;
140
+ const result = { status, requestId: request.requestId };
141
+ if (error !== void 0) result.error = error;
142
+ if (request.requestId === this._sequence) this._lastApplyResult = result;
143
+ request.resolve(result);
144
+ }
145
+ drain() {
146
+ if (this._disposed || this._pending || this._active || !this._queued) return;
147
+ const request = this._queued;
148
+ this._queued = void 0;
149
+ this._active = request;
150
+ const complete = (result, error, failed = false) => {
151
+ try {
152
+ if (!request.settled) {
153
+ if (failed) {
154
+ console.warn(`[UserLayoutInstance] id="${this._id}" importDef 실패:`, error);
155
+ this.finish(request, "failed", error);
156
+ } else if (result && typeof result === "object" && "status" in result && result.status !== "applied") {
157
+ this.finish(request, result.status, result.error);
158
+ } else {
159
+ request.applied(true);
160
+ this.finish(request, "applied");
161
+ }
162
+ }
163
+ } catch (error2) {
164
+ this.finish(request, "failed", error2);
165
+ } finally {
166
+ this._active = void 0;
167
+ this.drain();
168
+ }
169
+ };
170
+ try {
171
+ const result = this._importDef(request.value);
172
+ if (result && typeof result.then === "function") {
173
+ Promise.resolve(result).then(
174
+ (value) => complete(value),
175
+ (error) => complete(void 0, error, true)
176
+ );
177
+ } else {
178
+ complete(result);
179
+ }
180
+ } catch (error) {
181
+ complete(void 0, error, true);
86
182
  }
87
- this._importDef(value);
88
- this.setApplied(true);
89
183
  }
90
184
  setDefaultToCurrent() {
91
185
  this._default = this._exportDef();
@@ -113,11 +207,13 @@ function useGridPreset(options) {
113
207
  return def;
114
208
  };
115
209
  const applyExtra = (set, value) => {
116
- if (value !== void 0) set == null ? void 0 : set(value);
210
+ if (value !== void 0) return set == null ? void 0 : set(value);
117
211
  };
118
- const applyPreset = async (preset) => {
119
- const grid = options.gridInstance.value;
120
- if (!grid) return;
212
+ const applyPreset = async (preset, grid, requestId) => {
213
+ const invalidOwner = () => {
214
+ if (layoutInstance.disposed) return { status: "disposed", requestId };
215
+ if (options.gridInstance.value !== grid) return { status: "superseded", requestId };
216
+ };
121
217
  const baseline = layoutInstance.default ?? null;
122
218
  const migrated = migrateGridPreset(preset ?? baseline, {
123
219
  grid,
@@ -128,25 +224,39 @@ function useGridPreset(options) {
128
224
  if (options.onImportDiagnostics) options.onImportDiagnostics(migrated.diagnostics);
129
225
  else console.warn("[Grid preset compatibility]", migrated.diagnostics);
130
226
  }
227
+ const beforeCore = invalidOwner();
228
+ if (beforeCore) return beforeCore;
131
229
  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);
230
+ const extras = [
231
+ [options.setColumnFormatState, migrated.columnFormatConfig],
232
+ [options.setCustomCellState, migrated.customCellConfig],
233
+ [options.setViewOptionsState, migrated.viewOptionsConfig]
234
+ ];
235
+ for (const [set, value] of extras) {
236
+ const invalid = invalidOwner();
237
+ if (invalid) return invalid;
238
+ await applyExtra(set, value);
239
+ }
240
+ const beforeSettlement = invalidOwner();
241
+ if (beforeSettlement) return beforeSettlement;
242
+ await grid.waitForSettled();
243
+ return invalidOwner() ?? { status: "applied", requestId };
135
244
  };
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;
245
+ const importDef = async (preset) => {
246
+ const requestId = layoutInstance.requestId;
247
+ const grid = options.gridInstance.value;
248
+ await Promise.resolve();
249
+ if (layoutInstance.disposed) return { status: "disposed", requestId };
250
+ if (requestId !== layoutInstance.requestId || options.gridInstance.value !== grid) {
251
+ return { status: "superseded", requestId };
252
+ }
253
+ if (!grid) return { status: "failed", requestId, error: new Error("Grid preset: grid-not-ready") };
254
+ try {
255
+ return await applyPreset(preset, grid, requestId);
256
+ } catch (error) {
257
+ console.error("[Grid preset] import failed", error);
258
+ return { status: "failed", requestId, error };
259
+ }
150
260
  };
151
261
  const layoutInstance = new UserLayoutInstance({
152
262
  id: options.name.value,
@@ -155,18 +265,31 @@ function useGridPreset(options) {
155
265
  exportDef
156
266
  });
157
267
  let registered = false;
158
- watch(options.gridInstance, (grid) => {
159
- if (!grid || registered) return;
268
+ watch(options.gridInstance, (grid, _previous, onCleanup) => {
269
+ if (!grid || registered || layoutInstance.disposed) return;
160
270
  if (!options.name.value) return;
271
+ let active = true;
272
+ let scheduled = false;
273
+ const isCurrent = () => active && !layoutInstance.disposed && options.gridInstance.value === grid;
161
274
  const unsub = grid.on("data:loaded", () => {
162
- if (registered) return;
275
+ if (registered || scheduled || !isCurrent()) return;
276
+ scheduled = true;
163
277
  layoutInstance.setDefaultToCurrent();
164
- nextTick(() => {
165
- register(layoutInstance);
278
+ void nextTick(async () => {
279
+ if (!isCurrent() || registered) return;
166
280
  registered = true;
167
281
  unsub();
282
+ try {
283
+ await register(layoutInstance);
284
+ } catch (error) {
285
+ console.error("[Grid preset] registration failed", error);
286
+ }
168
287
  });
169
288
  });
289
+ onCleanup(() => {
290
+ active = false;
291
+ unsub();
292
+ });
170
293
  });
171
294
  }
172
295
  let instance = null;
@@ -1000,6 +1123,7 @@ const GRID_BULK_EDITOR_MODULE_KEY = Symbol("moz-grid-bulk-editor-module");
1000
1123
  const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1001
1124
  __name: "MozGrid",
1002
1125
  props: /* @__PURE__ */ mergeModels({
1126
+ sourceRevision: {},
1003
1127
  height: { default: "100%" },
1004
1128
  contextMenuConfig: {},
1005
1129
  emptyState: {},
@@ -1278,10 +1402,19 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1278
1402
  }
1279
1403
  chromeUnsubs = [];
1280
1404
  }
1281
- function createChrome() {
1405
+ const reportedErrors = /* @__PURE__ */ new WeakSet();
1406
+ function reportInitializationError(error) {
1407
+ if (error && typeof error === "object") {
1408
+ if (reportedErrors.has(error)) return;
1409
+ reportedErrors.add(error);
1410
+ }
1411
+ console.error("[MozGrid] initialization failed", error);
1412
+ }
1413
+ function createChrome(snapshot) {
1282
1414
  if (!hostRef.value) return null;
1283
1415
  vueRenderer.setAppContext(appContext);
1284
- const c2 = new GridChrome(hostRef.value, buildCoreConfig(), buildChromeOptions());
1416
+ const c2 = markRaw(new GridChrome(hostRef.value, buildCoreConfig(), { ...buildChromeOptions(), sourceRevision: props.sourceRevision }, snapshot));
1417
+ markRaw(c2.grid);
1285
1418
  chrome = c2;
1286
1419
  gridInstance.value = c2.grid;
1287
1420
  containerRef.value = hostRef.value;
@@ -1294,56 +1427,56 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1294
1427
  c2.syncAppliedConfig(buildCoreConfig());
1295
1428
  })
1296
1429
  );
1430
+ void c2.ready().catch(reportInitializationError);
1297
1431
  return c2;
1298
1432
  }
1299
- async function mountChrome() {
1300
- const c2 = createChrome();
1301
- if (!c2) return;
1302
- try {
1303
- await c2.ready();
1304
- if (!isCurrentChrome(c2)) return;
1433
+ let readyChrome = null;
1434
+ const settingsCoordinator = new LatestSettingsCoordinator({
1435
+ waitUntilSettled: async (request, signal) => {
1436
+ if (chrome == null ? void 0 : chrome.waitForReinitSettled) await chrome.waitForReinitSettled(signal, chrome.isSourceReplacement(request.config, request.sourceRevision));
1437
+ else await (chrome == null ? void 0 : chrome.ready());
1438
+ },
1439
+ apply: async (request, context) => {
1440
+ if (!chrome) return;
1441
+ let c2 = chrome;
1442
+ const { reinitRequired, completion } = c2.reconcileCoreConfig(request.config, request.sourceRevision);
1443
+ if (completion) await completion;
1444
+ if (reinitRequired) {
1445
+ const captureHost = c2;
1446
+ const snapshot = captureHost.captureReinitStateAsync ? await captureHost.captureReinitStateAsync(request.config, request.sourceRevision) : c2.captureReinitState(request.config, request.sourceRevision);
1447
+ if (!context.isCurrent() || chrome !== c2) return;
1448
+ clearChromeUnsubs();
1449
+ c2.destroy();
1450
+ chrome = null;
1451
+ vueRenderer.unmountAll();
1452
+ const next = createChrome(snapshot);
1453
+ if (!next) return;
1454
+ c2 = next;
1455
+ await c2.ready();
1456
+ if (chrome !== c2) return;
1457
+ await c2.restoreReinitState(snapshot);
1458
+ } else {
1459
+ await c2.ready();
1460
+ }
1461
+ if (!context.isCurrent() || chrome !== c2 || readyChrome === c2) return;
1462
+ readyChrome = c2;
1305
1463
  toolItemsProps.value = c2.getToolItemsProps();
1306
1464
  emit("ready", c2.grid, c2);
1307
- } catch (error) {
1308
- if (chrome === c2) console.error("[MozGrid] initialization failed", error);
1309
- }
1310
- }
1465
+ },
1466
+ onError: reportInitializationError
1467
+ });
1311
1468
  function reconcile() {
1312
- if (!chrome) return;
1313
- const { reinitRequired } = chrome.reconcileCoreConfig(buildCoreConfig());
1314
- if (reinitRequired) void reinitChrome();
1315
- }
1316
- function isCurrentChrome(candidate) {
1317
- return chrome === candidate;
1318
- }
1319
- async function reinitChrome() {
1320
- if (!chrome) return;
1321
- const snapshot = chrome.captureReinitState();
1322
- clearChromeUnsubs();
1323
- chrome.destroy();
1324
- chrome = null;
1325
- vueRenderer.unmountAll();
1326
- const c2 = createChrome();
1327
- if (!c2) return;
1328
- try {
1329
- await c2.ready();
1330
- if (!isCurrentChrome(c2)) return;
1331
- await c2.restoreReinitState(snapshot);
1332
- if (!isCurrentChrome(c2)) return;
1333
- toolItemsProps.value = c2.getToolItemsProps();
1334
- emit("ready", c2.grid, c2);
1335
- } catch (error) {
1336
- if (chrome === c2) console.error("[MozGrid] initialization failed", error);
1337
- }
1469
+ settingsCoordinator.request({ config: buildCoreConfig(), sourceRevision: props.sourceRevision });
1338
1470
  }
1339
1471
  onMounted(() => {
1340
- void mountChrome();
1472
+ createChrome();
1473
+ reconcile();
1341
1474
  watch(
1342
1475
  () => {
1343
1476
  const all = coreConfig.value;
1344
1477
  void all.data;
1345
1478
  void all.fields;
1346
- const rest = { _locale: resolvedLocale.value, _timeZone: resolvedTimeZone.value };
1479
+ const rest = { _locale: resolvedLocale.value, _timeZone: resolvedTimeZone.value, _sourceRevision: props.sourceRevision };
1347
1480
  for (const k2 in all) {
1348
1481
  if (k2 === "data" || k2 === "fields") continue;
1349
1482
  rest[k2] = all[k2];
@@ -1408,6 +1541,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1408
1541
  );
1409
1542
  });
1410
1543
  onBeforeUnmount(() => {
1544
+ settingsCoordinator.destroy();
1411
1545
  clearChromeUnsubs();
1412
1546
  chrome == null ? void 0 : chrome.destroy();
1413
1547
  chrome = null;
@@ -1480,7 +1614,7 @@ const _export_sfc = (sfc, props) => {
1480
1614
  }
1481
1615
  return target;
1482
1616
  };
1483
- const MozGrid = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-ecaeb65f"]]);
1617
+ const MozGrid = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-3e1ac7f7"]]);
1484
1618
  const CellTemplate = defineComponent({
1485
1619
  name: "CellTemplate",
1486
1620
  props: {