@d3-polytree/editor 0.1.0 → 0.3.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/README.md +36 -0
- package/dist/editor.umd.js +6 -6
- package/dist/editor.umd.js.map +1 -1
- package/dist/index.cjs +550 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +183 -3
- package/dist/index.d.ts +183 -3
- package/dist/index.js +540 -10
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -0
- package/package.json +8 -7
package/dist/index.cjs
CHANGED
|
@@ -21,19 +21,530 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Editor: () => Editor,
|
|
24
|
-
|
|
24
|
+
EntryFactory: () => EntryFactory,
|
|
25
|
+
PfdnPropertiesProvider: () => PfdnPropertiesProvider,
|
|
26
|
+
PropertiesPanel: () => PropertiesPanel,
|
|
27
|
+
default: () => index_default,
|
|
28
|
+
entryFactoryModule: () => entryFactoryModule,
|
|
29
|
+
pfdnPropertiesProviderModule: () => pfdnPropertiesProviderModule,
|
|
30
|
+
propertiesPanelModule: () => propertiesPanelModule
|
|
25
31
|
});
|
|
26
32
|
module.exports = __toCommonJS(index_exports);
|
|
27
33
|
var import_interactive_viewer = require("@d3-polytree/interactive-viewer");
|
|
28
34
|
var import_viewer = require("@d3-polytree/viewer");
|
|
29
35
|
var import_core = require("@d3-polytree/core");
|
|
30
|
-
|
|
36
|
+
|
|
37
|
+
// src/properties-panel/utils.ts
|
|
38
|
+
function is(definition, elementType) {
|
|
39
|
+
return definition.$instanceOf(elementType);
|
|
40
|
+
}
|
|
41
|
+
function deepGet(obj, path) {
|
|
42
|
+
return path.split(".").reduce(
|
|
43
|
+
(o, k) => o == null ? void 0 : o[k],
|
|
44
|
+
obj
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
function deepSet(obj, path, value) {
|
|
48
|
+
const keys = path.split(".");
|
|
49
|
+
let cursor = obj;
|
|
50
|
+
for (let i = 0; i < keys.length - 1; i += 1) {
|
|
51
|
+
const key = keys[i];
|
|
52
|
+
if (cursor[key] == null || typeof cursor[key] !== "object") {
|
|
53
|
+
cursor[key] = {};
|
|
54
|
+
}
|
|
55
|
+
cursor = cursor[key];
|
|
56
|
+
}
|
|
57
|
+
cursor[keys[keys.length - 1]] = value;
|
|
58
|
+
}
|
|
59
|
+
function debounce(fn, wait) {
|
|
60
|
+
let timer;
|
|
61
|
+
return (...args) => {
|
|
62
|
+
if (timer) {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
}
|
|
65
|
+
timer = setTimeout(() => fn(...args), wait);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function startCase(input) {
|
|
69
|
+
return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([a-zA-Z])([0-9])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (c) => c.toUpperCase());
|
|
70
|
+
}
|
|
71
|
+
function isHexColor(value) {
|
|
72
|
+
return typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/properties-panel/EntryFactory.ts
|
|
76
|
+
function ensureNotNull(prop) {
|
|
77
|
+
if (!prop) {
|
|
78
|
+
throw new Error(`${prop} must be set.`);
|
|
79
|
+
}
|
|
80
|
+
return prop;
|
|
81
|
+
}
|
|
82
|
+
var EntryFactory = class {
|
|
83
|
+
constructor(eventBus) {
|
|
84
|
+
this._eventBus = eventBus;
|
|
85
|
+
}
|
|
86
|
+
setDefaultParameters(options) {
|
|
87
|
+
const eventBus = this._eventBus;
|
|
88
|
+
const defaultGet = (element, formNode) => {
|
|
89
|
+
const prop = ensureNotNull(options.modelProperty);
|
|
90
|
+
const value = deepGet(element, prop);
|
|
91
|
+
const input = formNode.querySelector("input");
|
|
92
|
+
if (input) {
|
|
93
|
+
input.value = value == null ? "" : String(value);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const defaultSet = (element, values) => {
|
|
97
|
+
const prop = ensureNotNull(options.modelProperty);
|
|
98
|
+
deepSet(element, prop, deepGet(values, prop));
|
|
99
|
+
return true;
|
|
100
|
+
};
|
|
101
|
+
const triggerUpdate = (propertyId, element) => {
|
|
102
|
+
eventBus.emit("PropertiesPanel.propertyChanged", propertyId, element);
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
html: "",
|
|
106
|
+
description: "",
|
|
107
|
+
get: defaultGet,
|
|
108
|
+
set: defaultSet,
|
|
109
|
+
validate: () => ({}),
|
|
110
|
+
...options,
|
|
111
|
+
triggerUpdate
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
textField(options) {
|
|
115
|
+
return textInputField(this.setDefaultParameters(options));
|
|
116
|
+
}
|
|
117
|
+
selectBox(options) {
|
|
118
|
+
return selectBoxField(this.setDefaultParameters(options));
|
|
119
|
+
}
|
|
120
|
+
colorPicker(options) {
|
|
121
|
+
return colorPickerField(this.setDefaultParameters(options));
|
|
122
|
+
}
|
|
123
|
+
spreadsheet(options) {
|
|
124
|
+
return this.setDefaultParameters(options);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
EntryFactory.$inject = ["eventBus"];
|
|
128
|
+
function fieldWrapper(resource, inner) {
|
|
129
|
+
const label = resource.label ?? resource.id;
|
|
130
|
+
return `<div class="pfdjs-pp-field-wrapper"><label for="pfdjs-${resource.id}">${label}</label>${inner}</div>`;
|
|
131
|
+
}
|
|
132
|
+
function textInputField(resource) {
|
|
133
|
+
const type = resource.type ?? "text";
|
|
134
|
+
resource.html = fieldWrapper(
|
|
135
|
+
resource,
|
|
136
|
+
`<input id="pfdjs-${resource.id}" type="${type}" name="${resource.modelProperty}" />`
|
|
137
|
+
);
|
|
138
|
+
return resource;
|
|
139
|
+
}
|
|
140
|
+
function selectBoxField(resource) {
|
|
141
|
+
const allowEmpty = resource.allowEmpty ?? true;
|
|
142
|
+
const options = Array.isArray(resource.selectOptions) ? resource.selectOptions.concat(allowEmpty ? [{ name: "", value: "" }] : []) : [{ name: "", value: "" }];
|
|
143
|
+
const optionsHtml = options.map((o) => `<option value="${o.value}">${o.name}</option>`).join("");
|
|
144
|
+
resource.html = fieldWrapper(
|
|
145
|
+
resource,
|
|
146
|
+
`<select id="pfdjs-${resource.id}" name="${resource.modelProperty}">${optionsHtml}</select>`
|
|
147
|
+
);
|
|
148
|
+
resource.get = (element, formNode) => {
|
|
149
|
+
const value = element.get(resource.modelProperty) ?? "default";
|
|
150
|
+
formNode.querySelectorAll(`select#pfdjs-${resource.id} > option`).forEach((option) => {
|
|
151
|
+
option.selected = option.value === value;
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
return resource;
|
|
155
|
+
}
|
|
156
|
+
function colorPickerField(resource) {
|
|
157
|
+
resource.html = fieldWrapper(
|
|
158
|
+
resource,
|
|
159
|
+
`<input id="pfdjs-${resource.id}" type="color" name="${resource.modelProperty}" />`
|
|
160
|
+
);
|
|
161
|
+
resource.get = (element, formNode) => {
|
|
162
|
+
const input = formNode.querySelector("input[type=color]");
|
|
163
|
+
const value = deepGet(element, resource.modelProperty);
|
|
164
|
+
if (input && isHexColor(value)) {
|
|
165
|
+
input.value = value;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
return resource;
|
|
169
|
+
}
|
|
170
|
+
var entryFactoryModule = {
|
|
171
|
+
__init__: ["entryFactory"],
|
|
172
|
+
entryFactory: ["type", EntryFactory]
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// src/properties-panel/PfdnPropertiesProvider.ts
|
|
176
|
+
function nameProps(group, element, factory) {
|
|
177
|
+
if (is(element, "pfdn:Node")) {
|
|
178
|
+
group.entries.push(factory.textField({ id: "name", label: "Name", modelProperty: "name" }));
|
|
179
|
+
group.entries.push(factory.textField({ id: "tag", label: "Tag", modelProperty: "tag" }));
|
|
180
|
+
group.entries.push(
|
|
181
|
+
factory.textField({ id: "label.text", label: "Diagram label", modelProperty: "label.text" })
|
|
182
|
+
);
|
|
183
|
+
} else if (is(element, "pfdn:Settings")) {
|
|
184
|
+
group.entries.push(factory.textField({ id: "name", label: "Diagram name", modelProperty: "name" }));
|
|
185
|
+
group.entries.push(factory.textField({ id: "author", label: "Author's name", modelProperty: "author" }));
|
|
186
|
+
} else if (is(element, "pfdn:Link")) {
|
|
187
|
+
group.entries.push(
|
|
188
|
+
factory.textField({ id: "label.text", label: "Diagram label", modelProperty: "label.text" })
|
|
189
|
+
);
|
|
190
|
+
} else if (is(element, "pfdn:Label")) {
|
|
191
|
+
group.entries.push(factory.textField({ id: "text", label: "Label", modelProperty: "text" }));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function nodeFormatProps(group, element, factory, icons) {
|
|
195
|
+
if (!is(element, "pfdn:Node")) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const selectOptions = Object.keys(icons).map((key) => ({ value: key, name: startCase(key) }));
|
|
199
|
+
group.entries.push(
|
|
200
|
+
factory.selectBox({
|
|
201
|
+
id: "type",
|
|
202
|
+
label: "Icon",
|
|
203
|
+
modelProperty: "type",
|
|
204
|
+
allowEmpty: false,
|
|
205
|
+
selectOptions
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
group.entries.push(
|
|
209
|
+
factory.textField({ id: "size", label: "Size", modelProperty: "size", type: "number" })
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
function linkFormatProps(group, element, factory) {
|
|
213
|
+
if (!is(element, "pfdn:Link")) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
group.entries.push(
|
|
217
|
+
factory.textField({ id: "lineWidth", label: "Line width", modelProperty: "lineWidth", type: "number" })
|
|
218
|
+
);
|
|
219
|
+
group.entries.push(
|
|
220
|
+
factory.colorPicker({ id: "lineColor", label: "Line color", modelProperty: "lineColor" })
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
function labelFormatProps(group, element, factory) {
|
|
224
|
+
if (is(element, "pfdn:Node") || is(element, "pfdn:Link")) {
|
|
225
|
+
group.entries.push(
|
|
226
|
+
factory.textField({
|
|
227
|
+
id: "label.fontSize",
|
|
228
|
+
label: "Font size",
|
|
229
|
+
modelProperty: "label.fontSize",
|
|
230
|
+
type: "number"
|
|
231
|
+
})
|
|
232
|
+
);
|
|
233
|
+
group.entries.push(
|
|
234
|
+
factory.colorPicker({ id: "label.color", label: "Color", modelProperty: "label.color" })
|
|
235
|
+
);
|
|
236
|
+
} else if (is(element, "pfdn:Label")) {
|
|
237
|
+
group.entries.push(
|
|
238
|
+
factory.textField({ id: "fontSize", label: "Font size", modelProperty: "fontSize", type: "number" })
|
|
239
|
+
);
|
|
240
|
+
group.entries.push(factory.colorPicker({ id: "color", label: "Color", modelProperty: "color" }));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function gridFormatProps(group, element, factory) {
|
|
244
|
+
if (!is(element, "pfdn:Settings")) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
group.entries.push(
|
|
248
|
+
factory.colorPicker({ id: "backgroundColor", label: "Background color", modelProperty: "backgroundColor" })
|
|
249
|
+
);
|
|
250
|
+
group.entries.push(
|
|
251
|
+
factory.colorPicker({ id: "grid.lineColor", label: "Line color", modelProperty: "grid.lineColor" })
|
|
252
|
+
);
|
|
253
|
+
group.entries.push(
|
|
254
|
+
factory.textField({ id: "grid.size", label: "Square size", modelProperty: "grid.size", type: "number" })
|
|
255
|
+
);
|
|
256
|
+
group.entries.push(
|
|
257
|
+
factory.textField({ id: "grid.lineWidth", label: "Line width", modelProperty: "grid.lineWidth", type: "number" })
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
var PfdnPropertiesProvider = class {
|
|
261
|
+
constructor(icons, entryFactory, eventBus) {
|
|
262
|
+
this._icons = icons;
|
|
263
|
+
this._entryFactory = entryFactory;
|
|
264
|
+
this._eventBus = eventBus;
|
|
265
|
+
}
|
|
266
|
+
updateDrawing(definition) {
|
|
267
|
+
if (is(definition, "pfdn:Settings")) {
|
|
268
|
+
this._eventBus.emit("canvas.resized");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
this._eventBus.emit("element.updated", definition.id, definition);
|
|
272
|
+
if (is(definition, "pfdn:Link") || is(definition, "pfdn:Node")) {
|
|
273
|
+
const label = definition.label;
|
|
274
|
+
if (label) {
|
|
275
|
+
this._eventBus.emit("element.updated", label.id, label);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
getTabs(element) {
|
|
280
|
+
const factory = this._entryFactory;
|
|
281
|
+
return [this._propertiesTab(element, factory), this._formatTab(element, factory)];
|
|
282
|
+
}
|
|
283
|
+
_propertiesTab(element, factory) {
|
|
284
|
+
const general = { id: "general", label: "General", entries: [] };
|
|
285
|
+
nameProps(general, element, factory);
|
|
286
|
+
return { id: "properties", label: "Properties", groups: [general] };
|
|
287
|
+
}
|
|
288
|
+
_formatTab(element, factory) {
|
|
289
|
+
const elementFormat = { id: "elementFormat", label: "Element format", entries: [] };
|
|
290
|
+
const labelFormat = { id: "labelFormat", label: "Label format", entries: [] };
|
|
291
|
+
const gridFormat = { id: "gridFormat", label: "Grid format", entries: [] };
|
|
292
|
+
nodeFormatProps(elementFormat, element, factory, this._icons);
|
|
293
|
+
linkFormatProps(elementFormat, element, factory);
|
|
294
|
+
labelFormatProps(labelFormat, element, factory);
|
|
295
|
+
gridFormatProps(gridFormat, element, factory);
|
|
296
|
+
return { id: "format", label: "Format", groups: [elementFormat, labelFormat, gridFormat] };
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
PfdnPropertiesProvider.$inject = ["icons", "entryFactory", "eventBus"];
|
|
300
|
+
var pfdnPropertiesProviderModule = {
|
|
301
|
+
__init__: ["propertiesProvider"],
|
|
302
|
+
propertiesProvider: ["type", PfdnPropertiesProvider]
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// src/properties-panel/PropertiesPanel.ts
|
|
306
|
+
function fromHtml(html) {
|
|
307
|
+
const template = document.createElement("template");
|
|
308
|
+
template.innerHTML = html.trim();
|
|
309
|
+
return template.content.firstElementChild;
|
|
310
|
+
}
|
|
311
|
+
var FORM_CONTROLS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
|
|
312
|
+
var _PropertiesPanel = class _PropertiesPanel {
|
|
313
|
+
constructor(sideTabsProvider, eventBus, propertiesProvider, diagramSettings, commandStack) {
|
|
314
|
+
this._entries = {};
|
|
315
|
+
this._container = null;
|
|
316
|
+
this._tabsEl = null;
|
|
317
|
+
this._contentsEl = null;
|
|
318
|
+
this._eventBus = eventBus;
|
|
319
|
+
this._propertiesProvider = propertiesProvider;
|
|
320
|
+
this._diagramSettings = diagramSettings;
|
|
321
|
+
this._commandStack = commandStack;
|
|
322
|
+
this._registerUpdatePropertiesCommand();
|
|
323
|
+
this._registerSideTab(sideTabsProvider);
|
|
324
|
+
this._registerSelectionListener();
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Register the `element.updateProperties` command. Its apply logic is
|
|
328
|
+
* editor-specific (a property `scope` plus the provider's drawing update), so
|
|
329
|
+
* the panel — which owns both — registers it on the shared stack rather than
|
|
330
|
+
* the core modelling orchestrator.
|
|
331
|
+
*/
|
|
332
|
+
_registerUpdatePropertiesCommand() {
|
|
333
|
+
const apply = (ctx, props) => {
|
|
334
|
+
ctx.scope.set(ctx.definition, props);
|
|
335
|
+
this._propertiesProvider.updateDrawing(ctx.definition);
|
|
336
|
+
};
|
|
337
|
+
this._commandStack.registerHandler("element.updateProperties", {
|
|
338
|
+
execute: (ctx) => apply(ctx, ctx.after),
|
|
339
|
+
revert: (ctx) => apply(ctx, ctx.before)
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
_registerSideTab(provider) {
|
|
343
|
+
provider.registerSideTab(
|
|
344
|
+
{
|
|
345
|
+
title: "Properties",
|
|
346
|
+
iconClassName: "icon-sliders",
|
|
347
|
+
action: { created: (content) => this._drawPanel(content) }
|
|
348
|
+
},
|
|
349
|
+
1
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
_registerSelectionListener() {
|
|
353
|
+
this._eventBus.on(
|
|
354
|
+
"selection.changed",
|
|
355
|
+
(oldSelection, newSelection) => {
|
|
356
|
+
let selected = this._diagramSettings;
|
|
357
|
+
if (newSelection.length === 1 && (oldSelection.length !== 1 || oldSelection[0].definition.id !== newSelection[0].definition.id)) {
|
|
358
|
+
selected = newSelection[0].definition;
|
|
359
|
+
}
|
|
360
|
+
this._update(selected);
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
_drawPanel(content) {
|
|
365
|
+
if (!content) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
this._drawContainer(content);
|
|
369
|
+
this._update(this._diagramSettings);
|
|
370
|
+
}
|
|
371
|
+
_drawContainer(content) {
|
|
372
|
+
this._container = fromHtml(_PropertiesPanel.HTML_MARKUP);
|
|
373
|
+
content.insertBefore(this._container, content.firstChild);
|
|
374
|
+
this._tabsEl = this._container.querySelector(".tab-sheets");
|
|
375
|
+
this._contentsEl = this._container.querySelector(".pfdjs-pp-contents");
|
|
376
|
+
this._container.addEventListener("click", (event) => {
|
|
377
|
+
const tab = event.target.closest(".tab-sheet");
|
|
378
|
+
if (tab) {
|
|
379
|
+
this._selectTab(tab.getAttribute("data-tab-target"));
|
|
380
|
+
event.stopImmediatePropagation();
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
this._registerInputChangeHandlers();
|
|
384
|
+
}
|
|
385
|
+
_registerInputChangeHandlers() {
|
|
386
|
+
const container = this._container;
|
|
387
|
+
if (!container) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const debouncedApply = debounce((target) => this._applyChange(target), 300);
|
|
391
|
+
container.addEventListener("input", (event) => {
|
|
392
|
+
const target = event.target;
|
|
393
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
394
|
+
debouncedApply(target);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
container.addEventListener("change", (event) => {
|
|
398
|
+
const target = event.target;
|
|
399
|
+
if (FORM_CONTROLS.has(target.tagName)) {
|
|
400
|
+
this._applyChange(target);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
this._eventBus.on(
|
|
404
|
+
"PropertiesPanel.propertyChanged",
|
|
405
|
+
(propertyId, definition) => this._applyChangeByProperty(propertyId, definition)
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
_applyChange(target) {
|
|
409
|
+
const entryId = target.getAttribute("name");
|
|
410
|
+
if (!entryId) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
this._commit(entryId, target.value);
|
|
414
|
+
}
|
|
415
|
+
_applyChangeByProperty(propertyId, definition) {
|
|
416
|
+
this._commit(propertyId, deepGet(definition, propertyId));
|
|
417
|
+
}
|
|
418
|
+
_commit(entryId, newValue) {
|
|
419
|
+
const entry = this._entries[entryId];
|
|
420
|
+
if (!entry) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const after = {};
|
|
424
|
+
deepSet(after, entryId, newValue);
|
|
425
|
+
const before = {};
|
|
426
|
+
deepSet(before, entryId, deepGet(entry.definition, entryId));
|
|
427
|
+
this._commandStack.execute("element.updateProperties", {
|
|
428
|
+
scope: entry.scope,
|
|
429
|
+
definition: entry.definition,
|
|
430
|
+
before,
|
|
431
|
+
after
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
_selectTab(tabId) {
|
|
435
|
+
this._tabsEl?.querySelectorAll(".tab-sheet").forEach((tab) => {
|
|
436
|
+
tab.classList.toggle("tab-sheet-active", tab.getAttribute("data-tab-target") === tabId);
|
|
437
|
+
});
|
|
438
|
+
this._contentsEl?.querySelectorAll(".pfdjs-pp-content").forEach((content) => {
|
|
439
|
+
content.classList.toggle("open", content.getAttribute("data-tab-target") === tabId);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
_update(definition) {
|
|
443
|
+
if (this._tabsEl && this._contentsEl) {
|
|
444
|
+
this._tabsEl.replaceChildren();
|
|
445
|
+
this._contentsEl.replaceChildren();
|
|
446
|
+
}
|
|
447
|
+
if (definition) {
|
|
448
|
+
this._drawEntries(definition);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
_drawEntries(definition) {
|
|
452
|
+
if (!this._tabsEl || !this._contentsEl) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const tabs = this._propertiesProvider.getTabs(definition);
|
|
456
|
+
const renderedTabs = [];
|
|
457
|
+
this._entries = {};
|
|
458
|
+
for (const tab of tabs) {
|
|
459
|
+
const content = fromHtml(`<div class="pfdjs-pp-content" data-tab-target="${tab.id}"></div>`);
|
|
460
|
+
let tabHasContent = false;
|
|
461
|
+
for (const group of tab.groups) {
|
|
462
|
+
if (group.entries.length === 0) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const groupContent = fromHtml(
|
|
466
|
+
`<div class="pfdjs-pp-content-group" data-group-target="${group.id}"><div class="tab-content-group-title">${group.label}</div></div>`
|
|
467
|
+
);
|
|
468
|
+
for (const entry of group.entries) {
|
|
469
|
+
const entryNode = fromHtml(`<div>${entry.html}</div>`);
|
|
470
|
+
groupContent.appendChild(entryNode);
|
|
471
|
+
this._entries[entry.id] = { scope: entry, definition, formNode: entryNode };
|
|
472
|
+
}
|
|
473
|
+
content.appendChild(groupContent);
|
|
474
|
+
tabHasContent = true;
|
|
475
|
+
}
|
|
476
|
+
if (tabHasContent) {
|
|
477
|
+
this._contentsEl.appendChild(content);
|
|
478
|
+
renderedTabs.push({ id: tab.id, label: tab.label });
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
Object.values(this._entries).forEach((entry) => entry.scope.get(entry.definition, entry.formNode));
|
|
482
|
+
let firstTab = null;
|
|
483
|
+
for (const tab of renderedTabs) {
|
|
484
|
+
firstTab ?? (firstTab = tab.id);
|
|
485
|
+
this._tabsEl.appendChild(
|
|
486
|
+
fromHtml(
|
|
487
|
+
`<li class="tab-sheet" data-tab-target="${tab.id}"><a href="#">${tab.label}</a></li>`
|
|
488
|
+
)
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
this._selectTab(firstTab);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
_PropertiesPanel.$inject = [
|
|
495
|
+
"sideTabsProvider",
|
|
496
|
+
"eventBus",
|
|
497
|
+
"propertiesProvider",
|
|
498
|
+
"d3polytree.definitions.settings",
|
|
499
|
+
"commandStack"
|
|
500
|
+
];
|
|
501
|
+
_PropertiesPanel.HTML_MARKUP = '<div id="pfdjs-pp-container"><div class="pfdjs-pp-tabs"><ul class="tab-sheets"></ul></div><div class="pfdjs-pp-contents"></div></div>';
|
|
502
|
+
var PropertiesPanel = _PropertiesPanel;
|
|
503
|
+
|
|
504
|
+
// src/properties-panel/index.ts
|
|
505
|
+
var propertiesPanelModule = {
|
|
506
|
+
__init__: ["propertiesPanel"],
|
|
507
|
+
propertiesPanel: ["type", PropertiesPanel]
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/index.ts
|
|
31
511
|
var INITIAL_DIAGRAM = '<?xml version="1.0" encoding="UTF-8"?><pfdn:diagram xmlns:pfdn="http://pfdn" xmlns="http://pfdn"><settings author="No Author" name="No Name Diagram" status="1"><zoom><offset x="0" y="0" /><scale>1</scale></zoom><grid /></settings><node id="node_1" label="label_1" status="1"><position x="20" y="100" /></node><label id="label_1" fontSize="12" isReadOnly="true" status="1"><position x="33" y="140" /><text>Node 1</text></label></pfdn:diagram>';
|
|
32
512
|
var _Editor = class _Editor extends import_interactive_viewer.InteractiveViewer {
|
|
33
|
-
constructor() {
|
|
34
|
-
super(
|
|
513
|
+
constructor(options = {}) {
|
|
514
|
+
super(options);
|
|
35
515
|
/** The document a fresh editor opens with (used by {@link createDiagram}). */
|
|
36
516
|
this.initialDiagram = INITIAL_DIAGRAM;
|
|
517
|
+
this._onKeydown = (event) => this._handleKeydown(event);
|
|
518
|
+
options.container?.addEventListener("keydown", this._onKeydown);
|
|
519
|
+
}
|
|
520
|
+
_handleKeydown(event) {
|
|
521
|
+
const target = event.target;
|
|
522
|
+
if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA")) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (!this.getHost()) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const mod = event.ctrlKey || event.metaKey;
|
|
529
|
+
if (!mod) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const key = event.key.toLowerCase();
|
|
533
|
+
if (key === "z" && event.shiftKey) {
|
|
534
|
+
event.preventDefault();
|
|
535
|
+
this.redo();
|
|
536
|
+
} else if (key === "z") {
|
|
537
|
+
event.preventDefault();
|
|
538
|
+
this.undo();
|
|
539
|
+
} else if (key === "y") {
|
|
540
|
+
event.preventDefault();
|
|
541
|
+
this.redo();
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/** Tear down the editor, removing the keyboard binding. */
|
|
545
|
+
destroy() {
|
|
546
|
+
this.options.container?.removeEventListener("keydown", this._onKeydown);
|
|
547
|
+
super.destroy();
|
|
37
548
|
}
|
|
38
549
|
/** (Re)open the initial diagram. */
|
|
39
550
|
createDiagram() {
|
|
@@ -46,9 +557,16 @@ var _Editor = class _Editor extends import_interactive_viewer.InteractiveViewer
|
|
|
46
557
|
...import_viewer.Viewer.modules
|
|
47
558
|
];
|
|
48
559
|
}
|
|
49
|
-
/**
|
|
560
|
+
/**
|
|
561
|
+
* Create a node (and its associated label) at an optional position.
|
|
562
|
+
*
|
|
563
|
+
* Routed through the command stack so it persists and is undoable (since B10
|
|
564
|
+
* the draw-layer `.created` event no longer persists on its own).
|
|
565
|
+
*/
|
|
50
566
|
createNode(parameters = {}) {
|
|
51
|
-
|
|
567
|
+
const ctx = { className: "node", parameters: [parameters] };
|
|
568
|
+
this.get("commandStack").execute("element.create", ctx);
|
|
569
|
+
return ctx.created;
|
|
52
570
|
}
|
|
53
571
|
/** Select an element by definition (e.g. to prepare a delete). */
|
|
54
572
|
select(definition) {
|
|
@@ -61,6 +579,22 @@ var _Editor = class _Editor extends import_interactive_viewer.InteractiveViewer
|
|
|
61
579
|
deleteSelected() {
|
|
62
580
|
this.get("selection").deleteSelected();
|
|
63
581
|
}
|
|
582
|
+
/** Undo the last edit (a whole gesture is one step). No-op if nothing to undo. */
|
|
583
|
+
undo() {
|
|
584
|
+
this.get("commandStack").undo();
|
|
585
|
+
}
|
|
586
|
+
/** Redo the last undone edit. No-op if nothing to redo. */
|
|
587
|
+
redo() {
|
|
588
|
+
this.get("commandStack").redo();
|
|
589
|
+
}
|
|
590
|
+
/** Whether there is an edit to undo. */
|
|
591
|
+
canUndo() {
|
|
592
|
+
return this.get("commandStack").canUndo();
|
|
593
|
+
}
|
|
594
|
+
/** Whether there is an undone edit to redo. */
|
|
595
|
+
canRedo() {
|
|
596
|
+
return this.get("commandStack").canRedo();
|
|
597
|
+
}
|
|
64
598
|
};
|
|
65
599
|
/** Editing modules on top of the interaction layer. */
|
|
66
600
|
_Editor.editionModules = [
|
|
@@ -73,14 +607,20 @@ _Editor.editionModules = [
|
|
|
73
607
|
import_core.resizeElementModule,
|
|
74
608
|
// the properties panel (registers a side tab; side-tabs + search-panel are
|
|
75
609
|
// inherited from InteractiveViewer)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
610
|
+
entryFactoryModule,
|
|
611
|
+
pfdnPropertiesProviderModule,
|
|
612
|
+
propertiesPanelModule
|
|
79
613
|
];
|
|
80
614
|
var Editor = _Editor;
|
|
81
615
|
var index_default = Editor;
|
|
82
616
|
// Annotate the CommonJS export names for ESM import in node:
|
|
83
617
|
0 && (module.exports = {
|
|
84
|
-
Editor
|
|
618
|
+
Editor,
|
|
619
|
+
EntryFactory,
|
|
620
|
+
PfdnPropertiesProvider,
|
|
621
|
+
PropertiesPanel,
|
|
622
|
+
entryFactoryModule,
|
|
623
|
+
pfdnPropertiesProviderModule,
|
|
624
|
+
propertiesPanelModule
|
|
85
625
|
});
|
|
86
626
|
//# sourceMappingURL=index.cjs.map
|