@d3-polytree/editor 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -10,11 +10,456 @@ import {
10
10
  paletteModule,
11
11
  resizeElementModule
12
12
  } from "@d3-polytree/core";
13
- import {
14
- entryFactoryModule,
15
- pfdnPropertiesProviderModule,
16
- propertiesPanelModule
17
- } from "@d3-polytree/properties-panel";
13
+
14
+ // src/properties-panel/utils.ts
15
+ function is(definition, elementType) {
16
+ return definition.$instanceOf(elementType);
17
+ }
18
+ function deepGet(obj, path) {
19
+ return path.split(".").reduce(
20
+ (o, k) => o == null ? void 0 : o[k],
21
+ obj
22
+ );
23
+ }
24
+ function deepSet(obj, path, value) {
25
+ const keys = path.split(".");
26
+ let cursor = obj;
27
+ for (let i = 0; i < keys.length - 1; i += 1) {
28
+ const key = keys[i];
29
+ if (cursor[key] == null || typeof cursor[key] !== "object") {
30
+ cursor[key] = {};
31
+ }
32
+ cursor = cursor[key];
33
+ }
34
+ cursor[keys[keys.length - 1]] = value;
35
+ }
36
+ function debounce(fn, wait) {
37
+ let timer;
38
+ return (...args) => {
39
+ if (timer) {
40
+ clearTimeout(timer);
41
+ }
42
+ timer = setTimeout(() => fn(...args), wait);
43
+ };
44
+ }
45
+ function startCase(input) {
46
+ 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());
47
+ }
48
+ function isHexColor(value) {
49
+ return typeof value === "string" && /^#[0-9a-fA-F]{6}$/.test(value);
50
+ }
51
+
52
+ // src/properties-panel/EntryFactory.ts
53
+ function ensureNotNull(prop) {
54
+ if (!prop) {
55
+ throw new Error(`${prop} must be set.`);
56
+ }
57
+ return prop;
58
+ }
59
+ var EntryFactory = class {
60
+ constructor(eventBus) {
61
+ this._eventBus = eventBus;
62
+ }
63
+ setDefaultParameters(options) {
64
+ const eventBus = this._eventBus;
65
+ const defaultGet = (element, formNode) => {
66
+ const prop = ensureNotNull(options.modelProperty);
67
+ const value = deepGet(element, prop);
68
+ const input = formNode.querySelector("input");
69
+ if (input) {
70
+ input.value = value == null ? "" : String(value);
71
+ }
72
+ };
73
+ const defaultSet = (element, values) => {
74
+ const prop = ensureNotNull(options.modelProperty);
75
+ deepSet(element, prop, deepGet(values, prop));
76
+ return true;
77
+ };
78
+ const triggerUpdate = (propertyId, element) => {
79
+ eventBus.emit("PropertiesPanel.propertyChanged", propertyId, element);
80
+ };
81
+ return {
82
+ html: "",
83
+ description: "",
84
+ get: defaultGet,
85
+ set: defaultSet,
86
+ validate: () => ({}),
87
+ ...options,
88
+ triggerUpdate
89
+ };
90
+ }
91
+ textField(options) {
92
+ return textInputField(this.setDefaultParameters(options));
93
+ }
94
+ selectBox(options) {
95
+ return selectBoxField(this.setDefaultParameters(options));
96
+ }
97
+ colorPicker(options) {
98
+ return colorPickerField(this.setDefaultParameters(options));
99
+ }
100
+ spreadsheet(options) {
101
+ return this.setDefaultParameters(options);
102
+ }
103
+ };
104
+ EntryFactory.$inject = ["eventBus"];
105
+ function fieldWrapper(resource, inner) {
106
+ const label = resource.label ?? resource.id;
107
+ return `<div class="pfdjs-pp-field-wrapper"><label for="pfdjs-${resource.id}">${label}</label>${inner}</div>`;
108
+ }
109
+ function textInputField(resource) {
110
+ const type = resource.type ?? "text";
111
+ resource.html = fieldWrapper(
112
+ resource,
113
+ `<input id="pfdjs-${resource.id}" type="${type}" name="${resource.modelProperty}" />`
114
+ );
115
+ return resource;
116
+ }
117
+ function selectBoxField(resource) {
118
+ const allowEmpty = resource.allowEmpty ?? true;
119
+ const options = Array.isArray(resource.selectOptions) ? resource.selectOptions.concat(allowEmpty ? [{ name: "", value: "" }] : []) : [{ name: "", value: "" }];
120
+ const optionsHtml = options.map((o) => `<option value="${o.value}">${o.name}</option>`).join("");
121
+ resource.html = fieldWrapper(
122
+ resource,
123
+ `<select id="pfdjs-${resource.id}" name="${resource.modelProperty}">${optionsHtml}</select>`
124
+ );
125
+ resource.get = (element, formNode) => {
126
+ const value = element.get(resource.modelProperty) ?? "default";
127
+ formNode.querySelectorAll(`select#pfdjs-${resource.id} > option`).forEach((option) => {
128
+ option.selected = option.value === value;
129
+ });
130
+ };
131
+ return resource;
132
+ }
133
+ function colorPickerField(resource) {
134
+ resource.html = fieldWrapper(
135
+ resource,
136
+ `<input id="pfdjs-${resource.id}" type="color" name="${resource.modelProperty}" />`
137
+ );
138
+ resource.get = (element, formNode) => {
139
+ const input = formNode.querySelector("input[type=color]");
140
+ const value = deepGet(element, resource.modelProperty);
141
+ if (input && isHexColor(value)) {
142
+ input.value = value;
143
+ }
144
+ };
145
+ return resource;
146
+ }
147
+ var entryFactoryModule = {
148
+ __init__: ["entryFactory"],
149
+ entryFactory: ["type", EntryFactory]
150
+ };
151
+
152
+ // src/properties-panel/PfdnPropertiesProvider.ts
153
+ function nameProps(group, element, factory) {
154
+ if (is(element, "pfdn:Node")) {
155
+ group.entries.push(factory.textField({ id: "name", label: "Name", modelProperty: "name" }));
156
+ group.entries.push(factory.textField({ id: "tag", label: "Tag", modelProperty: "tag" }));
157
+ group.entries.push(
158
+ factory.textField({ id: "label.text", label: "Diagram label", modelProperty: "label.text" })
159
+ );
160
+ } else if (is(element, "pfdn:Settings")) {
161
+ group.entries.push(factory.textField({ id: "name", label: "Diagram name", modelProperty: "name" }));
162
+ group.entries.push(factory.textField({ id: "author", label: "Author's name", modelProperty: "author" }));
163
+ } else if (is(element, "pfdn:Link")) {
164
+ group.entries.push(
165
+ factory.textField({ id: "label.text", label: "Diagram label", modelProperty: "label.text" })
166
+ );
167
+ } else if (is(element, "pfdn:Label")) {
168
+ group.entries.push(factory.textField({ id: "text", label: "Label", modelProperty: "text" }));
169
+ }
170
+ }
171
+ function nodeFormatProps(group, element, factory, icons) {
172
+ if (!is(element, "pfdn:Node")) {
173
+ return;
174
+ }
175
+ const selectOptions = Object.keys(icons).map((key) => ({ value: key, name: startCase(key) }));
176
+ group.entries.push(
177
+ factory.selectBox({
178
+ id: "type",
179
+ label: "Icon",
180
+ modelProperty: "type",
181
+ allowEmpty: false,
182
+ selectOptions
183
+ })
184
+ );
185
+ group.entries.push(
186
+ factory.textField({ id: "size", label: "Size", modelProperty: "size", type: "number" })
187
+ );
188
+ }
189
+ function linkFormatProps(group, element, factory) {
190
+ if (!is(element, "pfdn:Link")) {
191
+ return;
192
+ }
193
+ group.entries.push(
194
+ factory.textField({ id: "lineWidth", label: "Line width", modelProperty: "lineWidth", type: "number" })
195
+ );
196
+ group.entries.push(
197
+ factory.colorPicker({ id: "lineColor", label: "Line color", modelProperty: "lineColor" })
198
+ );
199
+ }
200
+ function labelFormatProps(group, element, factory) {
201
+ if (is(element, "pfdn:Node") || is(element, "pfdn:Link")) {
202
+ group.entries.push(
203
+ factory.textField({
204
+ id: "label.fontSize",
205
+ label: "Font size",
206
+ modelProperty: "label.fontSize",
207
+ type: "number"
208
+ })
209
+ );
210
+ group.entries.push(
211
+ factory.colorPicker({ id: "label.color", label: "Color", modelProperty: "label.color" })
212
+ );
213
+ } else if (is(element, "pfdn:Label")) {
214
+ group.entries.push(
215
+ factory.textField({ id: "fontSize", label: "Font size", modelProperty: "fontSize", type: "number" })
216
+ );
217
+ group.entries.push(factory.colorPicker({ id: "color", label: "Color", modelProperty: "color" }));
218
+ }
219
+ }
220
+ function gridFormatProps(group, element, factory) {
221
+ if (!is(element, "pfdn:Settings")) {
222
+ return;
223
+ }
224
+ group.entries.push(
225
+ factory.colorPicker({ id: "backgroundColor", label: "Background color", modelProperty: "backgroundColor" })
226
+ );
227
+ group.entries.push(
228
+ factory.colorPicker({ id: "grid.lineColor", label: "Line color", modelProperty: "grid.lineColor" })
229
+ );
230
+ group.entries.push(
231
+ factory.textField({ id: "grid.size", label: "Square size", modelProperty: "grid.size", type: "number" })
232
+ );
233
+ group.entries.push(
234
+ factory.textField({ id: "grid.lineWidth", label: "Line width", modelProperty: "grid.lineWidth", type: "number" })
235
+ );
236
+ }
237
+ var PfdnPropertiesProvider = class {
238
+ constructor(icons, entryFactory, eventBus) {
239
+ this._icons = icons;
240
+ this._entryFactory = entryFactory;
241
+ this._eventBus = eventBus;
242
+ }
243
+ updateDrawing(definition) {
244
+ if (is(definition, "pfdn:Settings")) {
245
+ this._eventBus.emit("canvas.resized");
246
+ return;
247
+ }
248
+ this._eventBus.emit("element.updated", definition.id, definition);
249
+ if (is(definition, "pfdn:Link") || is(definition, "pfdn:Node")) {
250
+ const label = definition.label;
251
+ if (label) {
252
+ this._eventBus.emit("element.updated", label.id, label);
253
+ }
254
+ }
255
+ }
256
+ getTabs(element) {
257
+ const factory = this._entryFactory;
258
+ return [this._propertiesTab(element, factory), this._formatTab(element, factory)];
259
+ }
260
+ _propertiesTab(element, factory) {
261
+ const general = { id: "general", label: "General", entries: [] };
262
+ nameProps(general, element, factory);
263
+ return { id: "properties", label: "Properties", groups: [general] };
264
+ }
265
+ _formatTab(element, factory) {
266
+ const elementFormat = { id: "elementFormat", label: "Element format", entries: [] };
267
+ const labelFormat = { id: "labelFormat", label: "Label format", entries: [] };
268
+ const gridFormat = { id: "gridFormat", label: "Grid format", entries: [] };
269
+ nodeFormatProps(elementFormat, element, factory, this._icons);
270
+ linkFormatProps(elementFormat, element, factory);
271
+ labelFormatProps(labelFormat, element, factory);
272
+ gridFormatProps(gridFormat, element, factory);
273
+ return { id: "format", label: "Format", groups: [elementFormat, labelFormat, gridFormat] };
274
+ }
275
+ };
276
+ PfdnPropertiesProvider.$inject = ["icons", "entryFactory", "eventBus"];
277
+ var pfdnPropertiesProviderModule = {
278
+ __init__: ["propertiesProvider"],
279
+ propertiesProvider: ["type", PfdnPropertiesProvider]
280
+ };
281
+
282
+ // src/properties-panel/PropertiesPanel.ts
283
+ function fromHtml(html) {
284
+ const template = document.createElement("template");
285
+ template.innerHTML = html.trim();
286
+ return template.content.firstElementChild;
287
+ }
288
+ var FORM_CONTROLS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
289
+ var _PropertiesPanel = class _PropertiesPanel {
290
+ constructor(sideTabsProvider, eventBus, propertiesProvider, diagramSettings) {
291
+ this._entries = {};
292
+ this._container = null;
293
+ this._tabsEl = null;
294
+ this._contentsEl = null;
295
+ this._eventBus = eventBus;
296
+ this._propertiesProvider = propertiesProvider;
297
+ this._diagramSettings = diagramSettings;
298
+ this._registerSideTab(sideTabsProvider);
299
+ this._registerSelectionListener();
300
+ }
301
+ _registerSideTab(provider) {
302
+ provider.registerSideTab(
303
+ {
304
+ title: "Properties",
305
+ iconClassName: "icon-sliders",
306
+ action: { created: (content) => this._drawPanel(content) }
307
+ },
308
+ 1
309
+ );
310
+ }
311
+ _registerSelectionListener() {
312
+ this._eventBus.on(
313
+ "selection.changed",
314
+ (oldSelection, newSelection) => {
315
+ let selected = this._diagramSettings;
316
+ if (newSelection.length === 1 && (oldSelection.length !== 1 || oldSelection[0].definition.id !== newSelection[0].definition.id)) {
317
+ selected = newSelection[0].definition;
318
+ }
319
+ this._update(selected);
320
+ }
321
+ );
322
+ }
323
+ _drawPanel(content) {
324
+ if (!content) {
325
+ return;
326
+ }
327
+ this._drawContainer(content);
328
+ this._update(this._diagramSettings);
329
+ }
330
+ _drawContainer(content) {
331
+ this._container = fromHtml(_PropertiesPanel.HTML_MARKUP);
332
+ content.insertBefore(this._container, content.firstChild);
333
+ this._tabsEl = this._container.querySelector(".tab-sheets");
334
+ this._contentsEl = this._container.querySelector(".pfdjs-pp-contents");
335
+ this._container.addEventListener("click", (event) => {
336
+ const tab = event.target.closest(".tab-sheet");
337
+ if (tab) {
338
+ this._selectTab(tab.getAttribute("data-tab-target"));
339
+ event.stopImmediatePropagation();
340
+ }
341
+ });
342
+ this._registerInputChangeHandlers();
343
+ }
344
+ _registerInputChangeHandlers() {
345
+ const container = this._container;
346
+ if (!container) {
347
+ return;
348
+ }
349
+ const debouncedApply = debounce((target) => this._applyChange(target), 300);
350
+ container.addEventListener("input", (event) => {
351
+ const target = event.target;
352
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
353
+ debouncedApply(target);
354
+ }
355
+ });
356
+ container.addEventListener("change", (event) => {
357
+ const target = event.target;
358
+ if (FORM_CONTROLS.has(target.tagName)) {
359
+ this._applyChange(target);
360
+ }
361
+ });
362
+ this._eventBus.on(
363
+ "PropertiesPanel.propertyChanged",
364
+ (propertyId, definition) => this._applyChangeByProperty(propertyId, definition)
365
+ );
366
+ }
367
+ _applyChange(target) {
368
+ const entryId = target.getAttribute("name");
369
+ if (!entryId) {
370
+ return;
371
+ }
372
+ this._commit(entryId, target.value);
373
+ }
374
+ _applyChangeByProperty(propertyId, definition) {
375
+ this._commit(propertyId, deepGet(definition, propertyId));
376
+ }
377
+ _commit(entryId, newValue) {
378
+ const entry = this._entries[entryId];
379
+ if (!entry) {
380
+ return;
381
+ }
382
+ const props = {};
383
+ deepSet(props, entryId, newValue);
384
+ entry.scope.set(entry.definition, props);
385
+ this._propertiesProvider.updateDrawing(entry.definition);
386
+ }
387
+ _selectTab(tabId) {
388
+ this._tabsEl?.querySelectorAll(".tab-sheet").forEach((tab) => {
389
+ tab.classList.toggle("tab-sheet-active", tab.getAttribute("data-tab-target") === tabId);
390
+ });
391
+ this._contentsEl?.querySelectorAll(".pfdjs-pp-content").forEach((content) => {
392
+ content.classList.toggle("open", content.getAttribute("data-tab-target") === tabId);
393
+ });
394
+ }
395
+ _update(definition) {
396
+ if (this._tabsEl && this._contentsEl) {
397
+ this._tabsEl.replaceChildren();
398
+ this._contentsEl.replaceChildren();
399
+ }
400
+ if (definition) {
401
+ this._drawEntries(definition);
402
+ }
403
+ }
404
+ _drawEntries(definition) {
405
+ if (!this._tabsEl || !this._contentsEl) {
406
+ return;
407
+ }
408
+ const tabs = this._propertiesProvider.getTabs(definition);
409
+ const renderedTabs = [];
410
+ this._entries = {};
411
+ for (const tab of tabs) {
412
+ const content = fromHtml(`<div class="pfdjs-pp-content" data-tab-target="${tab.id}"></div>`);
413
+ let tabHasContent = false;
414
+ for (const group of tab.groups) {
415
+ if (group.entries.length === 0) {
416
+ continue;
417
+ }
418
+ const groupContent = fromHtml(
419
+ `<div class="pfdjs-pp-content-group" data-group-target="${group.id}"><div class="tab-content-group-title">${group.label}</div></div>`
420
+ );
421
+ for (const entry of group.entries) {
422
+ const entryNode = fromHtml(`<div>${entry.html}</div>`);
423
+ groupContent.appendChild(entryNode);
424
+ this._entries[entry.id] = { scope: entry, definition, formNode: entryNode };
425
+ }
426
+ content.appendChild(groupContent);
427
+ tabHasContent = true;
428
+ }
429
+ if (tabHasContent) {
430
+ this._contentsEl.appendChild(content);
431
+ renderedTabs.push({ id: tab.id, label: tab.label });
432
+ }
433
+ }
434
+ Object.values(this._entries).forEach((entry) => entry.scope.get(entry.definition, entry.formNode));
435
+ let firstTab = null;
436
+ for (const tab of renderedTabs) {
437
+ firstTab ?? (firstTab = tab.id);
438
+ this._tabsEl.appendChild(
439
+ fromHtml(
440
+ `<li class="tab-sheet" data-tab-target="${tab.id}"><a href="#">${tab.label}</a></li>`
441
+ )
442
+ );
443
+ }
444
+ this._selectTab(firstTab);
445
+ }
446
+ };
447
+ _PropertiesPanel.$inject = [
448
+ "sideTabsProvider",
449
+ "eventBus",
450
+ "propertiesProvider",
451
+ "d3polytree.definitions.settings"
452
+ ];
453
+ _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>';
454
+ var PropertiesPanel = _PropertiesPanel;
455
+
456
+ // src/properties-panel/index.ts
457
+ var propertiesPanelModule = {
458
+ __init__: ["propertiesPanel"],
459
+ propertiesPanel: ["type", PropertiesPanel]
460
+ };
461
+
462
+ // src/index.ts
18
463
  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>';
