apostrophe 4.27.1 → 4.28.0
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/CHANGELOG.md +35 -0
- package/index.js +3 -0
- package/lib/stream-proxy.js +49 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +2 -11
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +38 -6
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +12 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +111 -41
- package/modules/@apostrophecms/area/ui/apos/components/AposBreadcrumbOperations.vue +1 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +22 -10
- package/modules/@apostrophecms/area/ui/apos/logic/AposAreaEditor.js +40 -0
- package/modules/@apostrophecms/asset/index.js +3 -2
- package/modules/@apostrophecms/attachment/index.js +270 -0
- package/modules/@apostrophecms/doc/index.js +8 -2
- package/modules/@apostrophecms/doc-type/index.js +81 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +18 -2
- package/modules/@apostrophecms/express/index.js +30 -1
- package/modules/@apostrophecms/file/index.js +71 -6
- package/modules/@apostrophecms/i18n/index.js +20 -1
- package/modules/@apostrophecms/image/index.js +11 -0
- package/modules/@apostrophecms/layout-widget/ui/apos/components/AposAreaLayoutEditor.vue +31 -6
- package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridLayout.vue +12 -10
- package/modules/@apostrophecms/login/index.js +43 -11
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +2 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +5 -0
- package/modules/@apostrophecms/page/index.js +9 -11
- package/modules/@apostrophecms/page-type/index.js +6 -1
- package/modules/@apostrophecms/piece-page-type/index.js +100 -13
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +1 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +28 -12
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
- package/modules/@apostrophecms/styles/lib/apiRoutes.js +25 -5
- package/modules/@apostrophecms/styles/lib/handlers.js +19 -0
- package/modules/@apostrophecms/styles/lib/methods.js +35 -12
- package/modules/@apostrophecms/styles/ui/apos/components/TheAposStyles.vue +7 -2
- package/modules/@apostrophecms/task/index.js +9 -1
- package/modules/@apostrophecms/template/views/outerLayoutBase.html +3 -0
- package/modules/@apostrophecms/ui/index.js +2 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButtonGroup.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/vue.js +2 -0
- package/modules/@apostrophecms/ui/ui/apos/stores/widget.js +12 -7
- package/modules/@apostrophecms/ui/ui/apos/stores/widgetGraph.js +461 -0
- package/modules/@apostrophecms/ui/ui/apos/universal/graph.js +452 -0
- package/modules/@apostrophecms/ui/ui/apos/universal/widgetGraph.js +10 -0
- package/modules/@apostrophecms/uploadfs/index.js +15 -1
- package/modules/@apostrophecms/url/index.js +419 -1
- package/package.json +6 -6
- package/test/add-missing-schema-fields-project/node_modules/.package-lock.json +131 -0
- package/test/external-front.js +1 -0
- package/test/files.js +135 -0
- package/test/login-requirements.js +145 -3
- package/test/static-build.js +2701 -0
- package/test/universal-graph.js +1135 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import { defineStore } from 'pinia';
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import WidgetGraph from '../universal/widgetGraph.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {import('../universal/widgetGraph.js').NodeMeta} NodeMeta
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} Widget
|
|
11
|
+
* @property {string} _id - Unique widget identifier.
|
|
12
|
+
* @property {string} type - Widget type name (e.g. '@apostrophecms/rich-text').
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} GraphEntry
|
|
17
|
+
* @property {WidgetGraph} graph - The directed graph instance.
|
|
18
|
+
* @property {import('vue').Ref<number>} revision - Reactive revision counter.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} DiscoveredChild
|
|
23
|
+
* @property {string} widgetId - The child widget's _id.
|
|
24
|
+
* @property {string} type - The child widget's type.
|
|
25
|
+
* @property {string} areaId - The _id of the area containing the child.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Object} ConsoleLine
|
|
30
|
+
* @property {string} text - The formatted text including %c placeholders.
|
|
31
|
+
* @property {string[]} styles - CSS style strings for each %c substitution.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export const useWidgetGraphStore = defineStore('widgetGraph', () => {
|
|
35
|
+
/** @type {Map<string, GraphEntry>} graphKey → { graph, revision } */
|
|
36
|
+
const graphs = new Map();
|
|
37
|
+
|
|
38
|
+
// ── public API ──
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Register a widget and discover its children from area fields
|
|
42
|
+
* in the schema. Handles node creation, edge creation, and
|
|
43
|
+
* revision bumping internally.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} graphKey
|
|
46
|
+
* @param {Widget} widget
|
|
47
|
+
* @param {Object} [options]
|
|
48
|
+
* @param {string} [options.areaId] - The _id of the area containing this widget.
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
51
|
+
function registerWidget(graphKey, widget, { areaId } = {}) {
|
|
52
|
+
try {
|
|
53
|
+
const { graph } = _ensure(graphKey);
|
|
54
|
+
const type = widget.type;
|
|
55
|
+
const schema = apos.modules[apos.area.widgetManagers[type]]?.schema || [];
|
|
56
|
+
|
|
57
|
+
graph.addNode(widget._id, {
|
|
58
|
+
type,
|
|
59
|
+
areaId
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Discover direct children from the widget's schema and register
|
|
63
|
+
// them as nodes with edges. Each child will recursively register
|
|
64
|
+
// its own children when its AposAreaWidget mounts.
|
|
65
|
+
const children = _discoverChildren(schema, widget);
|
|
66
|
+
for (const child of children) {
|
|
67
|
+
graph.addNode(child.widgetId, {
|
|
68
|
+
type: child.type,
|
|
69
|
+
areaId: child.areaId
|
|
70
|
+
});
|
|
71
|
+
graph.addEdge(widget._id, child.widgetId);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_bump(graphKey);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// eslint-disable-next-line no-console
|
|
77
|
+
console.warn(`[widgetGraph] registerWidget: ${e.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Remove a widget and its entire subtree.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} graphKey
|
|
85
|
+
* @param {string} widgetId
|
|
86
|
+
* @returns {void}
|
|
87
|
+
*/
|
|
88
|
+
function unregisterWidget(graphKey, widgetId) {
|
|
89
|
+
const entry = graphs.get(graphKey);
|
|
90
|
+
if (entry && entry.graph.hasNode(widgetId)) {
|
|
91
|
+
entry.graph.removeNode(widgetId);
|
|
92
|
+
_bump(graphKey);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Re-instantiate the graph for a graphKey (e.g. page refresh).
|
|
98
|
+
* Faster than clear() — allocates fresh internals.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} graphKey
|
|
101
|
+
* @returns {void}
|
|
102
|
+
*/
|
|
103
|
+
function resetGraph(graphKey) {
|
|
104
|
+
graphs.set(graphKey, {
|
|
105
|
+
graph: new WidgetGraph(),
|
|
106
|
+
revision: ref(0)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Delete the graph entry entirely (e.g. modal close).
|
|
112
|
+
*
|
|
113
|
+
* @param {string} graphKey
|
|
114
|
+
* @returns {void}
|
|
115
|
+
*/
|
|
116
|
+
function destroyGraph(graphKey) {
|
|
117
|
+
graphs.delete(graphKey);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get the WidgetGraph instance for a graphKey, or null.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} graphKey
|
|
124
|
+
* @returns {WidgetGraph | null}
|
|
125
|
+
*/
|
|
126
|
+
function getGraph(graphKey) {
|
|
127
|
+
return graphs.get(graphKey)?.graph ?? null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the reactive revision ref for a graphKey.
|
|
132
|
+
* Returns a ref(0) stub if no graph exists yet, so that
|
|
133
|
+
* computed properties don't break.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} graphKey
|
|
136
|
+
* @returns {import('vue').Ref<number>}
|
|
137
|
+
*/
|
|
138
|
+
function getRevision(graphKey) {
|
|
139
|
+
return graphs.get(graphKey)?.revision ?? ref(0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get the direct parent of a widget.
|
|
144
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} graphKey
|
|
147
|
+
* @param {string} nodeId
|
|
148
|
+
* @returns {string | null}
|
|
149
|
+
*/
|
|
150
|
+
function getParent(graphKey, nodeId) {
|
|
151
|
+
const { graph } = _read(graphKey);
|
|
152
|
+
return graph?.getParent(nodeId) ?? null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check if two widgets share the same direct parent.
|
|
157
|
+
* Two root widgets are NOT considered to share a parent.
|
|
158
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
159
|
+
*
|
|
160
|
+
* @param {string} graphKey
|
|
161
|
+
* @param {string} idA
|
|
162
|
+
* @param {string} idB
|
|
163
|
+
* @returns {boolean}
|
|
164
|
+
*/
|
|
165
|
+
function hasCommonParent(graphKey, idA, idB) {
|
|
166
|
+
const { graph } = _read(graphKey);
|
|
167
|
+
return graph?.hasCommonParent(idA, idB) ?? false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get all ancestor ids from the direct parent up to the root.
|
|
172
|
+
* Ordered nearest-first: [directParent, grandparent, …, root].
|
|
173
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} graphKey
|
|
176
|
+
* @param {string} nodeId
|
|
177
|
+
* @returns {string[]}
|
|
178
|
+
*/
|
|
179
|
+
function getAncestors(graphKey, nodeId) {
|
|
180
|
+
const { graph } = _read(graphKey);
|
|
181
|
+
return graph?.getAncestors(nodeId) ?? [];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if two widgets share any common ancestor.
|
|
186
|
+
* Two root widgets are NOT considered to share a common ancestor.
|
|
187
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
188
|
+
*
|
|
189
|
+
* @param {string} graphKey
|
|
190
|
+
* @param {string} idA
|
|
191
|
+
* @param {string} idB
|
|
192
|
+
* @returns {boolean}
|
|
193
|
+
*/
|
|
194
|
+
function hasCommonAncestor(graphKey, idA, idB) {
|
|
195
|
+
const { graph } = _read(graphKey);
|
|
196
|
+
return graph?.hasCommonAncestor(idA, idB) ?? false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Check if `candidateId` is an ancestor of `nodeId`.
|
|
201
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
202
|
+
*
|
|
203
|
+
* @param {string} graphKey
|
|
204
|
+
* @param {string} nodeId
|
|
205
|
+
* @param {string} candidateId
|
|
206
|
+
* @returns {boolean}
|
|
207
|
+
*/
|
|
208
|
+
function hasAncestor(graphKey, nodeId, candidateId) {
|
|
209
|
+
const { graph } = _read(graphKey);
|
|
210
|
+
return graph?.hasAncestor(nodeId, candidateId) ?? false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get the direct children of a widget.
|
|
215
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
216
|
+
*
|
|
217
|
+
* @param {string} graphKey
|
|
218
|
+
* @param {string} nodeId
|
|
219
|
+
* @returns {string[]}
|
|
220
|
+
*/
|
|
221
|
+
function getChildren(graphKey, nodeId) {
|
|
222
|
+
const { graph } = _read(graphKey);
|
|
223
|
+
return graph?.getChildren(nodeId) ?? [];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Check if `childId` is a direct child of `nodeId`.
|
|
228
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
229
|
+
*
|
|
230
|
+
* @param {string} graphKey
|
|
231
|
+
* @param {string} nodeId
|
|
232
|
+
* @param {string} childId
|
|
233
|
+
* @returns {boolean}
|
|
234
|
+
*/
|
|
235
|
+
function hasChild(graphKey, nodeId, childId) {
|
|
236
|
+
const { graph } = _read(graphKey);
|
|
237
|
+
return graph?.hasChild(nodeId, childId) ?? false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get all descendant ids (transitive children) of a widget.
|
|
242
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
243
|
+
*
|
|
244
|
+
* @param {string} graphKey
|
|
245
|
+
* @param {string} nodeId
|
|
246
|
+
* @returns {string[]}
|
|
247
|
+
*/
|
|
248
|
+
function getDescendants(graphKey, nodeId) {
|
|
249
|
+
const { graph } = _read(graphKey);
|
|
250
|
+
return graph?.getDescendants(nodeId) ?? [];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Check if `candidateId` is a descendant of `nodeId`.
|
|
255
|
+
* Reactive — triggers recomputation when the graph changes.
|
|
256
|
+
*
|
|
257
|
+
* @param {string} graphKey
|
|
258
|
+
* @param {string} nodeId
|
|
259
|
+
* @param {string} candidateId
|
|
260
|
+
* @returns {boolean}
|
|
261
|
+
*/
|
|
262
|
+
function hasDescendant(graphKey, nodeId, candidateId) {
|
|
263
|
+
const { graph } = _read(graphKey);
|
|
264
|
+
return graph?.hasDescendant(nodeId, candidateId) ?? false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ── internal ──
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Read a graph entry and touch its reactive revision counter.
|
|
271
|
+
* When called from within a Vue computed or watcher, this
|
|
272
|
+
* establishes a dependency so the consumer re-evaluates
|
|
273
|
+
* whenever the graph is mutated.
|
|
274
|
+
*
|
|
275
|
+
* @param {string} graphKey
|
|
276
|
+
* @returns {{ graph: WidgetGraph | null, revision: number }}
|
|
277
|
+
*/
|
|
278
|
+
function _read(graphKey) {
|
|
279
|
+
const entry = graphs.get(graphKey);
|
|
280
|
+
if (!entry) {
|
|
281
|
+
return {
|
|
282
|
+
graph: null,
|
|
283
|
+
revision: 0
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// Access .value to let Vue's reactivity system track this ref
|
|
287
|
+
const revision = entry.revision.value;
|
|
288
|
+
return {
|
|
289
|
+
graph: entry.graph,
|
|
290
|
+
revision
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Ensure a graph entry exists for the given key.
|
|
296
|
+
* Creates a new WidgetGraph and revision ref(0) if absent.
|
|
297
|
+
*
|
|
298
|
+
* @param {string} graphKey
|
|
299
|
+
* @returns {GraphEntry}
|
|
300
|
+
*/
|
|
301
|
+
function _ensure(graphKey) {
|
|
302
|
+
if (!graphs.has(graphKey)) {
|
|
303
|
+
graphs.set(graphKey, {
|
|
304
|
+
graph: new WidgetGraph(),
|
|
305
|
+
revision: ref(0)
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return graphs.get(graphKey);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Increment the reactive revision counter for a graph key,
|
|
313
|
+
* signalling dependents that the graph has changed.
|
|
314
|
+
*
|
|
315
|
+
* @param {string} graphKey
|
|
316
|
+
* @returns {void}
|
|
317
|
+
*/
|
|
318
|
+
function _bump(graphKey) {
|
|
319
|
+
const entry = graphs.get(graphKey);
|
|
320
|
+
if (entry) {
|
|
321
|
+
entry.revision.value++;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Recursively scan schema fields for nested area children.
|
|
327
|
+
* Traverses area, array, and object fields to find all
|
|
328
|
+
* widget items contained within. Only current schema widgets
|
|
329
|
+
* will be discovered. Returns flat list of direct children only.
|
|
330
|
+
*
|
|
331
|
+
* @param {Object[]} schema - Apostrophe field schema array.
|
|
332
|
+
* @param {Object} dataObj - The data object matching the schema.
|
|
333
|
+
* @returns {DiscoveredChild[]}
|
|
334
|
+
*/
|
|
335
|
+
function _discoverChildren(schema, dataObj) {
|
|
336
|
+
const children = [];
|
|
337
|
+
for (const field of schema || []) {
|
|
338
|
+
if (field.type === 'area') {
|
|
339
|
+
const area = dataObj[field.name];
|
|
340
|
+
if (area?.items?.length) {
|
|
341
|
+
for (const child of area.items) {
|
|
342
|
+
children.push({
|
|
343
|
+
widgetId: child._id,
|
|
344
|
+
type: child.type,
|
|
345
|
+
areaId: area._id
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} else if (field.type === 'array' && field.schema) {
|
|
350
|
+
for (const item of (dataObj[field.name] || [])) {
|
|
351
|
+
children.push(..._discoverChildren(field.schema, item));
|
|
352
|
+
}
|
|
353
|
+
} else if (field.schema) {
|
|
354
|
+
// object, or any future compound type with a nested schema
|
|
355
|
+
if (dataObj[field.name]) {
|
|
356
|
+
children.push(..._discoverChildren(field.schema, dataObj[field.name]));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return children;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ── debug ──
|
|
364
|
+
|
|
365
|
+
// Register window.aposWidgetGraph() when debug mode is enabled.
|
|
366
|
+
// Usage from browser console: aposWidgetGraph()
|
|
367
|
+
// Optionally pass a graphKey: aposWidgetGraph('some-doc-id')
|
|
368
|
+
if (typeof window !== 'undefined' && apos.ui.debug) {
|
|
369
|
+
const {
|
|
370
|
+
log, groupCollapsed, groupEnd, table
|
|
371
|
+
} = console;
|
|
372
|
+
window.aposWidgetGraph = function (graphKey) {
|
|
373
|
+
const entries = graphKey
|
|
374
|
+
? (graphs.has(graphKey) ? [ [ graphKey, graphs.get(graphKey) ] ] : [])
|
|
375
|
+
: [ ...graphs ];
|
|
376
|
+
|
|
377
|
+
if (graphKey && entries.length === 0) {
|
|
378
|
+
log('%c⚠ No graph for key "%s"', 'color:#f44', graphKey);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (entries.length === 0) {
|
|
382
|
+
log('%c⚠ No widget graphs registered.', 'color:#f44');
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
for (const [ key, { graph } ] of entries) {
|
|
387
|
+
const roots = graph.getRoots();
|
|
388
|
+
groupCollapsed(
|
|
389
|
+
`%cTree ${key} %c(${graph.size} nodes)`,
|
|
390
|
+
'color:#dcdcaa;font-weight:bold;font-size:13px',
|
|
391
|
+
'color:#999;font-weight:normal;font-size:12px'
|
|
392
|
+
);
|
|
393
|
+
roots.forEach((root, i) => {
|
|
394
|
+
const lines = _printTree(graph, root, '', i === roots.length - 1, true);
|
|
395
|
+
for (const line of lines) {
|
|
396
|
+
log(line.text, ...line.styles);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
groupEnd();
|
|
400
|
+
|
|
401
|
+
// Raw data table in its own collapsible group
|
|
402
|
+
groupCollapsed(
|
|
403
|
+
`%cData ${key} %c(${graph.size} rows)`,
|
|
404
|
+
'color:#9cdcfe;font-weight:bold;font-size:13px',
|
|
405
|
+
'color:#999;font-weight:normal;font-size:12px'
|
|
406
|
+
);
|
|
407
|
+
const rows = {};
|
|
408
|
+
for (const nodeId of graph.getNodes()) {
|
|
409
|
+
const meta = graph.getMeta(nodeId) || {};
|
|
410
|
+
rows[nodeId] = {
|
|
411
|
+
type: meta.type || '',
|
|
412
|
+
areaId: meta.areaId || '',
|
|
413
|
+
parent: graph.getParent(nodeId) || '(root)',
|
|
414
|
+
children: graph.getChildren(nodeId).length,
|
|
415
|
+
depth: graph.getDepth(nodeId)
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
table(rows);
|
|
419
|
+
groupEnd();
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
function _printTree(graph, nodeId, prefix, isLast, isRoot = false) {
|
|
424
|
+
const meta = graph.getMeta(nodeId);
|
|
425
|
+
const label = meta?.type || 'unknown';
|
|
426
|
+
const connector = isRoot ? '' : (isLast ? '└─ ' : '├─ ');
|
|
427
|
+
const childPrefix = isRoot ? '' : (prefix + (isLast ? ' ' : '│ '));
|
|
428
|
+
const result = [
|
|
429
|
+
{
|
|
430
|
+
text: `${prefix}${connector}%c${label}%c {${nodeId}}`,
|
|
431
|
+
styles: [ 'color:#4EC9B0;font-weight:bold', 'color:#888' ]
|
|
432
|
+
}
|
|
433
|
+
];
|
|
434
|
+
const children = graph.getChildren(nodeId);
|
|
435
|
+
children.forEach((childId, i) => {
|
|
436
|
+
result.push(
|
|
437
|
+
..._printTree(graph, childId, childPrefix, i === children.length - 1)
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
registerWidget,
|
|
446
|
+
unregisterWidget,
|
|
447
|
+
resetGraph,
|
|
448
|
+
destroyGraph,
|
|
449
|
+
getGraph,
|
|
450
|
+
getRevision,
|
|
451
|
+
getParent,
|
|
452
|
+
hasCommonParent,
|
|
453
|
+
getAncestors,
|
|
454
|
+
hasCommonAncestor,
|
|
455
|
+
hasAncestor,
|
|
456
|
+
getChildren,
|
|
457
|
+
hasChild,
|
|
458
|
+
getDescendants,
|
|
459
|
+
hasDescendant
|
|
460
|
+
};
|
|
461
|
+
});
|