@gramlot/gramlot-examples 0.3.3 → 0.3.5
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/README.md +6 -3
- package/package.json +4 -3
- package/src/gramlot_examples/catalog.json +54 -0
- package/src/gramlot_examples/gallery/dist/frame.js +548 -36
- package/src/gramlot_examples/gallery/dist/gallery.js +2467 -16677
- package/src/gramlot_examples/gallery/dist/inputs.js +12 -6
- package/src/gramlot_examples/gallery/dist/inspector.js +547 -35
- package/src/gramlot_examples/gallery/dist/layout.js +847 -28
- package/src/gramlot_examples/gallery/dist/notices.json +0 -24
- package/src/gramlot_examples/gallery/dist/theme-styles.js +1 -1
- package/src/gramlot_examples/gallery/gallery-page.js +77 -47
- package/src/gramlot_examples/gallery/gallery.css +61 -38
- package/src/gramlot_examples/gallery/guided-demo.js +151 -0
- package/src/gramlot_examples/gallery/page.js +6 -6
- package/src/gramlot_examples/index.js +1 -0
- package/src/gramlot_examples/pages/layout/01_border.js +80 -0
- package/src/gramlot_examples/pages/layout/01_border.md +23 -0
- package/src/gramlot_examples/pages/layout/02_nested.js +99 -0
- package/src/gramlot_examples/pages/layout/02_nested.md +23 -0
- package/src/gramlot_examples/pages/layout/03_rounded.js +111 -0
- package/src/gramlot_examples/pages/layout/03_rounded.md +25 -0
- package/src/gramlot_examples/pages/layout/04_drawer_handles.js +140 -0
- package/src/gramlot_examples/pages/layout/04_drawer_handles.md +33 -0
- package/src/gramlot_examples/pages/layout/05_nested_lab.js +251 -0
- package/src/gramlot_examples/pages/layout/05_nested_lab.md +25 -0
- package/src/gramlot_examples/pages/layout/06_border_events.js +118 -0
- package/src/gramlot_examples/pages/layout/06_border_events.md +25 -0
- package/src/gramlot_examples/pages/layout/07_tabs.js +300 -0
- package/src/gramlot_examples/pages/layout/07_tabs.md +37 -0
- package/src/gramlot_examples/pages/layout/08_stack.js +142 -0
- package/src/gramlot_examples/pages/layout/08_stack.md +27 -0
- package/src/gramlot_examples/pages/layout/README.md +15 -0
|
@@ -52,6 +52,239 @@
|
|
|
52
52
|
function readParameterEnums(node) {
|
|
53
53
|
return Object.fromEntries(Object.entries(node._getMeta("gramlot_component")?.parameters ?? {}).filter(([, spec]) => spec.enum).map(([name, spec]) => [name, spec.enum]));
|
|
54
54
|
}
|
|
55
|
+
var InspectorHistory = class {
|
|
56
|
+
#undo = [];
|
|
57
|
+
#redo = [];
|
|
58
|
+
#references = /* @__PURE__ */ new WeakMap();
|
|
59
|
+
constructor(gramlot) {
|
|
60
|
+
this.gramlot = gramlot;
|
|
61
|
+
this.Source = gramlot.src.source.constructor;
|
|
62
|
+
this.Bag = gramlot.data.constructor;
|
|
63
|
+
}
|
|
64
|
+
get state() {
|
|
65
|
+
return { undo: this.#undo.at(-1)?.label ?? "", redo: this.#redo.at(-1)?.label ?? "" };
|
|
66
|
+
}
|
|
67
|
+
clear() {
|
|
68
|
+
this.#undo = [];
|
|
69
|
+
this.#redo = [];
|
|
70
|
+
}
|
|
71
|
+
#ref(node) {
|
|
72
|
+
if (!this.#references.has(node)) this.#references.set(node, { node });
|
|
73
|
+
return this.#references.get(node);
|
|
74
|
+
}
|
|
75
|
+
#bind(ref, node) {
|
|
76
|
+
ref.node = node;
|
|
77
|
+
this.#references.set(node, ref);
|
|
78
|
+
}
|
|
79
|
+
#capture(value) {
|
|
80
|
+
if (!(value instanceof this.Bag)) return value;
|
|
81
|
+
return value.getNodes().map((node) => [
|
|
82
|
+
this.#ref(node),
|
|
83
|
+
node.nodeTag,
|
|
84
|
+
node.resolver,
|
|
85
|
+
Object.entries(node.getAttr()).sort(([a], [b]) => a.localeCompare(b)),
|
|
86
|
+
this.#capture(node.getValue(true))
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
#equal(a, b) {
|
|
90
|
+
return Object.is(a, b) || Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => this.#equal(v, b[i]));
|
|
91
|
+
}
|
|
92
|
+
#commit(label, capture, redo, undo) {
|
|
93
|
+
const before = capture();
|
|
94
|
+
let error;
|
|
95
|
+
try {
|
|
96
|
+
redo();
|
|
97
|
+
} catch (caught) {
|
|
98
|
+
error = caught;
|
|
99
|
+
}
|
|
100
|
+
const after = capture();
|
|
101
|
+
if (!this.#equal(before, after)) {
|
|
102
|
+
this.#undo.push({ label, capture, before, after, undo, redo });
|
|
103
|
+
this.#redo = [];
|
|
104
|
+
}
|
|
105
|
+
if (error) throw error;
|
|
106
|
+
}
|
|
107
|
+
#step(from, to, direction, expected, result) {
|
|
108
|
+
const action = from.at(-1);
|
|
109
|
+
if (!action) return;
|
|
110
|
+
if (this.gramlot.state === "disposed") throw new Error("Inspected page disconnected");
|
|
111
|
+
if (!this.#equal(action.capture(), action[expected])) throw new Error("The target changed outside the inspector. Undo/Redo would overwrite those changes.");
|
|
112
|
+
let error;
|
|
113
|
+
try {
|
|
114
|
+
action[direction]();
|
|
115
|
+
} catch (caught) {
|
|
116
|
+
error = caught;
|
|
117
|
+
}
|
|
118
|
+
if (this.#equal(action.capture(), action[result])) {
|
|
119
|
+
from.pop();
|
|
120
|
+
to.push(action);
|
|
121
|
+
} else if (!error) error = new Error("The application changed the target during Undo/Redo.");
|
|
122
|
+
if (error) throw error;
|
|
123
|
+
}
|
|
124
|
+
undo() {
|
|
125
|
+
this.#step(this.#undo, this.#redo, "undo", "after", "before");
|
|
126
|
+
}
|
|
127
|
+
redo() {
|
|
128
|
+
this.#step(this.#redo, this.#undo, "redo", "before", "after");
|
|
129
|
+
}
|
|
130
|
+
edit(node, attrs, value, changeAttrs, changeValue) {
|
|
131
|
+
const ref = this.#ref(node), oldAttrs = { ...node.getAttr() }, oldValue = node.getValue(true);
|
|
132
|
+
const capture = () => [ref.node.parentBag, this.#captureNode(ref.node)];
|
|
133
|
+
const write = (attributes, content) => {
|
|
134
|
+
if (changeAttrs) ref.node.setAttr(attributes, true, false, false);
|
|
135
|
+
if (changeValue) ref.node.setValue(content);
|
|
136
|
+
};
|
|
137
|
+
this.#commit("Edit value or attributes", capture, () => write(attrs, value), () => write(oldAttrs, oldValue));
|
|
138
|
+
}
|
|
139
|
+
#captureNode(node) {
|
|
140
|
+
return [node.resolver, Object.entries(node.getAttr()).sort(([a], [b]) => a.localeCompare(b)), this.#capture(node.getValue(true))];
|
|
141
|
+
}
|
|
142
|
+
#sourceCapture = () => this.#capture(this.gramlot.src.source);
|
|
143
|
+
#node(path) {
|
|
144
|
+
if (typeof path !== "string" || !path) throw new Error("Select a Source node");
|
|
145
|
+
const node = this.gramlot.src.source.getNode(path);
|
|
146
|
+
if (!node || node.resolver) throw new Error("Source node is missing or deferred");
|
|
147
|
+
return node;
|
|
148
|
+
}
|
|
149
|
+
#removable(node) {
|
|
150
|
+
if (node.parentBag === this.gramlot.src.source) throw new Error("The page Source root cannot be removed or moved");
|
|
151
|
+
}
|
|
152
|
+
#saved(node) {
|
|
153
|
+
return { ref: this.#ref(node), label: node.label, tag: node.nodeTag, value: node.getValue(true), attrs: { ...node.getAttr() } };
|
|
154
|
+
}
|
|
155
|
+
#restore(saved, bag, index) {
|
|
156
|
+
if (bag.getNode(saved.label)) throw new Error(`Source label already exists: ${saved.label}`);
|
|
157
|
+
try {
|
|
158
|
+
bag.setItem(saved.label, saved.value, saved.attrs, index, false, false, "inspector", false, true, null, saved.tag);
|
|
159
|
+
} finally {
|
|
160
|
+
const inserted = bag.getNode(saved.label);
|
|
161
|
+
if (inserted) this.#bind(saved.ref, inserted);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
#parentState(parent) {
|
|
165
|
+
return { value: parent.getValue(true), attrs: { ...parent.getAttr() } };
|
|
166
|
+
}
|
|
167
|
+
#setParent(parent, state) {
|
|
168
|
+
parent.setValue(state.value, true, state.attrs, false);
|
|
169
|
+
parent.setAttr(state.attrs, true, false, false);
|
|
170
|
+
}
|
|
171
|
+
childFields(parent) {
|
|
172
|
+
const specifications = parent._getMeta("gramlot_component")?.childParameters ?? {};
|
|
173
|
+
return Object.entries(specifications).filter(([, spec]) => spec.required).map(([name, spec]) => {
|
|
174
|
+
const children = parent.getValue(true);
|
|
175
|
+
const used = spec.unique && children instanceof this.Source ? children.getNodes().map((child) => child.getAttr(name)) : [];
|
|
176
|
+
return { name, choices: spec.enum.filter((value) => !used.includes(value)) };
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
validateChild(parent, attrs, moving = null) {
|
|
180
|
+
const specifications = parent._getMeta("gramlot_component")?.childParameters ?? {};
|
|
181
|
+
for (const [name, spec] of Object.entries(specifications)) {
|
|
182
|
+
const value = attrs[name];
|
|
183
|
+
if (spec.required && value == null) throw new Error(`Choose ${name} before inserting the child`);
|
|
184
|
+
if (spec.enum && value != null && !spec.enum.includes(value)) throw new Error(`Invalid ${name}: ${value}`);
|
|
185
|
+
const children = parent.getValue(true);
|
|
186
|
+
if (spec.unique && children instanceof this.Source && children.getNodes().some((child) => child !== moving && child.getAttr(name) === value)) {
|
|
187
|
+
throw new Error(`${name} is already in use: ${value}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catalog(path) {
|
|
192
|
+
const node = this.#node(path), builder = node.builder;
|
|
193
|
+
return Object.entries(builder.schema).filter(([, info]) => !info._meta?.data_element && !info._meta?.subbuilder && !info._meta?.container).map(([tag, info]) => ({ tag, kind: info._meta?.gramlot_component ? "component" : "html", fields: this.childFields(node) }));
|
|
194
|
+
}
|
|
195
|
+
insert(path, requestedTag, attrs = {}) {
|
|
196
|
+
const parent = this.#node(path), builder = parent.builder;
|
|
197
|
+
const tag = builder.schemaTag(requestedTag);
|
|
198
|
+
if (!this.catalog(path).some((entry) => entry.tag === tag)) throw new Error("Choose an available component or HTML tag");
|
|
199
|
+
this.validateChild(parent, attrs);
|
|
200
|
+
const previous = this.#parentState(parent), parentRef = this.#ref(parent);
|
|
201
|
+
let saved, promoted;
|
|
202
|
+
const redo = () => {
|
|
203
|
+
if (saved) {
|
|
204
|
+
if (!(previous.value instanceof this.Source)) this.#setParent(parentRef.node, promoted);
|
|
205
|
+
this.#restore(saved, parentRef.node.getValue(true), parentRef.node.getValue(true).getNodes().length);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const existing = previous.value instanceof this.Source ? new Set(previous.value.getNodes()) : /* @__PURE__ */ new Set();
|
|
209
|
+
try {
|
|
210
|
+
builder.commandOnNode(parentRef.node, tag, null, attrs);
|
|
211
|
+
} finally {
|
|
212
|
+
const body = parentRef.node.getValue(true);
|
|
213
|
+
if (body instanceof this.Source) {
|
|
214
|
+
const added = body.getNodes().find((node) => !existing.has(node));
|
|
215
|
+
if (added) {
|
|
216
|
+
saved = this.#saved(added);
|
|
217
|
+
promoted = this.#parentState(parentRef.node);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const undo = () => {
|
|
223
|
+
if (saved) saved.ref.node.parentBag.popNode(saved.ref.node.label, "inspector");
|
|
224
|
+
if (!(previous.value instanceof this.Source)) this.#setParent(parentRef.node, previous);
|
|
225
|
+
};
|
|
226
|
+
this.#commit("Add element", this.#sourceCapture, redo, undo);
|
|
227
|
+
return `${path}.${saved.label}`;
|
|
228
|
+
}
|
|
229
|
+
remove(path) {
|
|
230
|
+
const node = this.#node(path);
|
|
231
|
+
this.#removable(node);
|
|
232
|
+
const saved = this.#saved(node), bag = node.parentBag, index = bag.getNodes().indexOf(node);
|
|
233
|
+
this.#commit(
|
|
234
|
+
"Remove branch",
|
|
235
|
+
this.#sourceCapture,
|
|
236
|
+
() => saved.ref.node.parentBag.popNode(saved.label, "inspector"),
|
|
237
|
+
() => this.#restore(saved, bag, index)
|
|
238
|
+
);
|
|
239
|
+
return path.slice(0, path.lastIndexOf("."));
|
|
240
|
+
}
|
|
241
|
+
move(path, destination, placement) {
|
|
242
|
+
if (!["before", "inside", "after"].includes(placement)) throw new Error("Invalid drop position");
|
|
243
|
+
const node = this.#node(path), target = this.#node(destination);
|
|
244
|
+
this.#removable(node);
|
|
245
|
+
if (path === destination || destination.startsWith(path + ".")) throw new Error("A branch cannot be moved into itself");
|
|
246
|
+
const parent = placement === "inside" ? target : target.parentBag.parentNode;
|
|
247
|
+
if (!parent) throw new Error("Drop inside the page Source root");
|
|
248
|
+
const builder = parent.builder;
|
|
249
|
+
if (node.builder !== builder) throw new Error("Branches cannot cross builder dialects");
|
|
250
|
+
this.validateChild(parent, node.getAttr(), node);
|
|
251
|
+
const parentRef = this.#ref(parent);
|
|
252
|
+
const previous = this.#parentState(parent), origin = node.parentBag, oldIndex = origin.getNodes().indexOf(node);
|
|
253
|
+
const body = previous.value;
|
|
254
|
+
if (body instanceof this.Source && body !== origin && body.getNode(node.label)) throw new Error(`Source label already exists: ${node.label}`);
|
|
255
|
+
const children = body instanceof this.Source ? body.getNodes().filter((child) => child !== node) : [];
|
|
256
|
+
if (parent.nodeTag) {
|
|
257
|
+
builder.validateParent(node.nodeTag, parent.nodeTag);
|
|
258
|
+
builder.validateChildren(parent.nodeTag, [...children.map((child) => child.nodeTag), node.nodeTag]);
|
|
259
|
+
}
|
|
260
|
+
let index = placement === "inside" ? children.length : children.indexOf(target) + (placement === "after" ? 1 : 0);
|
|
261
|
+
if (index < 0) throw new Error("Drop target changed");
|
|
262
|
+
if (body === origin) {
|
|
263
|
+
const ref = this.#ref(node);
|
|
264
|
+
this.#commit("Move branch", this.#sourceCapture, () => origin.move(origin.getNodes().indexOf(ref.node), index), () => origin.move(origin.getNodes().indexOf(ref.node), oldIndex));
|
|
265
|
+
} else {
|
|
266
|
+
const saved = this.#saved(node);
|
|
267
|
+
const destinationBag = body instanceof this.Source ? body : new this.Source(null, builder);
|
|
268
|
+
let promoted;
|
|
269
|
+
this.#commit("Move branch", this.#sourceCapture, () => {
|
|
270
|
+
if (!(body instanceof this.Source)) {
|
|
271
|
+
if (promoted) this.#setParent(parentRef.node, promoted);
|
|
272
|
+
else {
|
|
273
|
+
builder.promoteNodeValue(parentRef.node, body, destinationBag);
|
|
274
|
+
promoted = this.#parentState(parentRef.node);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
saved.ref.node.parentBag.popNode(saved.label, "inspector");
|
|
278
|
+
this.#restore(saved, destinationBag, index);
|
|
279
|
+
}, () => {
|
|
280
|
+
saved.ref.node.parentBag.popNode(saved.label, "inspector");
|
|
281
|
+
this.#restore(saved, origin, oldIndex);
|
|
282
|
+
if (!(body instanceof this.Source)) this.#setParent(parentRef.node, previous);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return `${placement === "inside" ? destination : destination.slice(0, destination.lastIndexOf("."))}.${node.label}`;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
55
288
|
var InspectorStyles = class {
|
|
56
289
|
#document;
|
|
57
290
|
#element = null;
|
|
@@ -127,6 +360,7 @@
|
|
|
127
360
|
const document2 = gramlot.src.renderer.destination.ownerDocument;
|
|
128
361
|
const window2 = document2.defaultView;
|
|
129
362
|
const styles = new InspectorStyles(document2);
|
|
363
|
+
const history = new InspectorHistory(gramlot);
|
|
130
364
|
const subscriber = `inspector-bridge-${window2.crypto.getRandomValues(new Uint32Array(4)).join("-")}`;
|
|
131
365
|
const identities = /* @__PURE__ */ new WeakMap();
|
|
132
366
|
const versions = /* @__PURE__ */ new WeakMap();
|
|
@@ -161,7 +395,8 @@
|
|
|
161
395
|
const snapshot = () => ({
|
|
162
396
|
data: describe(bag("data"), "data"),
|
|
163
397
|
source: describe(bag("source"), "source"),
|
|
164
|
-
breakpoints: [...breakpoints.values()].map(({ test, ...record }) => record)
|
|
398
|
+
breakpoints: [...breakpoints.values()].map(({ test, ...record }) => record),
|
|
399
|
+
history: history.state
|
|
165
400
|
});
|
|
166
401
|
const send = (data) => {
|
|
167
402
|
if (!stopped) port.postMessage(data);
|
|
@@ -234,8 +469,7 @@
|
|
|
234
469
|
if (node.resolver || !scalarType(node.getValue(true))) throw new Error("This value is read-only");
|
|
235
470
|
value = decodeScalar2(command.value);
|
|
236
471
|
}
|
|
237
|
-
|
|
238
|
-
if (Object.hasOwn(command, "value")) node.setValue(value);
|
|
472
|
+
history.edit(node, attrs, value, !!(command.set.length || command.remove.length), Object.hasOwn(command, "value"));
|
|
239
473
|
};
|
|
240
474
|
const setBreakpoint = (command) => {
|
|
241
475
|
bag(command.root);
|
|
@@ -261,10 +495,27 @@
|
|
|
261
495
|
send({ type: "result", id: message.id, ok: true, styles: styles.snapshot() });
|
|
262
496
|
return;
|
|
263
497
|
}
|
|
264
|
-
|
|
498
|
+
let selection;
|
|
499
|
+
if (message.type === "catalog") {
|
|
500
|
+
send({ type: "result", id: message.id, ok: true, catalog: history.catalog(message.command.path) });
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (["insert", "remove", "move"].includes(message.type)) {
|
|
504
|
+
const command = message.command, node = bag("source").getNode(command.path);
|
|
505
|
+
if (!node || identity(node) !== command.nodeId || (versions.get(node) ?? 0) !== command.revision) throw new Error("Source node changed. Select it again.");
|
|
506
|
+
if (message.type === "insert") selection = history.insert(command.path, command.tag, command.attrs);
|
|
507
|
+
if (message.type === "remove") selection = history.remove(command.path);
|
|
508
|
+
if (message.type === "move") {
|
|
509
|
+
const target = bag("source").getNode(command.destination);
|
|
510
|
+
if (!target || identity(target) !== command.targetId || (versions.get(target) ?? 0) !== command.targetRevision) throw new Error("Drop target changed. Try again.");
|
|
511
|
+
selection = history.move(command.path, command.destination, command.placement);
|
|
512
|
+
}
|
|
513
|
+
} else if (message.type === "undo") history.undo();
|
|
514
|
+
else if (message.type === "redo") history.redo();
|
|
515
|
+
else if (message.type === "edit") edit(message.command);
|
|
265
516
|
else if (message.type === "breakpoint") setBreakpoint(message.command);
|
|
266
517
|
else if (message.type !== "refresh") throw new Error(`Unknown inspector command: ${message.type}`);
|
|
267
|
-
send({ type: "result", id: message.id, ok: true, snapshot: snapshot() });
|
|
518
|
+
send({ type: "result", id: message.id, ok: true, selection, snapshot: snapshot() });
|
|
268
519
|
} catch (error) {
|
|
269
520
|
send({ type: "result", id: message.id, ok: false, error: error.message });
|
|
270
521
|
}
|
|
@@ -275,6 +526,7 @@
|
|
|
275
526
|
stopped = true;
|
|
276
527
|
for (const root of roots) bag(root).unsubscribe(subscriber, { any: true });
|
|
277
528
|
breakpoints.clear();
|
|
529
|
+
history.clear();
|
|
278
530
|
styles.dispose();
|
|
279
531
|
port.removeEventListener("message", receive);
|
|
280
532
|
window2.removeEventListener("pagehide", dispose);
|
|
@@ -306,6 +558,7 @@
|
|
|
306
558
|
error = "";
|
|
307
559
|
breakpoints = [];
|
|
308
560
|
styles = null;
|
|
561
|
+
history = { undo: "", redo: "" };
|
|
309
562
|
#info = /* @__PURE__ */ new WeakMap();
|
|
310
563
|
#listeners = /* @__PURE__ */ new Set();
|
|
311
564
|
#pending = /* @__PURE__ */ new Map();
|
|
@@ -337,6 +590,7 @@
|
|
|
337
590
|
if (message.snapshot) {
|
|
338
591
|
for (const root of roots2) this.#reconcile(this[root], message.snapshot[root]);
|
|
339
592
|
this.breakpoints = message.snapshot.breakpoints;
|
|
593
|
+
this.history = message.snapshot.history;
|
|
340
594
|
this.state = "started";
|
|
341
595
|
this.error = "";
|
|
342
596
|
} else if (message.type === "error") this.error = message.error;
|
|
@@ -347,7 +601,7 @@
|
|
|
347
601
|
const pending = this.#pending.get(message.id);
|
|
348
602
|
if (pending) {
|
|
349
603
|
this.#pending.delete(message.id);
|
|
350
|
-
if (message.ok && !this.error) pending.resolve();
|
|
604
|
+
if (message.ok && !this.error) pending.resolve(message);
|
|
351
605
|
else pending.reject(new Error(message.error || this.error));
|
|
352
606
|
}
|
|
353
607
|
}
|
|
@@ -410,8 +664,8 @@
|
|
|
410
664
|
this.#notify();
|
|
411
665
|
}
|
|
412
666
|
};
|
|
413
|
-
var gramlot_tools_inspector_default = ":host { all: initial; color-scheme: light; font: 12px/1.4 system-ui, sans-serif; color: #38414b; }\n* { box-sizing: border-box; }\nbutton, input, select { font: inherit; color: inherit; }\nbutton { cursor: pointer; }\nbutton:focus-visible, input:focus-visible, select:focus-visible { outline: 1px solid #3574cb; outline-offset: -1px; }\n.tree { padding: 7px 3px; }\n.properties { background: white; }\n.properties-heading { font-size: 11px; display: flex; align-items: center; justify-content: space-between; height: 29px; padding: 0 8px; background: #f1f3f5; color: #6d7989; border-bottom: 1px solid #d1d7df; }\n.properties-heading button { background: white; border: 1px solid #c5cdda; border-radius: 2px; width: 23px; height: 23px; padding: 0; font-size: 18px; line-height: 20px; }\nfieldset { border: 0; margin: 0; padding: 0; min-width: 0; }\n.property-row { display: grid; grid-template-columns: minmax(95px, 30%) minmax(0, 1fr); min-height: 20px; border-bottom: 1px solid #d1d7df; font-size: 11px; }\n.property-name { display: flex; align-items: center; justify-content: flex-end; background: #888; color: white; font-weight: 700; padding: 1px 6px; border-right: 1px solid #d1d7df; overflow-wrap: anywhere; text-align: right; }\n.property-value { display: flex; align-items: center; min-width: 0; }\n.property-row input { border: 0; border-radius: 0; min-width: 0; width: 100%; height: 19px; padding: 1px 5px; background: transparent; }\n.property-name input { color: white; text-align: right; padding: 0; }\n.property-value input { flex: 1; }\n.property-value input[type=checkbox] { flex: none; width: 14px; height: 14px; margin: 2px 6px; }\n.property-value select { max-width: 82px; height: 20px; padding: 0; border: 0; border-left: 1px solid #d1d7df; background: #f1f3f5; font-size: 10px; }\n.remove { display: inline-flex; align-items: center; justify-content: center; margin-left: auto; flex: none; width: 22px; height: 20px; padding: 3px; border: 0; background: transparent; color: #7c8797; opacity: 0; }\n.remove svg { width: 13px; height: 13px; }\n.property-row:hover .remove, .property-row:focus-within .remove { opacity: 1; }\n.remove:hover { color: #b82323; background: #fbe9e9; }\n:disabled { cursor: default; opacity: .65; }\n.path { flex: none; padding: 5px 9px; border-top: 1px solid #c9cfd6; background: #f5f7f9; color: #697687; font-size: 11px; min-height: 27px; overflow-wrap: anywhere; }\n.error, .edit-status, .breakpoint-status, .breakpoint-line { font-size: 11px; margin: 0; padding: 3px 8px; overflow-wrap: anywhere; }\n.error, .edit-status, .breakpoint-status { color: #a12121; }\n.breakpoint-line { background: #fff2ed; color: #854127; border-bottom: 1px solid #d1d7df; }\n.error:empty, .edit-status:empty, .breakpoint-status:empty, .breakpoint-line:empty { display: none; }\n.breakpoint-dialog { width: min(340px, 90vw); color: #38414b; border: 1px solid #bbc3cf; border-radius: 4px; padding: 12px; box-shadow: 0 8px 28px #0003; font: 12px/1.4 system-ui, sans-serif; }\n.breakpoint-dialog::backdrop { background: #0002; }\n.breakpoint-dialog h2 { font-size: 13px; margin: 0 0 10px; overflow-wrap: anywhere; }\n.breakpoint-dialog select, .breakpoint-dialog input { width: 100%; border: 1px solid #bbc3cf; padding: 4px; margin: 0 0 7px; background: white; }\n.dialog-actions { display: flex; gap: 6px; justify-content: flex-end; margin-top: 7px; }\n.dialog-actions button { padding: 3px 10px; border: 1px solid #bbc3cf; border-radius: 3px; background: #f1f3f5; }\n.heading-label { flex: 1; }\n.properties-heading .breakpoint-count { width: auto; padding: 0 5px; margin-right: 5px; font-size: 11px; color: #b04a35; }\n.breakpoint-entry { display: flex; align-items: center; gap: 6px; padding: 4px 0; border-bottom: 1px solid #d1d7df; }\n.breakpoint-entry span { flex: 1; overflow-wrap: anywhere; }\n.breakpoint-entry button { background: transparent; border: 0; font-size: 17px; }\n[hidden] { display: none !important; }\n.targetbar { flex: none; height: 28px; padding: 2px 6px; background: #edf2fa; border-bottom: 1px solid #dadce0; }\n.targetbar select { width: 100%; height: 23px; border: 0; background: transparent; font-size: 11px; }\n.styles-panel { flex: 1; min-height: 0; overflow: auto; background: #fff; font-size: 11px; }\n.styles-toolbar { position: sticky; top: 0; z-index: 1; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 6px 8px; background: #f1f3f5; border-bottom: 1px solid #d1d7df; }\n.styles-toolbar span { flex: 1; font-weight: 600; }\n.styles-toolbar button, .styles-toolbar a { padding: 3px 7px; border: 1px solid #c5cdda; border-radius: 3px; background: white; }\n.styles-toolbar a { color: inherit; text-decoration: none; }\n.styles-toolbar a[aria-disabled=true] { cursor: default; opacity: .65; }\n.styles-toolbar a:focus-visible { outline: 1px solid #3574cb; outline-offset: -1px; }\n.styles-help, .styles-empty, .styles-status { margin: 0; padding: 7px 9px; color: #697687; }\n.styles-status { color: #a12121; }\n.styles-status:empty { display: none; }\n.styles-group h3 { margin: 0; padding: 6px 9px; font-size: 11px; background: #edf2fa; border-block: 1px solid #d1d7df; }\n.style-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 25px 23px; align-items: center; gap: 8px; min-width: 0; padding: 3px 9px; border-bottom: 1px solid #e8ebef; }\n.style-row label { min-width: 0; font: 11px/1.4 ui-monospace, monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.style-row input { min-width: 0; width: 100%; height: 23px; padding: 2px 5px; border: 1px solid #c5cdda; border-radius: 3px; background: white; }\n.style-row input[type=color] { width: 25px; padding: 1px; cursor: pointer; }\n.style-row button { grid-column: 4; width: 23px; height: 23px; padding: 0; border: 1px solid #c5cdda; border-radius: 3px; background: #f5f7f9; font-size: 15px; }\n\n:host([embedded]) { display: block; height: 100%; min-height: 0; }\n:host([embedded]) > div { height: 100%; }\n.embedded-panel { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }\n\n.property-value select.enum-value { flex: 1; min-width: 0; max-width: none; width: 100%; border: 0; background: white; font: inherit; padding: 1px 5px; }\n\n/* Presentation changes preserve the inspector runtime and selection. */\n:host([hidden]) { display: none; }\n:host([docked]) { display: block; height: 100%; min-height: 0; }\n:host([docked][hidden]) { display: none; }\n:host([docked]) > div { height: 100%; }\n:host([docked]) .targetbar select { min-height: 40px; font-size: 16px; }\n:host([docked]) .property-row { min-height: 36px; font-size: 13px; }\n:host([docked]) .property-row input { min-height: 35px; font-size: 16px; }\n:host([docked]) .remove { opacity: 1; min-height: 35px; width: 32px; }\n\n:host([docked]) { --tree-action-size: 28px; --tree-font-size: 14px; --tree-row-height: 40px; --separator-size: 14px; }\n:host([docked]) .targetbar { height: 44px; }\n:host([docked]) gramlot-poc-tabs > button { height: 44px; font-size: 14px; }\n:host([docked]) .properties-heading { height: 40px; font-size: 13px; }\n:host([docked]) .properties-heading button { width: 36px; height: 36px; }\n:host([docked]) .property-value select { height: 35px; font-size: 14px; }\n";
|
|
414
|
-
var gramlot_poc_tree_default = "/* Copyright 2025-2026 Softwell S.r.l. - SPDX-License-Identifier: Apache-2.0\n * Inspector subset of the DevTools storeTree stylesheet and inspector.css variables. */\n:host { display:block; font:var(--tree-font-size,11px)/var(--tree-row-height,18px) -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; color:#38414b; }\n:host([hidden]) { display:none; }\nul { list-style:none; margin:0; padding-left:17px; }\n.tree-root { padding-left:calc(var(--tree-action-size,14px) + 2px); }\n.tree-root > ul { padding-left:2px; }\nsummary, .leaf { position:relative; }\nli { line-height:var(--tree-row-height,18px); }\nsummary { display:flex; align-items:center; gap:7px; padding:0 5px; cursor:pointer; user-select:none; list-style:none; }\nsummary::-webkit-details-marker { display:none; }\nsummary::before { content:''; width:5px; height:5px; border-right:1.5px solid #78818a; border-bottom:1.5px solid #78818a; transform:rotate(-45deg); flex:none; margin:0 3px; }\ndetails[open] > summary::before { transform:rotate(45deg); }\n.leaf { display:flex; align-items:center; gap:7px; padding:0 5px 0 23px; cursor:pointer; }\nsummary:hover,.leaf:hover { background:#f0f3f6; }\n.selected,.selected:hover { background:transparent; color:#315dbd; font-weight:700; border-radius:2px; }\n.caption { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n/* Reserve a left gutter; showing an action must not shift the node label. */\n.actions { position:absolute; left:calc(-1 * var(--tree-action-size,14px)); top:0; display:inline-flex; opacity:0; pointer-events:none; }\nsummary:hover > .actions,.leaf:hover > .actions,summary:focus-visible > .actions,.leaf:focus-visible > .actions,\n.actions:has(button:focus-visible),.actions:has(button.active) { opacity:1; pointer-events:auto; }\n.actions button { border:0; background:transparent; color:#80868b; font:inherit; font-size:11px; line-height:var(--tree-row-height,18px); width:var(--tree-action-size,14px); height:var(--tree-row-height,18px); padding:0; cursor:pointer; border-radius:3px; }\n.actions button:hover { background:#e1e5ed; }\n.actions button[data-action=breakpoint] { font-size:0; }\n.actions button[data-action=breakpoint]::before { content:''; display:inline-block; box-sizing:border-box; width:8px; height:8px; border:1.5px solid currentColor; border-radius:50%; vertical-align:middle; }\n.actions button.active { color:#b3261e; }\n.actions button.active.conditional { color:#f9ab00; }\n.actions button.active[data-action=breakpoint]::before { background:currentColor; }\n.actions button:focus-visible,summary:focus-visible,.leaf:focus-visible { outline:2px solid #356f9f; outline-offset:-2px; }\n\n@media (hover: none) { .actions { opacity:1; pointer-events:auto; } }\n";
|
|
667
|
+
var gramlot_tools_inspector_default = ":host { all: initial; color-scheme: light; font: 12px/1.4 system-ui, sans-serif; color: #38414b; }\n* { box-sizing: border-box; }\nbutton, input, select { font: inherit; color: inherit; }\nbutton { cursor: pointer; }\nbutton:focus-visible, input:focus-visible, select:focus-visible { outline: 1px solid #3574cb; outline-offset: -1px; }\n.tree { padding: 7px 3px; }\n.properties { background: white; }\n.properties-heading { font-size: 11px; display: flex; align-items: center; justify-content: space-between; height: 29px; padding: 0 8px; background: #f1f3f5; color: #6d7989; border-bottom: 1px solid #d1d7df; }\n.properties-heading button { background: white; border: 1px solid #c5cdda; border-radius: 2px; width: 23px; height: 23px; padding: 0; font-size: 18px; line-height: 20px; }\nfieldset { border: 0; margin: 0; padding: 0; min-width: 0; }\n.property-row { display: grid; grid-template-columns: minmax(95px, 30%) minmax(0, 1fr); min-height: 20px; border-bottom: 1px solid #d1d7df; font-size: 11px; }\n.property-name { display: flex; align-items: center; justify-content: flex-end; background: #888; color: white; font-weight: 700; padding: 1px 6px; border-right: 1px solid #d1d7df; overflow-wrap: anywhere; text-align: right; }\n.property-value { display: flex; align-items: center; min-width: 0; }\n.property-row input { border: 0; border-radius: 0; min-width: 0; width: 100%; height: 19px; padding: 1px 5px; background: transparent; }\n.property-name input { color: white; text-align: right; padding: 0; }\n.property-value input { flex: 1; }\n.property-value input[type=checkbox] { flex: none; width: 14px; height: 14px; margin: 2px 6px; }\n.property-value select { max-width: 82px; height: 20px; padding: 0; border: 0; border-left: 1px solid #d1d7df; background: #f1f3f5; font-size: 10px; }\n.remove { display: inline-flex; align-items: center; justify-content: center; margin-left: auto; flex: none; width: 22px; height: 20px; padding: 3px; border: 0; background: transparent; color: #7c8797; opacity: 0; }\n.remove svg { width: 13px; height: 13px; }\n.property-row:hover .remove, .property-row:focus-within .remove { opacity: 1; }\n.remove:hover { color: #b82323; background: #fbe9e9; }\n:disabled { cursor: default; opacity: .65; }\n.path { flex: none; padding: 5px 9px; border-top: 1px solid #c9cfd6; background: #f5f7f9; color: #697687; font-size: 11px; min-height: 27px; overflow-wrap: anywhere; }\n.error, .edit-status, .breakpoint-status, .breakpoint-line { font-size: 11px; margin: 0; padding: 3px 8px; overflow-wrap: anywhere; }\n.error, .edit-status, .breakpoint-status { color: #a12121; }\n.breakpoint-line { background: #fff2ed; color: #854127; border-bottom: 1px solid #d1d7df; }\n.error:empty, .edit-status:empty, .breakpoint-status:empty, .breakpoint-line:empty { display: none; }\n.breakpoint-dialog { width: min(340px, 90vw); color: #38414b; border: 1px solid #bbc3cf; border-radius: 4px; padding: 12px; box-shadow: 0 8px 28px #0003; font: 12px/1.4 system-ui, sans-serif; }\n.breakpoint-dialog::backdrop { background: #0002; }\n.breakpoint-dialog h2 { font-size: 13px; margin: 0 0 10px; overflow-wrap: anywhere; }\n.breakpoint-dialog select, .breakpoint-dialog input { width: 100%; border: 1px solid #bbc3cf; padding: 4px; margin: 0 0 7px; background: white; }\n.dialog-actions { display: flex; gap: 6px; justify-content: flex-end; margin-top: 7px; }\n.dialog-actions button { padding: 3px 10px; border: 1px solid #bbc3cf; border-radius: 3px; background: #f1f3f5; }\n.heading-label { flex: 1; }\n.properties-heading .breakpoint-count { width: auto; padding: 0 5px; margin-right: 5px; font-size: 11px; color: #b04a35; }\n.breakpoint-entry { display: flex; align-items: center; gap: 6px; padding: 4px 0; border-bottom: 1px solid #d1d7df; }\n.breakpoint-entry span { flex: 1; overflow-wrap: anywhere; }\n.breakpoint-entry button { background: transparent; border: 0; font-size: 17px; }\n[hidden] { display: none !important; }\n.targetbar { flex: none; height: 28px; padding: 2px 6px; background: #edf2fa; border-bottom: 1px solid #dadce0; }\n.targetbar select { width: 100%; height: 23px; border: 0; background: transparent; font-size: 11px; }\n.styles-panel { flex: 1; min-height: 0; overflow: auto; background: #fff; font-size: 11px; }\n.styles-toolbar { position: sticky; top: 0; z-index: 1; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 6px 8px; background: #f1f3f5; border-bottom: 1px solid #d1d7df; }\n.styles-toolbar span { flex: 1; font-weight: 600; }\n.styles-toolbar button, .styles-toolbar a { padding: 3px 7px; border: 1px solid #c5cdda; border-radius: 3px; background: white; }\n.styles-toolbar a { color: inherit; text-decoration: none; }\n.styles-toolbar a[aria-disabled=true] { cursor: default; opacity: .65; }\n.styles-toolbar a:focus-visible { outline: 1px solid #3574cb; outline-offset: -1px; }\n.styles-help, .styles-empty, .styles-status { margin: 0; padding: 7px 9px; color: #697687; }\n.styles-status { color: #a12121; }\n.styles-status:empty { display: none; }\n.styles-group h3 { margin: 0; padding: 6px 9px; font-size: 11px; background: #edf2fa; border-block: 1px solid #d1d7df; }\n.style-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 25px 23px; align-items: center; gap: 8px; min-width: 0; padding: 3px 9px; border-bottom: 1px solid #e8ebef; }\n.style-row label { min-width: 0; font: 11px/1.4 ui-monospace, monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.style-row input { min-width: 0; width: 100%; height: 23px; padding: 2px 5px; border: 1px solid #c5cdda; border-radius: 3px; background: white; }\n.style-row input[type=color] { width: 25px; padding: 1px; cursor: pointer; }\n.style-row button { grid-column: 4; width: 23px; height: 23px; padding: 0; border: 1px solid #c5cdda; border-radius: 3px; background: #f5f7f9; font-size: 15px; }\n\n:host([embedded]) { display: block; height: 100%; min-height: 0; }\n:host([embedded]) > div { height: 100%; }\n.embedded-panel { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }\n\n.property-value select.enum-value { flex: 1; min-width: 0; max-width: none; width: 100%; border: 0; background: white; font: inherit; padding: 1px 5px; }\n\n/* Presentation changes preserve the inspector runtime and selection. */\n:host([hidden]) { display: none; }\n:host([docked]) { display: block; height: 100%; min-height: 0; }\n:host([docked][hidden]) { display: none; }\n:host([docked]) > div { height: 100%; }\n:host([docked]) .targetbar select { min-height: 40px; font-size: 16px; }\n:host([docked]) .property-row { min-height: 36px; font-size: 13px; }\n:host([docked]) .property-row input { min-height: 35px; font-size: 16px; }\n:host([docked]) .remove { opacity: 1; min-height: 35px; width: 32px; }\n\n:host([docked]) { --tree-action-size: 28px; --tree-font-size: 14px; --tree-row-height: 40px; --separator-size: 14px; }\n:host([docked]) .targetbar { height: 44px; }\n:host([docked]) gramlot-poc-tabs > button { height: 44px; font-size: 14px; }\n:host([docked]) .properties-heading { height: 40px; font-size: 13px; }\n:host([docked]) .properties-heading button { width: 36px; height: 36px; }\n:host([docked]) .property-value select { height: 35px; font-size: 14px; }\n\n.history-toolbar { flex:none; display:flex; align-items:center; gap:6px; padding:4px 8px; border-bottom:1px solid #d1d7df; background:#f6f8fb; font:11px/1.4 system-ui,sans-serif; }\n.history-toolbar button { flex:none; padding:3px 6px; border:1px solid #c5cdda; border-radius:3px; background:white; cursor:pointer; }\n.history-toolbar button:disabled { opacity:.45; cursor:default; }\n.history-toolbar span { min-width:0; color:#a12121; overflow-wrap:anywhere; }\n";
|
|
668
|
+
var gramlot_poc_tree_default = "/* Copyright 2025-2026 Softwell S.r.l. - SPDX-License-Identifier: Apache-2.0\n * Inspector subset of the DevTools storeTree stylesheet and inspector.css variables. */\n:host { display:block; font:var(--tree-font-size,11px)/var(--tree-row-height,18px) -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; color:#38414b; }\n:host([hidden]) { display:none; }\nul { list-style:none; margin:0; padding-left:17px; }\n.tree-root { padding-left:calc(var(--tree-action-size,14px) + 2px); }\n.tree-root > ul { padding-left:2px; }\nsummary, .leaf { position:relative; }\nli { line-height:var(--tree-row-height,18px); }\nsummary { display:flex; align-items:center; gap:7px; padding:0 5px; cursor:pointer; user-select:none; list-style:none; }\nsummary::-webkit-details-marker { display:none; }\nsummary::before { content:''; width:5px; height:5px; border-right:1.5px solid #78818a; border-bottom:1.5px solid #78818a; transform:rotate(-45deg); flex:none; margin:0 3px; }\ndetails[open] > summary::before { transform:rotate(45deg); }\n.leaf { display:flex; align-items:center; gap:7px; padding:0 5px 0 23px; cursor:pointer; }\nsummary:hover,.leaf:hover { background:#f0f3f6; }\n.selected,.selected:hover { background:transparent; color:#315dbd; font-weight:700; border-radius:2px; }\n.caption { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n/* Reserve a left gutter; showing an action must not shift the node label. */\n.actions { position:absolute; left:calc(-1 * var(--tree-action-size,14px)); top:0; display:inline-flex; opacity:0; pointer-events:none; }\nsummary:hover > .actions,.leaf:hover > .actions,summary:focus-visible > .actions,.leaf:focus-visible > .actions,\n.actions:has(button:focus-visible),.actions:has(button.active) { opacity:1; pointer-events:auto; }\n.actions button { border:0; background:transparent; color:#80868b; font:inherit; font-size:11px; line-height:var(--tree-row-height,18px); width:var(--tree-action-size,14px); height:var(--tree-row-height,18px); padding:0; cursor:pointer; border-radius:3px; }\n.actions button:hover { background:#e1e5ed; }\n.actions button[data-action=breakpoint] { font-size:0; }\n.actions button[data-action=breakpoint]::before { content:''; display:inline-block; box-sizing:border-box; width:8px; height:8px; border:1.5px solid currentColor; border-radius:50%; vertical-align:middle; }\n.actions button.active { color:#b3261e; }\n.actions button.active.conditional { color:#f9ab00; }\n.actions button.active[data-action=breakpoint]::before { background:currentColor; }\n.actions button:focus-visible,summary:focus-visible,.leaf:focus-visible { outline:2px solid #356f9f; outline-offset:-2px; }\n\n@media (hover: none) { .actions { opacity:1; pointer-events:auto; } }\n\n.actions-end { position:static; margin-left:auto; flex:none; }\n[data-drop=inside] { outline:2px solid #356f9f; outline-offset:-2px; background:#e7f0fc; }\n[data-drop=before] { box-shadow:inset 0 2px #356f9f; }\n[data-drop=after] { box-shadow:inset 0 -2px #356f9f; }\n";
|
|
415
669
|
var sequence = 0;
|
|
416
670
|
var GramlotPocTree = class extends HTMLElement {
|
|
417
671
|
constructor() {
|
|
@@ -427,6 +681,9 @@
|
|
|
427
681
|
this._expanded = /* @__PURE__ */ new Set();
|
|
428
682
|
this._nodes = /* @__PURE__ */ new Map();
|
|
429
683
|
this._rowActions = [];
|
|
684
|
+
this.dragEnabled = false;
|
|
685
|
+
this.fixedPaths = [];
|
|
686
|
+
this._drag = null;
|
|
430
687
|
this._pendingRender = null;
|
|
431
688
|
this._subId = `gramlot-poc-tree-${++sequence}`;
|
|
432
689
|
}
|
|
@@ -454,6 +711,12 @@
|
|
|
454
711
|
this._selectedPath = path;
|
|
455
712
|
if (this.isConnected) this._markSelection();
|
|
456
713
|
}
|
|
714
|
+
revealPath(path) {
|
|
715
|
+
for (const details of this._root.querySelectorAll("details")) {
|
|
716
|
+
if (path.startsWith(details.dataset.path + ".")) details.open = true;
|
|
717
|
+
}
|
|
718
|
+
this.selectedPath = path;
|
|
719
|
+
}
|
|
457
720
|
get rowActions() {
|
|
458
721
|
return this._rowActions;
|
|
459
722
|
}
|
|
@@ -483,12 +746,22 @@
|
|
|
483
746
|
}
|
|
484
747
|
_render() {
|
|
485
748
|
this._pendingRender = null;
|
|
749
|
+
const focused = this.shadowRoot.activeElement;
|
|
750
|
+
const focusedPath = focused?.closest("[data-node-path]")?.dataset.nodePath;
|
|
751
|
+
const focusedAction = focused?.dataset.action;
|
|
486
752
|
for (const details of this._root.querySelectorAll("details")) {
|
|
487
753
|
if (details.open) this._expanded.add(details.dataset.path);
|
|
488
754
|
else this._expanded.delete(details.dataset.path);
|
|
489
755
|
}
|
|
490
756
|
this._nodes.clear();
|
|
491
757
|
this._root.replaceChildren(...this._store ? [this._buildLevel(this._store, "", /* @__PURE__ */ new Set())] : []);
|
|
758
|
+
if (focusedPath) {
|
|
759
|
+
let path = focusedPath;
|
|
760
|
+
while (path && !this._nodes.has(path)) path = path.includes(".") ? path.slice(0, path.lastIndexOf(".")) : "";
|
|
761
|
+
const row = [...this._root.querySelectorAll("[data-node-path]")].find((item) => item.dataset.nodePath === path);
|
|
762
|
+
const action = focusedAction && row && [...row.querySelectorAll("button")].find((button) => button.dataset.action === focusedAction);
|
|
763
|
+
(action || row)?.focus({ preventScroll: true });
|
|
764
|
+
}
|
|
492
765
|
if (this._selectedPath !== null && !this._nodes.has(this._selectedPath)) {
|
|
493
766
|
this._selectedPath = null;
|
|
494
767
|
this._notifySelection();
|
|
@@ -531,35 +804,91 @@
|
|
|
531
804
|
row.append(caption);
|
|
532
805
|
row.addEventListener("click", () => this._select(path));
|
|
533
806
|
this._actions(row, path, node);
|
|
807
|
+
this._dragEvents(row, path, node);
|
|
534
808
|
list.append(item);
|
|
535
809
|
}
|
|
536
810
|
return list;
|
|
537
811
|
}
|
|
538
812
|
_actions(row, path, node) {
|
|
539
813
|
if (!this._rowActions.length) return;
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
814
|
+
for (const position of ["start", "end"]) {
|
|
815
|
+
const actions = this.ownerDocument.createElement("span");
|
|
816
|
+
actions.className = `actions actions-${position}`;
|
|
817
|
+
for (const action of this._rowActions) {
|
|
818
|
+
if ((action.position ?? "start") !== position || action.hiddenPaths?.includes(path)) continue;
|
|
819
|
+
const button = this.ownerDocument.createElement("button");
|
|
820
|
+
button.type = "button";
|
|
821
|
+
button.textContent = action.icon ?? action.label;
|
|
822
|
+
button.title = action.label;
|
|
823
|
+
button.setAttribute("aria-label", action.label);
|
|
824
|
+
button.dataset.action = action.id;
|
|
825
|
+
button.classList.toggle("active", action.activePaths?.includes(path) ?? false);
|
|
826
|
+
button.classList.toggle("conditional", action.conditionalPaths?.includes(path) ?? false);
|
|
827
|
+
button.addEventListener("click", (event) => {
|
|
828
|
+
event.preventDefault();
|
|
829
|
+
event.stopPropagation();
|
|
830
|
+
this.dispatchEvent(new this.ownerDocument.defaultView.CustomEvent("tree-action", {
|
|
831
|
+
bubbles: true,
|
|
832
|
+
composed: true,
|
|
833
|
+
detail: { action: action.id, path, node }
|
|
834
|
+
}));
|
|
835
|
+
});
|
|
836
|
+
actions.append(button);
|
|
837
|
+
}
|
|
838
|
+
if (actions.childElementCount) row.append(actions);
|
|
561
839
|
}
|
|
562
|
-
|
|
840
|
+
}
|
|
841
|
+
_clearDrop() {
|
|
842
|
+
for (const row of this._root.querySelectorAll("[data-drop]")) delete row.dataset.drop;
|
|
843
|
+
}
|
|
844
|
+
_dragEvents(row, path, node) {
|
|
845
|
+
if (!this.dragEnabled) return;
|
|
846
|
+
row.draggable = !this.fixedPaths.includes(path);
|
|
847
|
+
row.addEventListener("dragstart", (event) => {
|
|
848
|
+
if (!row.draggable || event.target.closest("button")) {
|
|
849
|
+
event.preventDefault();
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
event.stopPropagation();
|
|
853
|
+
this._drag = { path, node };
|
|
854
|
+
event.dataTransfer.effectAllowed = "move";
|
|
855
|
+
event.dataTransfer.setData("application/x-gramlot-source", this._subId);
|
|
856
|
+
});
|
|
857
|
+
const placement = (event) => {
|
|
858
|
+
const rect = row.getBoundingClientRect();
|
|
859
|
+
const fraction = (event.clientY - rect.top) / rect.height;
|
|
860
|
+
return this.fixedPaths.includes(path) ? "inside" : fraction < 0.25 ? "before" : fraction > 0.75 ? "after" : "inside";
|
|
861
|
+
};
|
|
862
|
+
const valid = () => this._drag && this._drag.path !== path && !path.startsWith(this._drag.path + ".");
|
|
863
|
+
row.addEventListener("dragover", (event) => {
|
|
864
|
+
if (!valid()) return;
|
|
865
|
+
event.preventDefault();
|
|
866
|
+
event.stopPropagation();
|
|
867
|
+
event.dataTransfer.dropEffect = "move";
|
|
868
|
+
this._clearDrop();
|
|
869
|
+
row.dataset.drop = placement(event);
|
|
870
|
+
});
|
|
871
|
+
row.addEventListener("dragleave", () => {
|
|
872
|
+
delete row.dataset.drop;
|
|
873
|
+
});
|
|
874
|
+
row.addEventListener("drop", (event) => {
|
|
875
|
+
event.preventDefault();
|
|
876
|
+
event.stopPropagation();
|
|
877
|
+
this._clearDrop();
|
|
878
|
+
if (!valid() || event.dataTransfer.getData("application/x-gramlot-source") !== this._subId) return;
|
|
879
|
+
const source = this._drag;
|
|
880
|
+
this._drag = null;
|
|
881
|
+
if (this._store.getNode(source.path) !== source.node) return;
|
|
882
|
+
this.dispatchEvent(new this.ownerDocument.defaultView.CustomEvent("tree-drop", {
|
|
883
|
+
bubbles: true,
|
|
884
|
+
composed: true,
|
|
885
|
+
detail: { path: source.path, destination: path, placement: placement(event) }
|
|
886
|
+
}));
|
|
887
|
+
});
|
|
888
|
+
row.addEventListener("dragend", () => {
|
|
889
|
+
this._drag = null;
|
|
890
|
+
this._clearDrop();
|
|
891
|
+
});
|
|
563
892
|
}
|
|
564
893
|
_markSelection() {
|
|
565
894
|
for (const row of this._root.querySelectorAll("[data-node-path]")) {
|
|
@@ -736,6 +1065,9 @@
|
|
|
736
1065
|
}
|
|
737
1066
|
}
|
|
738
1067
|
#target = null;
|
|
1068
|
+
#history = null;
|
|
1069
|
+
#insertTarget = null;
|
|
1070
|
+
#catalog = [];
|
|
739
1071
|
#connection = null;
|
|
740
1072
|
#connectionRelease = null;
|
|
741
1073
|
#targets = [];
|
|
@@ -979,6 +1311,67 @@
|
|
|
979
1311
|
inspector.#applyEdit(choose);
|
|
980
1312
|
} else choose();
|
|
981
1313
|
}
|
|
1314
|
+
sourceAction(node, event) {
|
|
1315
|
+
if (event.detail.action === "breakpoint") {
|
|
1316
|
+
this.openBreakpoint(node, event);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const run = () => {
|
|
1320
|
+
if (event.detail.action === "insert") inspector.#openInsert(event.detail.path);
|
|
1321
|
+
if (event.detail.action === "remove") {
|
|
1322
|
+
try {
|
|
1323
|
+
inspector.#runCommand("remove", inspector.#sourceCommand(event.detail.path));
|
|
1324
|
+
} catch (error) {
|
|
1325
|
+
ui.data.setItem("status", error.message);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
if (ui.data.getItem("editor.dirty")) inspector.#applyEdit(run);
|
|
1330
|
+
else run();
|
|
1331
|
+
}
|
|
1332
|
+
sourceDrop(node, event) {
|
|
1333
|
+
const run = () => {
|
|
1334
|
+
try {
|
|
1335
|
+
const { path, destination, placement } = event.detail;
|
|
1336
|
+
const command = inspector.#sourceCommand(path);
|
|
1337
|
+
const target = inspector.#sourceCommand(destination);
|
|
1338
|
+
inspector.#runCommand("move", { ...command, destination, placement, targetId: target.nodeId, targetRevision: target.revision });
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
ui.data.setItem("status", error.message);
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
if (ui.data.getItem("editor.dirty")) inspector.#applyEdit(run);
|
|
1344
|
+
else run();
|
|
1345
|
+
}
|
|
1346
|
+
insertKind() {
|
|
1347
|
+
inspector.#insertChoices();
|
|
1348
|
+
}
|
|
1349
|
+
addElement() {
|
|
1350
|
+
inspector.#runCommand("insert", { ...inspector.#insertTarget, tag: ui.data.getItem("insert.tag"), attrs: Object.fromEntries((inspector.#catalog[0]?.fields ?? []).map(({ name }) => [name, ui.data.getItem(`insert.attrs.${name}`)])) });
|
|
1351
|
+
}
|
|
1352
|
+
closeInsert() {
|
|
1353
|
+
inspector.#dialog("insert").close();
|
|
1354
|
+
inspector.#insertTarget = null;
|
|
1355
|
+
}
|
|
1356
|
+
undo() {
|
|
1357
|
+
if (ui.data.getItem("editor.dirty")) inspector.#applyEdit(() => inspector.#runCommand("undo"));
|
|
1358
|
+
else inspector.#runCommand("undo");
|
|
1359
|
+
}
|
|
1360
|
+
redo() {
|
|
1361
|
+
if (ui.data.getItem("editor.dirty")) inspector.#applyEdit(() => inspector.#runCommand("redo"));
|
|
1362
|
+
else inspector.#runCommand("redo");
|
|
1363
|
+
}
|
|
1364
|
+
historyKey(node, event) {
|
|
1365
|
+
if (!(event.metaKey || event.ctrlKey) || event.altKey || event.target.closest("input,textarea,select,[contenteditable]")) return;
|
|
1366
|
+
if (event.key.toLowerCase() === "z") {
|
|
1367
|
+
event.preventDefault();
|
|
1368
|
+
event.shiftKey ? this.redo() : this.undo();
|
|
1369
|
+
}
|
|
1370
|
+
if (event.key.toLowerCase() === "y") {
|
|
1371
|
+
event.preventDefault();
|
|
1372
|
+
this.redo();
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
982
1375
|
openBreakpoint(node, event) {
|
|
983
1376
|
if (event.detail.action !== "breakpoint") return;
|
|
984
1377
|
ui.data.setItem("breakpointDirty", false);
|
|
@@ -1048,6 +1441,8 @@
|
|
|
1048
1441
|
breakpointCount: "\u25CF 0",
|
|
1049
1442
|
noBreakpoints: true
|
|
1050
1443
|
})) ui.data.setItem(key, value);
|
|
1444
|
+
ui.data.setItem("history", new Bag({ noUndo: true, noRedo: true, undo: "", redo: "" }));
|
|
1445
|
+
ui.data.setItem("insert", new Bag({ kind: "html", tag: "", html: true, component: false, status: "" }));
|
|
1051
1446
|
ui.data.setItem("editor", new Bag({ dirty: false, disabled: true, status: "", mode: "", condition: "" }));
|
|
1052
1447
|
ui.data.setItem("breakpoints", new Bag());
|
|
1053
1448
|
ui.data.setItem("breakpointDirty", false);
|
|
@@ -1055,7 +1450,7 @@
|
|
|
1055
1450
|
const builder = new GramlotBuilder();
|
|
1056
1451
|
const root = builder.root;
|
|
1057
1452
|
root.style(gramlot_tools_inspector_default);
|
|
1058
|
-
const palette = this.hasAttribute("embedded") ? root.section({ node_label: "palette", class: "embedded-panel", "aria-label": "Gramlot inspector" }) : root.webcomponent({ code: "gramlot-poc-palette", node_label: "palette", title: "Gramlot inspector", docked: this.hasAttribute("docked") ? "" : null, connect_onclose: "inspector.close" });
|
|
1453
|
+
const palette = this.hasAttribute("embedded") ? root.section({ node_label: "palette", class: "embedded-panel", "aria-label": "Gramlot inspector", connect_onkeydown: "inspector.historyKey" }) : root.webcomponent({ code: "gramlot-poc-palette", node_label: "palette", title: "Gramlot inspector", docked: this.hasAttribute("docked") ? "" : null, connect_onclose: "inspector.close", connect_onkeydown: "inspector.historyKey" });
|
|
1059
1454
|
const targetbar = palette.div({ node_label: "targetbar", class: "targetbar", hidden: "^singleTarget" });
|
|
1060
1455
|
targetbar.select({ node_label: "targets", value: "^targetId", disabled: "^busy", "aria-label": "Inspected page", connect_onchange: "inspector.selectTarget" });
|
|
1061
1456
|
const toolbar = palette.webcomponent({ code: "gramlot-poc-tabs", node_label: "tabs", role: "tablist", "aria-label": "Inspected root" });
|
|
@@ -1067,6 +1462,10 @@
|
|
|
1067
1462
|
"aria-selected": "false",
|
|
1068
1463
|
connect_onclick: "inspector.selectRoot"
|
|
1069
1464
|
});
|
|
1465
|
+
const historybar = palette.div({ class: "history-toolbar", hidden: "^stylesActive" });
|
|
1466
|
+
historybar.html_button("\u21B6 Undo", { type: "button", disabled: "^history.noUndo", title: "^history.undo", connect_onclick: "inspector.undo" });
|
|
1467
|
+
historybar.html_button("\u21B7 Redo", { type: "button", disabled: "^history.noRedo", title: "^history.redo", connect_onclick: "inspector.redo" });
|
|
1468
|
+
historybar.span("^status", { role: "status" });
|
|
1070
1469
|
palette.p("^error", { role: "alert", class: "error" });
|
|
1071
1470
|
const content = palette.webcomponent({ code: "gramlot-poc-splitter", node_label: "content", hidden: "^stylesActive" });
|
|
1072
1471
|
const trees = content.div({ node_label: "tree", slot: "top", class: "tree" });
|
|
@@ -1076,7 +1475,8 @@
|
|
|
1076
1475
|
"data-root": name,
|
|
1077
1476
|
hidden: name === "data" ? "^sourceActive" : "^dataActive",
|
|
1078
1477
|
connect_onchange: "inspector.selectNode",
|
|
1079
|
-
"connect_ontree-action": "inspector.
|
|
1478
|
+
"connect_ontree-action": "inspector.sourceAction",
|
|
1479
|
+
"connect_ontree-drop": "inspector.sourceDrop"
|
|
1080
1480
|
});
|
|
1081
1481
|
const properties = content.section({ node_label: "properties", slot: "bottom", class: "properties", "aria-label": "Selected node properties" });
|
|
1082
1482
|
const heading = properties.div({ class: "properties-heading" });
|
|
@@ -1128,6 +1528,19 @@
|
|
|
1128
1528
|
list.h2("Active breakpoints");
|
|
1129
1529
|
list.div({ node_label: "entries" });
|
|
1130
1530
|
list.div({ class: "dialog-actions" }).html_button("Close", { type: "button", connect_onclick: "inspector.closeBreakpoints" });
|
|
1531
|
+
const insert = palette.dialog({ node_label: "insert", "aria-label": "Add Source element", class: "breakpoint-dialog", connect_oncancel: "inspector.closeInsert" });
|
|
1532
|
+
insert.h2("Add a child element");
|
|
1533
|
+
const kind = insert.select({ value: "^insert.kind", "aria-label": "Element kind", connect_onchange: "inspector.insertKind" });
|
|
1534
|
+
kind.option("HTML tag", { value: "html" });
|
|
1535
|
+
kind.option("Widget or container", { value: "component" });
|
|
1536
|
+
insert.select({ node_label: "components", value: "^insert.tag", hidden: "^insert.html", "aria-label": "Widget or container" });
|
|
1537
|
+
insert.input({ value: "^insert.tag", live: true, hidden: "^insert.component", list: `${this.#subscriber}-tags`, "aria-label": "HTML tag", placeholder: "div, span\u2026" });
|
|
1538
|
+
insert.datalist({ node_label: "tags", id: `${this.#subscriber}-tags` });
|
|
1539
|
+
insert.div({ node_label: "fields" });
|
|
1540
|
+
insert.p("^insert.status", { role: "status" });
|
|
1541
|
+
const insertActions = insert.div({ class: "dialog-actions" });
|
|
1542
|
+
insertActions.html_button("Cancel", { type: "button", connect_onclick: "inspector.closeInsert" });
|
|
1543
|
+
insertActions.html_button("Add", { type: "button", disabled: "^busy", connect_onclick: "inspector.addElement" });
|
|
1131
1544
|
ui.src.startSource(builder.source);
|
|
1132
1545
|
for (const name of ["data", "source"]) this.#trees[name] = ui.dom.getDomNode(ui.src.source.getNode(`main.palette.content.tree.${name}`));
|
|
1133
1546
|
this.#renderTargets();
|
|
@@ -1153,6 +1566,7 @@
|
|
|
1153
1566
|
return;
|
|
1154
1567
|
}
|
|
1155
1568
|
if (!this.#target) return;
|
|
1569
|
+
this.#history = new InspectorHistory(this.#target);
|
|
1156
1570
|
this.#subscriptions = [this.#target.data, this.#target.src.source];
|
|
1157
1571
|
for (const [index, bag] of this.#subscriptions.entries()) bag.subscribe(this.#subscriber, { any: (event) => {
|
|
1158
1572
|
this.#checkBreakpoints(index === 0 ? "data" : "source", event);
|
|
@@ -1160,6 +1574,10 @@
|
|
|
1160
1574
|
} });
|
|
1161
1575
|
}
|
|
1162
1576
|
#unsubscribe() {
|
|
1577
|
+
this.#history?.clear();
|
|
1578
|
+
this.#history = null;
|
|
1579
|
+
this.#insertTarget = null;
|
|
1580
|
+
if (this.#ui) this.#dialog("insert").close();
|
|
1163
1581
|
++this.#styleRequest;
|
|
1164
1582
|
this.#styles?.dispose();
|
|
1165
1583
|
this.#styles = null;
|
|
@@ -1206,6 +1624,11 @@
|
|
|
1206
1624
|
ui.data.setItem("breakpoints", configurations);
|
|
1207
1625
|
ui.data.setItem("breakpointError", this.#connection.breakpoints.filter((record) => record.error).map((record) => `${record.root}: ${record.path}: ${record.error}`).join("; "));
|
|
1208
1626
|
}
|
|
1627
|
+
const history = this.#connection ? this.#connection.history : this.#history?.state;
|
|
1628
|
+
for (const action of ["undo", "redo"]) {
|
|
1629
|
+
ui.data.setItem(`history.${action}`, history?.[action] ?? "");
|
|
1630
|
+
ui.data.setItem(`history.no${action === "undo" ? "Undo" : "Redo"}`, !history?.[action] || ui.data.getItem("busy") || this.#state() !== "started");
|
|
1631
|
+
}
|
|
1209
1632
|
const count = ui.data.getItem("breakpoints").getNodes().length;
|
|
1210
1633
|
ui.data.setItem("breakpointCount", `\u25CF ${count}`);
|
|
1211
1634
|
ui.data.setItem("noBreakpoints", count === 0);
|
|
@@ -1215,13 +1638,18 @@
|
|
|
1215
1638
|
const tree = this.#trees[name];
|
|
1216
1639
|
tree.storeBag = this.#connection || this.#target ? this.#targetBag(name) : null;
|
|
1217
1640
|
const breakpoints = ui.data.getItem("breakpoints").getNodes().filter((node) => node.value.getItem("root") === name);
|
|
1641
|
+
tree.dragEnabled = name === "source";
|
|
1642
|
+
tree.fixedPaths = tree.storeBag?.getNodes().map((node) => node.label) ?? [];
|
|
1218
1643
|
tree.rowActions = [{
|
|
1219
1644
|
id: "breakpoint",
|
|
1220
1645
|
icon: "\u25CB",
|
|
1221
1646
|
label: "Breakpoint",
|
|
1222
1647
|
activePaths: breakpoints.map((node) => node.value.getItem("path")),
|
|
1223
1648
|
conditionalPaths: breakpoints.filter((node) => node.value.getItem("condition").trim()).map((node) => node.value.getItem("path"))
|
|
1224
|
-
}
|
|
1649
|
+
}, ...name === "source" ? [
|
|
1650
|
+
{ id: "insert", icon: "+", label: "Add child element", position: "end" },
|
|
1651
|
+
{ id: "remove", icon: "\u2212", label: "Remove branch", position: "end", hiddenPaths: tree.fixedPaths }
|
|
1652
|
+
] : []];
|
|
1225
1653
|
}
|
|
1226
1654
|
if (this.#target || this.#connection) this.#index(this.#bag(), []);
|
|
1227
1655
|
this.#trees[root].selectedPath = this.#selected()?.path.join(".") ?? null;
|
|
@@ -1591,8 +2019,7 @@ ${declarations}
|
|
|
1591
2019
|
});
|
|
1592
2020
|
return false;
|
|
1593
2021
|
}
|
|
1594
|
-
|
|
1595
|
-
if (!Object.is(value, this.#snapshot.value)) target.setValue(value);
|
|
2022
|
+
this.#history.edit(target, attrs, value, changed, !Object.is(value, this.#snapshot.value));
|
|
1596
2023
|
this.#acceptEdit(target);
|
|
1597
2024
|
this.#refresh();
|
|
1598
2025
|
this.#ui.data.setItem("editor.status", "");
|
|
@@ -1603,6 +2030,91 @@ ${declarations}
|
|
|
1603
2030
|
return false;
|
|
1604
2031
|
}
|
|
1605
2032
|
}
|
|
2033
|
+
#sourceCommand(path) {
|
|
2034
|
+
const node = this.#targetBag("source").getNode(path);
|
|
2035
|
+
if (!node) throw new Error("Source node no longer exists");
|
|
2036
|
+
const info = this.#connection?.nodeInfo(node);
|
|
2037
|
+
return { path, nodeId: info?.id, revision: info?.revision, ...this.#connection ? {} : { node } };
|
|
2038
|
+
}
|
|
2039
|
+
async #openInsert(path) {
|
|
2040
|
+
if (this.#ui.data.getItem("busy")) return;
|
|
2041
|
+
const generation = this.#generation;
|
|
2042
|
+
try {
|
|
2043
|
+
this.#insertTarget = this.#sourceCommand(path);
|
|
2044
|
+
this.#catalog = this.#connection ? (await this.#connection.request("catalog", { path })).catalog : this.#history.catalog(path);
|
|
2045
|
+
if (!this.#ui || generation !== this.#generation) return;
|
|
2046
|
+
this.#ui.data.setItem("insert.kind", "html");
|
|
2047
|
+
this.#ui.data.setItem("insert.status", "");
|
|
2048
|
+
this.#insertChoices();
|
|
2049
|
+
const builder = new GramlotBuilder();
|
|
2050
|
+
this.#ui.data.setItem("insert.attrs", new Bag());
|
|
2051
|
+
for (const { name, choices } of this.#catalog[0]?.fields ?? []) {
|
|
2052
|
+
builder.root.html_label(name);
|
|
2053
|
+
const field = builder.root.select({ value: `^insert.attrs.${name}`, "aria-label": name });
|
|
2054
|
+
field.option(`Choose ${name}\u2026`, { value: "", disabled: true });
|
|
2055
|
+
for (const value of choices) field.option(value, { value });
|
|
2056
|
+
this.#ui.data.setItem(`insert.attrs.${name}`, "");
|
|
2057
|
+
}
|
|
2058
|
+
this.#ui.src.source.getNode("main.palette.insert.fields").setValue(this.#ui.src.prepareSource(builder.source));
|
|
2059
|
+
this.#dialog("insert").showModal();
|
|
2060
|
+
} catch (error) {
|
|
2061
|
+
if (this.#ui && generation === this.#generation) this.#ui.data.setItem("status", error.message);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
#insertChoices() {
|
|
2065
|
+
const html = this.#ui.data.getItem("insert.kind") === "html";
|
|
2066
|
+
this.#ui.data.setItem("insert.html", html);
|
|
2067
|
+
this.#ui.data.setItem("insert.component", !html);
|
|
2068
|
+
const choices = this.#catalog.filter((item) => item.kind === (html ? "html" : "component"));
|
|
2069
|
+
const builder = new GramlotBuilder();
|
|
2070
|
+
for (const item of choices) builder.root.option(item.tag, { value: item.tag });
|
|
2071
|
+
this.#ui.src.source.getNode(`main.palette.insert.${html ? "tags" : "components"}`).setValue(this.#ui.src.prepareSource(builder.source));
|
|
2072
|
+
this.#ui.data.setItem("insert.tag", html ? "" : choices[0]?.tag ?? "");
|
|
2073
|
+
}
|
|
2074
|
+
async #runCommand(type, command = {}) {
|
|
2075
|
+
const ui = this.#ui;
|
|
2076
|
+
if (ui.data.getItem("busy")) return;
|
|
2077
|
+
const generation = this.#generation;
|
|
2078
|
+
ui.data.setItem("busy", true);
|
|
2079
|
+
ui.data.setItem("status", "");
|
|
2080
|
+
ui.data.setItem("insert.status", "");
|
|
2081
|
+
try {
|
|
2082
|
+
let selection;
|
|
2083
|
+
if (this.#connection) ({ selection } = await this.#connection.request(type, command));
|
|
2084
|
+
else {
|
|
2085
|
+
if (command.path && this.#targetBag("source").getNode(command.path) !== command.node) throw new Error("Source node changed. Select it again.");
|
|
2086
|
+
if (type === "insert") selection = this.#history.insert(command.path, command.tag, command.attrs);
|
|
2087
|
+
if (type === "remove") selection = this.#history.remove(command.path);
|
|
2088
|
+
if (type === "move") selection = this.#history.move(command.path, command.destination, command.placement);
|
|
2089
|
+
if (type === "undo") this.#history.undo();
|
|
2090
|
+
if (type === "redo") this.#history.redo();
|
|
2091
|
+
}
|
|
2092
|
+
if (!this.#ui || generation !== this.#generation) return;
|
|
2093
|
+
if (type === "insert") this.#dialog("insert").close();
|
|
2094
|
+
this.#snapshot = null;
|
|
2095
|
+
ui.data.setItem("editor.dirty", false);
|
|
2096
|
+
this.#refresh();
|
|
2097
|
+
if (selection) {
|
|
2098
|
+
ui.data.setItem("root", "source");
|
|
2099
|
+
ui.data.setItem("tab", "source");
|
|
2100
|
+
this.#refresh();
|
|
2101
|
+
const entry = this.#nodes.find((item) => item.path.join(".") === selection);
|
|
2102
|
+
ui.data.setItem("selection", entry?.id ?? null);
|
|
2103
|
+
this.#trees.source.revealPath(selection);
|
|
2104
|
+
this.#refresh();
|
|
2105
|
+
}
|
|
2106
|
+
} catch (error) {
|
|
2107
|
+
if (this.#ui && generation === this.#generation) {
|
|
2108
|
+
ui.data.setItem("status", error.message);
|
|
2109
|
+
ui.data.setItem("insert.status", error.message);
|
|
2110
|
+
}
|
|
2111
|
+
} finally {
|
|
2112
|
+
if (this.#ui && generation === this.#generation) {
|
|
2113
|
+
ui.data.setItem("busy", false);
|
|
2114
|
+
this.#refresh();
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
1606
2118
|
#findBreakpoint(root, path) {
|
|
1607
2119
|
return this.#ui.data.getItem("breakpoints").getNodes().find((node) => {
|
|
1608
2120
|
const value = node.getValue(true);
|