19
464
  var _Editor = class _Editor extends InteractiveViewer {
20
465
  constructor() {
@@ -68,6 +513,12 @@ var Editor = _Editor;
68
513
  var index_default = Editor;
69
514
  export {
70
515
  Editor,
71
- index_default as default
516
+ EntryFactory,
517
+ PfdnPropertiesProvider,
518
+ PropertiesPanel,
519
+ index_default as default,
520
+ entryFactoryModule,
521
+ pfdnPropertiesProviderModule,
522
+ propertiesPanelModule
72
523
  };
73
524
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @d3-polytree/editor — create and modify polytree diagrams.\n *\n * Extends the {@link InteractiveViewer} with the editing layer: element\n * dragging plus the modelling create/save/delete flows. Element creation is\n * exposed programmatically here; the palette toolbar UI is layered on next.\n */\nimport { InteractiveViewer, type InteractiveViewerOptions } from '@d3-polytree/interactive-viewer';\nimport { Viewer } from '@d3-polytree/viewer';\nimport {\n dragModule,\n modellingModule,\n exportingModule,\n localStorageModule,\n uploadModule,\n paletteModule,\n resizeElementModule,\n type DiagramModule,\n type DrawingRegistry,\n type ModellingNodes,\n type ModellingModelElement,\n type CreateParameters,\n type Selection\n} from '@d3-polytree/core';\nimport {\n entryFactoryModule,\n pfdnPropertiesProviderModule,\n propertiesPanelModule\n} from '@d3-polytree/properties-panel';\n\n/** The document a fresh editor opens with. */\nconst INITIAL_DIAGRAM =\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>' +\n '<pfdn:diagram xmlns:pfdn=\"http://pfdn\" xmlns=\"http://pfdn\">' +\n '<settings author=\"No Author\" name=\"No Name Diagram\" status=\"1\">' +\n '<zoom><offset x=\"0\" y=\"0\" /><scale>1</scale></zoom><grid />' +\n '</settings>' +\n '<node id=\"node_1\" label=\"label_1\" status=\"1\"><position x=\"20\" y=\"100\" /></node>' +\n '<label id=\"label_1\" fontSize=\"12\" isReadOnly=\"true\" status=\"1\">' +\n '<position x=\"33\" y=\"140\" /><text>Node 1</text></label>' +\n '</pfdn:diagram>';\n\nexport type EditorOptions = InteractiveViewerOptions;\n\nexport class Editor extends InteractiveViewer {\n /** Editing modules on top of the interaction layer. */\n static readonly editionModules: readonly DiagramModule[] = [\n dragModule as DiagramModule,\n modellingModule as DiagramModule,\n exportingModule as DiagramModule,\n localStorageModule as DiagramModule,\n uploadModule as DiagramModule,\n paletteModule as DiagramModule,\n resizeElementModule as DiagramModule,\n // the properties panel (registers a side tab; side-tabs + search-panel are\n // inherited from InteractiveViewer)\n entryFactoryModule as DiagramModule,\n pfdnPropertiesProviderModule as DiagramModule,\n propertiesPanelModule as DiagramModule\n ];\n\n /** The document a fresh editor opens with (used by {@link createDiagram}). */\n initialDiagram = INITIAL_DIAGRAM;\n\n /** (Re)open the initial diagram. */\n createDiagram(): Promise<void> {\n return this.importDiagram(this.initialDiagram);\n }\n\n getModules(): readonly DiagramModule[] {\n // interaction + editing features first (they subscribe / swap the layer),\n // then the drawers\n return [\n ...InteractiveViewer.interactionModules,\n ...Editor.editionModules,\n ...Viewer.modules\n ];\n }\n\n /** Create a node (and its associated label) at an optional position. */\n createNode(parameters: CreateParameters = {}): ModellingModelElement {\n return this.get<ModellingNodes>('modellingNodes').create(parameters);\n }\n\n /** Select an element by definition (e.g. to prepare a delete). */\n select(definition: ModellingModelElement): void {\n const element = this.get<DrawingRegistry>('drawingRegistry').get(definition.id as string);\n if (element) {\n this.get<Selection>('selection').select(element, definition);\n }\n }\n\n /** Delete the current selection (cascading to associated labels). */\n deleteSelected(): void {\n this.get<Selection>('selection').deleteSelected();\n }\n}\n\nexport default Editor;\n"],"mappings":";AAOA,SAAS,yBAAwD;AACjE,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAOK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,IAAM,kBACJ;AAYK,IAAM,UAAN,MAAM,gBAAe,kBAAkB;AAAA,EAAvC;AAAA;AAkBL;AAAA,0BAAiB;AAAA;AAAA;AAAA,EAGjB,gBAA+B;AAC7B,WAAO,KAAK,cAAc,KAAK,cAAc;AAAA,EAC/C;AAAA,EAEA,aAAuC;AAGrC,WAAO;AAAA,MACL,GAAG,kBAAkB;AAAA,MACrB,GAAG,QAAO;AAAA,MACV,GAAG,OAAO;AAAA,IACZ;AAAA,EACF;AAAA;AAAA,EAGA,WAAW,aAA+B,CAAC,GAA0B;AACnE,WAAO,KAAK,IAAoB,gBAAgB,EAAE,OAAO,UAAU;AAAA,EACrE;AAAA;AAAA,EAGA,OAAO,YAAyC;AAC9C,UAAM,UAAU,KAAK,IAAqB,iBAAiB,EAAE,IAAI,WAAW,EAAY;AACxF,QAAI,SAAS;AACX,WAAK,IAAe,WAAW,EAAE,OAAO,SAAS,UAAU;AAAA,IAC7D;AAAA,EACF;AAAA;AAAA,EAGA,iBAAuB;AACrB,SAAK,IAAe,WAAW,EAAE,eAAe;AAAA,EAClD;AACF;AAAA;AApDa,QAEK,iBAA2C;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AACF;AAfK,IAAM,SAAN;AAsDP,IAAO,gBAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/properties-panel/utils.ts","../src/properties-panel/EntryFactory.ts","../src/properties-panel/PfdnPropertiesProvider.ts","../src/properties-panel/PropertiesPanel.ts","../src/properties-panel/index.ts"],"sourcesContent":["/**\n * @d3-polytree/editor — create and modify polytree diagrams.\n *\n * Extends the {@link InteractiveViewer} with the editing layer: element\n * dragging plus the modelling create/save/delete flows. Element creation is\n * exposed programmatically here; the palette toolbar UI is layered on next.\n */\nimport { InteractiveViewer, type InteractiveViewerOptions } from '@d3-polytree/interactive-viewer';\nimport { Viewer } from '@d3-polytree/viewer';\nimport {\n dragModule,\n modellingModule,\n exportingModule,\n localStorageModule,\n uploadModule,\n paletteModule,\n resizeElementModule,\n type DiagramModule,\n type DrawingRegistry,\n type ModellingNodes,\n type ModellingModelElement,\n type CreateParameters,\n type Selection\n} from '@d3-polytree/core';\n// The properties panel used to be its own package; it is now folded in here\n// (the editor was its only consumer) and re-exported below.\nimport {\n entryFactoryModule,\n pfdnPropertiesProviderModule,\n propertiesPanelModule\n} from './properties-panel';\n\nexport * from './properties-panel';\n\n/** The document a fresh editor opens with. */\nconst INITIAL_DIAGRAM =\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>' +\n '<pfdn:diagram xmlns:pfdn=\"http://pfdn\" xmlns=\"http://pfdn\">' +\n '<settings author=\"No Author\" name=\"No Name Diagram\" status=\"1\">' +\n '<zoom><offset x=\"0\" y=\"0\" /><scale>1</scale></zoom><grid />' +\n '</settings>' +\n '<node id=\"node_1\" label=\"label_1\" status=\"1\"><position x=\"20\" y=\"100\" /></node>' +\n '<label id=\"label_1\" fontSize=\"12\" isReadOnly=\"true\" status=\"1\">' +\n '<position x=\"33\" y=\"140\" /><text>Node 1</text></label>' +\n '</pfdn:diagram>';\n\nexport type EditorOptions = InteractiveViewerOptions;\n\nexport class Editor extends InteractiveViewer {\n /** Editing modules on top of the interaction layer. */\n static readonly editionModules: readonly DiagramModule[] = [\n dragModule as DiagramModule,\n modellingModule as DiagramModule,\n exportingModule as DiagramModule,\n localStorageModule as DiagramModule,\n uploadModule as DiagramModule,\n paletteModule as DiagramModule,\n resizeElementModule as DiagramModule,\n // the properties panel (registers a side tab; side-tabs + search-panel are\n // inherited from InteractiveViewer)\n entryFactoryModule as DiagramModule,\n pfdnPropertiesProviderModule as DiagramModule,\n propertiesPanelModule as DiagramModule\n ];\n\n /** The document a fresh editor opens with (used by {@link createDiagram}). */\n initialDiagram = INITIAL_DIAGRAM;\n\n /** (Re)open the initial diagram. */\n createDiagram(): Promise<void> {\n return this.importDiagram(this.initialDiagram);\n }\n\n getModules(): readonly DiagramModule[] {\n // interaction + editing features first (they subscribe / swap the layer),\n // then the drawers\n return [\n ...InteractiveViewer.interactionModules,\n ...Editor.editionModules,\n ...Viewer.modules\n ];\n }\n\n /** Create a node (and its associated label) at an optional position. */\n createNode(parameters: CreateParameters = {}): ModellingModelElement {\n return this.get<ModellingNodes>('modellingNodes').create(parameters);\n }\n\n /** Select an element by definition (e.g. to prepare a delete). */\n select(definition: ModellingModelElement): void {\n const element = this.get<DrawingRegistry>('drawingRegistry').get(definition.id as string);\n if (element) {\n this.get<Selection>('selection').select(element, definition);\n }\n }\n\n /** Delete the current selection (cascading to associated labels). */\n deleteSelected(): void {\n this.get<Selection>('selection').deleteSelected();\n }\n}\n\nexport default Editor;\n","/** A moddle model element as read/written by the properties panel. */\nexport interface Definition {\n id?: string;\n label?: Definition;\n $instanceOf(type: string): boolean;\n get(property: string): unknown;\n [key: string]: unknown;\n}\n\n/** `definition.$instanceOf(elementType)` — ported from `utils/modelUtils`. */\nexport function is(definition: Definition, elementType: string): boolean {\n return definition.$instanceOf(elementType);\n}\n\n/** Read a dotted property path (`label.text`) by plain property access. */\nexport function deepGet(obj: unknown, path: string): unknown {\n return path\n .split('.')\n .reduce<unknown>(\n (o, k) => (o == null ? undefined : (o as Record<string, unknown>)[k]),\n obj\n );\n}\n\n/** Write a dotted property path, creating intermediate objects as needed. */\nexport function deepSet(obj: Record<string, unknown>, path: string, value: unknown): void {\n const keys = path.split('.');\n let cursor = obj;\n for (let i = 0; i < keys.length - 1; i += 1) {\n const key = keys[i];\n if (cursor[key] == null || typeof cursor[key] !== 'object') {\n cursor[key] = {};\n }\n cursor = cursor[key] as Record<string, unknown>;\n }\n cursor[keys[keys.length - 1]] = value;\n}\n\n/** Trailing-nothing leading-edge? No — trailing debounce, like lodash's default. */\nexport function debounce<A extends unknown[]>(\n fn: (...args: A) => void,\n wait: number\n): (...args: A) => void {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return (...args: A) => {\n if (timer) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => fn(...args), wait);\n };\n}\n\n/** Title-case a token (`lineColor` → `Line Color`, `aws-ec2` → `Aws Ec 2`). */\nexport function startCase(input: string): string {\n return input\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/([a-zA-Z])([0-9])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/** Whether a string is a `#rrggbb` colour (accepted by `<input type=color>`). */\nexport function isHexColor(value: unknown): value is string {\n return typeof value === 'string' && /^#[0-9a-fA-F]{6}$/.test(value);\n}\n","import type EventEmitter from 'eventemitter3';\nimport { deepGet, deepSet, isHexColor, type Definition } from './utils';\n\n/** A `{name,value}` option for a select entry. */\nexport interface SelectOption {\n name: string;\n value: string;\n}\n\n/** Options accepted when building an entry. */\nexport interface EntryOptions {\n id: string;\n modelProperty: string;\n label?: string;\n description?: string;\n type?: string;\n allowEmpty?: boolean;\n selectOptions?: SelectOption[];\n}\n\n/** A rendered property entry: its markup plus get/set behaviour. */\nexport interface EntryResource extends EntryOptions {\n html: string;\n get: (element: Definition, formNode: HTMLElement) => void;\n set: (element: Definition, values: Record<string, unknown>) => boolean;\n validate: () => Record<string, unknown>;\n triggerUpdate: (propertyId: string, element: Definition) => void;\n}\n\nfunction ensureNotNull(prop: string | undefined): string {\n if (!prop) {\n throw new Error(`${prop} must be set.`);\n }\n return prop;\n}\n\n/**\n * Builds property-panel entries (text / select / colour / spreadsheet). Ported\n * from `@d3-polytree/properties-panel`'s `entryFactory/*`, de-jQuery-ed onto\n * native DOM; the colour entry uses a native `<input type=\"color\">` in place of\n * spectrum-colorpicker.\n */\nexport class EntryFactory {\n static readonly $inject = ['eventBus'];\n\n private readonly _eventBus: EventEmitter;\n\n constructor(eventBus: EventEmitter) {\n this._eventBus = eventBus;\n }\n\n setDefaultParameters(options: EntryOptions): EntryResource {\n const eventBus = this._eventBus;\n\n const defaultGet = (element: Definition, formNode: HTMLElement): void => {\n const prop = ensureNotNull(options.modelProperty);\n const value = deepGet(element, prop);\n const input = formNode.querySelector('input');\n if (input) {\n (input as HTMLInputElement).value = value == null ? '' : String(value);\n }\n };\n\n const defaultSet = (element: Definition, values: Record<string, unknown>): boolean => {\n const prop = ensureNotNull(options.modelProperty);\n deepSet(element as unknown as Record<string, unknown>, prop, deepGet(values, prop));\n return true;\n };\n\n const triggerUpdate = (propertyId: string, element: Definition): void => {\n eventBus.emit('PropertiesPanel.propertyChanged', propertyId, element);\n };\n\n return {\n html: '',\n description: '',\n get: defaultGet,\n set: defaultSet,\n validate: () => ({}),\n ...options,\n triggerUpdate\n };\n }\n\n textField(options: EntryOptions): EntryResource {\n return textInputField(this.setDefaultParameters(options));\n }\n\n selectBox(options: EntryOptions): EntryResource {\n return selectBoxField(this.setDefaultParameters(options));\n }\n\n colorPicker(options: EntryOptions): EntryResource {\n return colorPickerField(this.setDefaultParameters(options));\n }\n\n spreadsheet(options: EntryOptions): EntryResource {\n // Placeholder in the source engine; kept as an API-compatible no-op.\n return this.setDefaultParameters(options);\n }\n}\n\nfunction fieldWrapper(resource: EntryResource, inner: string): string {\n const label = resource.label ?? resource.id;\n return (\n `<div class=\"pfdjs-pp-field-wrapper\">` +\n `<label for=\"pfdjs-${resource.id}\">${label}</label>${inner}</div>`\n );\n}\n\nfunction textInputField(resource: EntryResource): EntryResource {\n const type = resource.type ?? 'text';\n resource.html = fieldWrapper(\n resource,\n `<input id=\"pfdjs-${resource.id}\" type=\"${type}\" name=\"${resource.modelProperty}\" />`\n );\n return resource;\n}\n\nfunction selectBoxField(resource: EntryResource): EntryResource {\n const allowEmpty = resource.allowEmpty ?? true;\n const options = Array.isArray(resource.selectOptions)\n ? resource.selectOptions.concat(allowEmpty ? [{ name: '', value: '' }] : [])\n : [{ name: '', value: '' }];\n\n const optionsHtml = options\n .map((o) => `<option value=\"${o.value}\">${o.name}</option>`)\n .join('');\n resource.html = fieldWrapper(\n resource,\n `<select id=\"pfdjs-${resource.id}\" name=\"${resource.modelProperty}\">${optionsHtml}</select>`\n );\n\n resource.get = (element, formNode) => {\n const value = element.get(resource.modelProperty) ?? 'default';\n formNode\n .querySelectorAll<HTMLOptionElement>(`select#pfdjs-${resource.id} > option`)\n .forEach((option) => {\n option.selected = option.value === value;\n });\n };\n\n return resource;\n}\n\nfunction colorPickerField(resource: EntryResource): EntryResource {\n resource.html = fieldWrapper(\n resource,\n `<input id=\"pfdjs-${resource.id}\" type=\"color\" name=\"${resource.modelProperty}\" />`\n );\n resource.get = (element, formNode) => {\n const input = formNode.querySelector<HTMLInputElement>('input[type=color]');\n const value = deepGet(element, resource.modelProperty);\n if (input && isHexColor(value)) {\n input.value = value;\n }\n };\n return resource;\n}\n\n/** didi module contributing the entry factory. */\nexport const entryFactoryModule = {\n __init__: ['entryFactory'],\n entryFactory: ['type', EntryFactory]\n};\n","import type EventEmitter from 'eventemitter3';\nimport type { EntryFactory, EntryResource } from './EntryFactory';\nimport { is, startCase, type Definition } from './utils';\n\n/** A group of entries within a tab. */\nexport interface PropertiesGroup {\n id: string;\n label: string;\n entries: EntryResource[];\n}\n\n/** A properties-panel tab. */\nexport interface PropertiesTab {\n id: string;\n label: string;\n groups: PropertiesGroup[];\n}\n\n/** Icon registry token: icon type → SVG source. */\nexport type IconMap = Record<string, string>;\n\n/** A properties provider: supplies the tabs for a selected element. */\nexport interface PropertiesProvider {\n getTabs(element: Definition): PropertiesTab[];\n updateDrawing(definition: Definition): void;\n}\n\n// --- pfdn property parts (ported from provider/pfdn/tabs/parts/*) ------------\n\nfunction nameProps(group: PropertiesGroup, element: Definition, factory: EntryFactory): void {\n if (is(element, 'pfdn:Node')) {\n group.entries.push(factory.textField({ id: 'name', label: 'Name', modelProperty: 'name' }));\n group.entries.push(factory.textField({ id: 'tag', label: 'Tag', modelProperty: 'tag' }));\n group.entries.push(\n factory.textField({ id: 'label.text', label: 'Diagram label', modelProperty: 'label.text' })\n );\n } else if (is(element, 'pfdn:Settings')) {\n group.entries.push(factory.textField({ id: 'name', label: 'Diagram name', modelProperty: 'name' }));\n group.entries.push(factory.textField({ id: 'author', label: \"Author's name\", modelProperty: 'author' }));\n } else if (is(element, 'pfdn:Link')) {\n group.entries.push(\n factory.textField({ id: 'label.text', label: 'Diagram label', modelProperty: 'label.text' })\n );\n } else if (is(element, 'pfdn:Label')) {\n group.entries.push(factory.textField({ id: 'text', label: 'Label', modelProperty: 'text' }));\n }\n}\n\nfunction nodeFormatProps(\n group: PropertiesGroup,\n element: Definition,\n factory: EntryFactory,\n icons: IconMap\n): void {\n if (!is(element, 'pfdn:Node')) {\n return;\n }\n const selectOptions = Object.keys(icons).map((key) => ({ value: key, name: startCase(key) }));\n group.entries.push(\n factory.selectBox({\n id: 'type',\n label: 'Icon',\n modelProperty: 'type',\n allowEmpty: false,\n selectOptions\n })\n );\n group.entries.push(\n factory.textField({ id: 'size', label: 'Size', modelProperty: 'size', type: 'number' })\n );\n}\n\nfunction linkFormatProps(group: PropertiesGroup, element: Definition, factory: EntryFactory): void {\n if (!is(element, 'pfdn:Link')) {\n return;\n }\n group.entries.push(\n factory.textField({ id: 'lineWidth', label: 'Line width', modelProperty: 'lineWidth', type: 'number' })\n );\n group.entries.push(\n factory.colorPicker({ id: 'lineColor', label: 'Line color', modelProperty: 'lineColor' })\n );\n}\n\nfunction labelFormatProps(group: PropertiesGroup, element: Definition, factory: EntryFactory): void {\n if (is(element, 'pfdn:Node') || is(element, 'pfdn:Link')) {\n group.entries.push(\n factory.textField({\n id: 'label.fontSize',\n label: 'Font size',\n modelProperty: 'label.fontSize',\n type: 'number'\n })\n );\n group.entries.push(\n factory.colorPicker({ id: 'label.color', label: 'Color', modelProperty: 'label.color' })\n );\n } else if (is(element, 'pfdn:Label')) {\n group.entries.push(\n factory.textField({ id: 'fontSize', label: 'Font size', modelProperty: 'fontSize', type: 'number' })\n );\n group.entries.push(factory.colorPicker({ id: 'color', label: 'Color', modelProperty: 'color' }));\n }\n}\n\nfunction gridFormatProps(group: PropertiesGroup, element: Definition, factory: EntryFactory): void {\n if (!is(element, 'pfdn:Settings')) {\n return;\n }\n group.entries.push(\n factory.colorPicker({ id: 'backgroundColor', label: 'Background color', modelProperty: 'backgroundColor' })\n );\n group.entries.push(\n factory.colorPicker({ id: 'grid.lineColor', label: 'Line color', modelProperty: 'grid.lineColor' })\n );\n group.entries.push(\n factory.textField({ id: 'grid.size', label: 'Square size', modelProperty: 'grid.size', type: 'number' })\n );\n group.entries.push(\n factory.textField({ id: 'grid.lineWidth', label: 'Line width', modelProperty: 'grid.lineWidth', type: 'number' })\n );\n}\n\n/**\n * The PFDN properties provider: builds the Properties + Format tabs for the\n * selected element, and applies edits back to the drawing. Ported from\n * `provider/pfdn/*`.\n */\nexport class PfdnPropertiesProvider implements PropertiesProvider {\n static readonly $inject = ['icons', 'entryFactory', 'eventBus'];\n\n private readonly _icons: IconMap;\n private readonly _entryFactory: EntryFactory;\n private readonly _eventBus: EventEmitter;\n\n constructor(icons: IconMap, entryFactory: EntryFactory, eventBus: EventEmitter) {\n this._icons = icons;\n this._entryFactory = entryFactory;\n this._eventBus = eventBus;\n }\n\n updateDrawing(definition: Definition): void {\n if (is(definition, 'pfdn:Settings')) {\n this._eventBus.emit('canvas.resized');\n return;\n }\n this._eventBus.emit('element.updated', definition.id, definition);\n if (is(definition, 'pfdn:Link') || is(definition, 'pfdn:Node')) {\n const label = definition.label;\n if (label) {\n this._eventBus.emit('element.updated', label.id, label);\n }\n }\n }\n\n getTabs(element: Definition): PropertiesTab[] {\n const factory = this._entryFactory;\n return [this._propertiesTab(element, factory), this._formatTab(element, factory)];\n }\n\n private _propertiesTab(element: Definition, factory: EntryFactory): PropertiesTab {\n const general: PropertiesGroup = { id: 'general', label: 'General', entries: [] };\n nameProps(general, element, factory);\n return { id: 'properties', label: 'Properties', groups: [general] };\n }\n\n private _formatTab(element: Definition, factory: EntryFactory): PropertiesTab {\n const elementFormat: PropertiesGroup = { id: 'elementFormat', label: 'Element format', entries: [] };\n const labelFormat: PropertiesGroup = { id: 'labelFormat', label: 'Label format', entries: [] };\n const gridFormat: PropertiesGroup = { id: 'gridFormat', label: 'Grid format', entries: [] };\n\n nodeFormatProps(elementFormat, element, factory, this._icons);\n linkFormatProps(elementFormat, element, factory);\n labelFormatProps(labelFormat, element, factory);\n gridFormatProps(gridFormat, element, factory);\n\n return { id: 'format', label: 'Format', groups: [elementFormat, labelFormat, gridFormat] };\n }\n}\n\n/** didi module contributing the PFDN properties provider. */\nexport const pfdnPropertiesProviderModule = {\n __init__: ['propertiesProvider'],\n propertiesProvider: ['type', PfdnPropertiesProvider]\n};\n","import type EventEmitter from 'eventemitter3';\nimport type { EntryResource } from './EntryFactory';\nimport type { PropertiesProvider } from './PfdnPropertiesProvider';\nimport { debounce, deepGet, deepSet, type Definition } from './utils';\n\n/** The side-tab registration surface the panel needs (structural). */\nexport interface SideTabRegistration {\n title?: string;\n iconClassName?: string;\n action: {\n created?: (content: HTMLElement | null) => void;\n [gesture: string]: ((content: HTMLElement | null) => void) | undefined;\n };\n}\nexport interface SideTabsRegistrar {\n registerSideTab(tab: SideTabRegistration, index?: number): void;\n}\n\n/** A selection entry as delivered by the core `selection.changed` event. */\ninterface SelectionEntry {\n definition: Definition;\n}\n\ninterface TrackedEntry {\n scope: EntryResource;\n definition: Definition;\n formNode: HTMLElement;\n}\n\n/** Parse an HTML fragment into its first element. */\nfunction fromHtml(html: string): HTMLElement {\n const template = document.createElement('template');\n template.innerHTML = html.trim();\n return template.content.firstElementChild as HTMLElement;\n}\n\nconst FORM_CONTROLS = new Set(['INPUT', 'TEXTAREA', 'SELECT']);\n\n/**\n * The editor properties panel: a tabbed editor for the selected element's\n * model properties. Ported from `@d3-polytree/properties-panel`'s\n * `PropertiesPanel.js`, de-jQuery-ed onto native DOM; the `scroll-tabs` widget\n * is replaced by native click-to-select tabs.\n */\nexport class PropertiesPanel {\n static readonly $inject = [\n 'sideTabsProvider',\n 'eventBus',\n 'propertiesProvider',\n 'd3polytree.definitions.settings'\n ];\n\n private readonly _eventBus: EventEmitter;\n private readonly _propertiesProvider: PropertiesProvider;\n private readonly _diagramSettings: Definition;\n private _entries: Record<string, TrackedEntry> = {};\n\n private _container: HTMLElement | null = null;\n private _tabsEl: HTMLElement | null = null;\n private _contentsEl: HTMLElement | null = null;\n\n constructor(\n sideTabsProvider: SideTabsRegistrar,\n eventBus: EventEmitter,\n propertiesProvider: PropertiesProvider,\n diagramSettings: Definition\n ) {\n this._eventBus = eventBus;\n this._propertiesProvider = propertiesProvider;\n this._diagramSettings = diagramSettings;\n this._registerSideTab(sideTabsProvider);\n this._registerSelectionListener();\n }\n\n private _registerSideTab(provider: SideTabsRegistrar): void {\n provider.registerSideTab(\n {\n title: 'Properties',\n iconClassName: 'icon-sliders',\n action: { created: (content) => this._drawPanel(content) }\n },\n 1\n );\n }\n\n private _registerSelectionListener(): void {\n this._eventBus.on(\n 'selection.changed',\n (oldSelection: SelectionEntry[], newSelection: SelectionEntry[]) => {\n let selected: Definition = this._diagramSettings;\n if (\n newSelection.length === 1 &&\n (oldSelection.length !== 1 ||\n oldSelection[0].definition.id !== newSelection[0].definition.id)\n ) {\n selected = newSelection[0].definition;\n }\n this._update(selected);\n }\n );\n }\n\n private _drawPanel(content: HTMLElement | null): void {\n if (!content) {\n return;\n }\n this._drawContainer(content);\n this._update(this._diagramSettings);\n }\n\n private _drawContainer(content: HTMLElement): void {\n this._container = fromHtml(PropertiesPanel.HTML_MARKUP);\n content.insertBefore(this._container, content.firstChild);\n this._tabsEl = this._container.querySelector('.tab-sheets');\n this._contentsEl = this._container.querySelector('.pfdjs-pp-contents');\n\n this._container.addEventListener('click', (event) => {\n const tab = (event.target as Element).closest<HTMLElement>('.tab-sheet');\n if (tab) {\n this._selectTab(tab.getAttribute('data-tab-target'));\n event.stopImmediatePropagation();\n }\n });\n this._registerInputChangeHandlers();\n }\n\n private _registerInputChangeHandlers(): void {\n const container = this._container;\n if (!container) {\n return;\n }\n // debounce keystroke updates on text inputs/areas; selects fire on change.\n const debouncedApply = debounce((target: HTMLElement) => this._applyChange(target), 300);\n container.addEventListener('input', (event) => {\n const target = event.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {\n debouncedApply(target);\n }\n });\n container.addEventListener('change', (event) => {\n const target = event.target as HTMLElement;\n if (FORM_CONTROLS.has(target.tagName)) {\n this._applyChange(target);\n }\n });\n this._eventBus.on(\n 'PropertiesPanel.propertyChanged',\n (propertyId: string, definition: Definition) => this._applyChangeByProperty(propertyId, definition)\n );\n }\n\n private _applyChange(target: HTMLElement): void {\n const entryId = target.getAttribute('name');\n if (!entryId) {\n return;\n }\n this._commit(entryId, (target as HTMLInputElement).value);\n }\n\n private _applyChangeByProperty(propertyId: string, definition: Definition): void {\n this._commit(propertyId, deepGet(definition, propertyId));\n }\n\n private _commit(entryId: string, newValue: unknown): void {\n const entry = this._entries[entryId];\n if (!entry) {\n return;\n }\n const props: Record<string, unknown> = {};\n deepSet(props, entryId, newValue);\n entry.scope.set(entry.definition, props);\n this._propertiesProvider.updateDrawing(entry.definition);\n }\n\n private _selectTab(tabId: string | null): void {\n this._tabsEl?.querySelectorAll<HTMLElement>('.tab-sheet').forEach((tab) => {\n tab.classList.toggle('tab-sheet-active', tab.getAttribute('data-tab-target') === tabId);\n });\n this._contentsEl?.querySelectorAll<HTMLElement>('.pfdjs-pp-content').forEach((content) => {\n content.classList.toggle('open', content.getAttribute('data-tab-target') === tabId);\n });\n }\n\n private _update(definition: Definition | undefined): void {\n if (this._tabsEl && this._contentsEl) {\n this._tabsEl.replaceChildren();\n this._contentsEl.replaceChildren();\n }\n if (definition) {\n this._drawEntries(definition);\n }\n }\n\n private _drawEntries(definition: Definition): void {\n if (!this._tabsEl || !this._contentsEl) {\n return;\n }\n const tabs = this._propertiesProvider.getTabs(definition);\n const renderedTabs: { id: string; label: string }[] = [];\n this._entries = {};\n\n for (const tab of tabs) {\n const content = fromHtml(`<div class=\"pfdjs-pp-content\" data-tab-target=\"${tab.id}\"></div>`);\n let tabHasContent = false;\n\n for (const group of tab.groups) {\n if (group.entries.length === 0) {\n continue;\n }\n const groupContent = fromHtml(\n `<div class=\"pfdjs-pp-content-group\" data-group-target=\"${group.id}\">` +\n `<div class=\"tab-content-group-title\">${group.label}</div></div>`\n );\n for (const entry of group.entries) {\n const entryNode = fromHtml(`<div>${entry.html}</div>`);\n groupContent.appendChild(entryNode);\n this._entries[entry.id] = { scope: entry, definition, formNode: entryNode };\n }\n content.appendChild(groupContent);\n tabHasContent = true;\n }\n\n if (tabHasContent) {\n this._contentsEl.appendChild(content);\n renderedTabs.push({ id: tab.id, label: tab.label });\n }\n }\n\n // populate each entry from the model\n Object.values(this._entries).forEach((entry) => entry.scope.get(entry.definition, entry.formNode));\n\n // draw the tab strip\n let firstTab: string | null = null;\n for (const tab of renderedTabs) {\n firstTab ??= tab.id;\n this._tabsEl.appendChild(\n fromHtml(\n `<li class=\"tab-sheet\" data-tab-target=\"${tab.id}\"><a href=\"#\">${tab.label}</a></li>`\n )\n );\n }\n this._selectTab(firstTab);\n }\n\n static readonly HTML_MARKUP =\n '<div id=\"pfdjs-pp-container\">' +\n '<div class=\"pfdjs-pp-tabs\"><ul class=\"tab-sheets\"></ul></div>' +\n '<div class=\"pfdjs-pp-contents\"></div>' +\n '</div>';\n}\n","/**\n * @d3-polytree/properties-panel — the editor's element-properties panel.\n *\n * Modernised (B6) from the 2017 jQuery/spectrum/scroll-tabs source to TypeScript\n * + ESM with native DOM: jQuery is gone, the colour entry uses a native\n * `<input type=\"color\">`, and the `scroll-tabs` widget is replaced by native\n * click-to-select tabs. Ships three didi modules (entry factory, PFDN provider,\n * panel) for the editor to compose.\n */\nexport { EntryFactory, entryFactoryModule } from './EntryFactory';\nexport type { EntryOptions, EntryResource, SelectOption } from './EntryFactory';\nexport {\n PfdnPropertiesProvider,\n pfdnPropertiesProviderModule\n} from './PfdnPropertiesProvider';\nexport type {\n IconMap,\n PropertiesGroup,\n PropertiesProvider,\n PropertiesTab\n} from './PfdnPropertiesProvider';\nexport { PropertiesPanel } from './PropertiesPanel';\nexport type { SideTabRegistration, SideTabsRegistrar } from './PropertiesPanel';\nexport type { Definition } from './utils';\n\nimport { PropertiesPanel } from './PropertiesPanel';\n\n/** didi module contributing the properties panel (composes with the provider). */\nexport const propertiesPanelModule = {\n __init__: ['propertiesPanel'],\n propertiesPanel: ['type', PropertiesPanel]\n};\n\nexport default propertiesPanelModule;\n"],"mappings":"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","names":[]}
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .pfdjs-container #pfdjs-pp-container{margin:10px 16px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-tabs{padding:0 15px;text-align:left}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-tabs .scroll-tabs-button{color:gray;cursor:pointer;font-size:16px;padding:3px 4px 3px 4px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-tabs .scroll-tabs-button:hover{font-weight:bold}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-tabs .scroll-tabs-button.scroll-tabs-left{float:left;margin-left:-15px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-tabs .scroll-tabs-button.scroll-tabs-right{float:right;margin-right:-15px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-tabs:not(.scroll-tabs-overflow) .scroll-tabs-button{display:none}.pfdjs-container #pfdjs-pp-container ul.tab-sheets{margin:-1px 0 5px 0;overflow:hidden;padding:0;white-space:nowrap}.pfdjs-container #pfdjs-pp-container ul.tab-sheets>li{display:inline-block;margin:0}.pfdjs-container #pfdjs-pp-container ul.tab-sheets>li.tab-sheet-ignore{display:none}.pfdjs-container #pfdjs-pp-container ul.tab-sheets>li>a{background-color:#fff;border:1px solid #d3d3d3;-ms-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;border-bottom:rgba(0,0,0,0);color:gray;display:inline-block;font-size:12px;padding:4px 7px;text-decoration:none}.pfdjs-container #pfdjs-pp-container ul.tab-sheets>li>a:hover{color:gray}.pfdjs-container #pfdjs-pp-container ul.tab-sheets>li+li{margin-left:1px}.pfdjs-container #pfdjs-pp-container ul.tab-sheets>li.tab-sheet-active a{border:1px solid gray;border-top:2px solid #5990bd;border-bottom:#fff;color:#333;padding-bottom:5px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents{margin-top:-6px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents .pfdjs-pp-content{display:none;border:1px solid gray;background-color:#fff;padding:6px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents .pfdjs-pp-content .pfdjs-pp-content-group{text-align:left;border-bottom:1px solid #d3d3d3;padding-bottom:6px;margin-bottom:6px;font-size:12px;font-weight:bold}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents .pfdjs-pp-content .pfdjs-pp-content-group .pfdjs-pp-field-wrapper{margin:4px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents .pfdjs-pp-content .pfdjs-pp-content-group label{margin-bottom:2px;margin-top:6px;font-size:inherit;display:block}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents .pfdjs-pp-content .pfdjs-pp-content-group input,.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents .pfdjs-pp-content .pfdjs-pp-content-group select{width:100%;font-size:11px}.pfdjs-container #pfdjs-pp-container .pfdjs-pp-contents .pfdjs-pp-content.open{display:block}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d3-polytree/editor",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Polytree editor (create and modify diagrams)",
5
5
  "license": "MIT",
6
6
  "author": "David Castillo",
@@ -14,17 +14,18 @@
14
14
  "import": "./dist/index.js",
15
15
  "require": "./dist/index.cjs"
16
16
  },
17
- "./umd": "./dist/editor.umd.js"
17
+ "./umd": "./dist/editor.umd.js",
18
+ "./style.css": "./dist/style.css"
18
19
  },
19
20
  "files": [
20
21
  "dist"
21
22
  ],
22
23
  "sideEffects": false,
23
24
  "dependencies": {
25
+ "eventemitter3": "^5.0.1",
24
26
  "@d3-polytree/viewer": "0.1.0",
25
27
  "@d3-polytree/core": "0.1.0",
26
- "@d3-polytree/interactive-viewer": "0.1.0",
27
- "@d3-polytree/properties-panel": "0.1.0"
28
+ "@d3-polytree/interactive-viewer": "0.2.0"
28
29
  },
29
30
  "unpkg": "./dist/editor.umd.js",
30
31
  "jsdelivr": "./dist/editor.umd.js",
@@ -38,7 +39,7 @@
38
39
  "access": "public"
39
40
  },
40
41
  "scripts": {
41
- "build": "tsup",
42
+ "build": "tsup && sass src/properties-panel/style/style.scss dist/style.css --no-source-map --style=compressed --silence-deprecation=import",
42
43
  "typecheck": "tsc --noEmit",
43
44
  "test": "vitest run"
44
45
  }