@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/dist/index.js CHANGED
@@ -10,17 +10,518 @@ 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, commandStack) {
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._commandStack = commandStack;
299
+ this._registerUpdatePropertiesCommand();
300
+ this._registerSideTab(sideTabsProvider);
301
+ this._registerSelectionListener();
302
+ }
303
+ /**
304
+ * Register the `element.updateProperties` command. Its apply logic is
305
+ * editor-specific (a property `scope` plus the provider's drawing update), so
306
+ * the panel — which owns both — registers it on the shared stack rather than
307
+ * the core modelling orchestrator.
308
+ */
309
+ _registerUpdatePropertiesCommand() {
310
+ const apply = (ctx, props) => {
311
+ ctx.scope.set(ctx.definition, props);
312
+ this._propertiesProvider.updateDrawing(ctx.definition);
313
+ };
314
+ this._commandStack.registerHandler("element.updateProperties", {
315
+ execute: (ctx) => apply(ctx, ctx.after),
316
+ revert: (ctx) => apply(ctx, ctx.before)
317
+ });
318
+ }
319
+ _registerSideTab(provider) {
320
+ provider.registerSideTab(
321
+ {
322
+ title: "Properties",
323
+ iconClassName: "icon-sliders",
324
+ action: { created: (content) => this._drawPanel(content) }
325
+ },
326
+ 1
327
+ );
328
+ }
329
+ _registerSelectionListener() {
330
+ this._eventBus.on(
331
+ "selection.changed",
332
+ (oldSelection, newSelection) => {
333
+ let selected = this._diagramSettings;
334
+ if (newSelection.length === 1 && (oldSelection.length !== 1 || oldSelection[0].definition.id !== newSelection[0].definition.id)) {
335
+ selected = newSelection[0].definition;
336
+ }
337
+ this._update(selected);
338
+ }
339
+ );
340
+ }
341
+ _drawPanel(content) {
342
+ if (!content) {
343
+ return;
344
+ }
345
+ this._drawContainer(content);
346
+ this._update(this._diagramSettings);
347
+ }
348
+ _drawContainer(content) {
349
+ this._container = fromHtml(_PropertiesPanel.HTML_MARKUP);
350
+ content.insertBefore(this._container, content.firstChild);
351
+ this._tabsEl = this._container.querySelector(".tab-sheets");
352
+ this._contentsEl = this._container.querySelector(".pfdjs-pp-contents");
353
+ this._container.addEventListener("click", (event) => {
354
+ const tab = event.target.closest(".tab-sheet");
355
+ if (tab) {
356
+ this._selectTab(tab.getAttribute("data-tab-target"));
357
+ event.stopImmediatePropagation();
358
+ }
359
+ });
360
+ this._registerInputChangeHandlers();
361
+ }
362
+ _registerInputChangeHandlers() {
363
+ const container = this._container;
364
+ if (!container) {
365
+ return;
366
+ }
367
+ const debouncedApply = debounce((target) => this._applyChange(target), 300);
368
+ container.addEventListener("input", (event) => {
369
+ const target = event.target;
370
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
371
+ debouncedApply(target);
372
+ }
373
+ });
374
+ container.addEventListener("change", (event) => {
375
+ const target = event.target;
376
+ if (FORM_CONTROLS.has(target.tagName)) {
377
+ this._applyChange(target);
378
+ }
379
+ });
380
+ this._eventBus.on(
381
+ "PropertiesPanel.propertyChanged",
382
+ (propertyId, definition) => this._applyChangeByProperty(propertyId, definition)
383
+ );
384
+ }
385
+ _applyChange(target) {
386
+ const entryId = target.getAttribute("name");
387
+ if (!entryId) {
388
+ return;
389
+ }
390
+ this._commit(entryId, target.value);
391
+ }
392
+ _applyChangeByProperty(propertyId, definition) {
393
+ this._commit(propertyId, deepGet(definition, propertyId));
394
+ }
395
+ _commit(entryId, newValue) {
396
+ const entry = this._entries[entryId];
397
+ if (!entry) {
398
+ return;
399
+ }
400
+ const after = {};
401
+ deepSet(after, entryId, newValue);
402
+ const before = {};
403
+ deepSet(before, entryId, deepGet(entry.definition, entryId));
404
+ this._commandStack.execute("element.updateProperties", {
405
+ scope: entry.scope,
406
+ definition: entry.definition,
407
+ before,
408
+ after
409
+ });
410
+ }
411
+ _selectTab(tabId) {
412
+ this._tabsEl?.querySelectorAll(".tab-sheet").forEach((tab) => {
413
+ tab.classList.toggle("tab-sheet-active", tab.getAttribute("data-tab-target") === tabId);
414
+ });
415
+ this._contentsEl?.querySelectorAll(".pfdjs-pp-content").forEach((content) => {
416
+ content.classList.toggle("open", content.getAttribute("data-tab-target") === tabId);
417
+ });
418
+ }
419
+ _update(definition) {
420
+ if (this._tabsEl && this._contentsEl) {
421
+ this._tabsEl.replaceChildren();
422
+ this._contentsEl.replaceChildren();
423
+ }
424
+ if (definition) {
425
+ this._drawEntries(definition);
426
+ }
427
+ }
428
+ _drawEntries(definition) {
429
+ if (!this._tabsEl || !this._contentsEl) {
430
+ return;
431
+ }
432
+ const tabs = this._propertiesProvider.getTabs(definition);
433
+ const renderedTabs = [];
434
+ this._entries = {};
435
+ for (const tab of tabs) {
436
+ const content = fromHtml(`<div class="pfdjs-pp-content" data-tab-target="${tab.id}"></div>`);
437
+ let tabHasContent = false;
438
+ for (const group of tab.groups) {
439
+ if (group.entries.length === 0) {
440
+ continue;
441
+ }
442
+ const groupContent = fromHtml(
443
+ `<div class="pfdjs-pp-content-group" data-group-target="${group.id}"><div class="tab-content-group-title">${group.label}</div></div>`
444
+ );
445
+ for (const entry of group.entries) {
446
+ const entryNode = fromHtml(`<div>${entry.html}</div>`);
447
+ groupContent.appendChild(entryNode);
448
+ this._entries[entry.id] = { scope: entry, definition, formNode: entryNode };
449
+ }
450
+ content.appendChild(groupContent);
451
+ tabHasContent = true;
452
+ }
453
+ if (tabHasContent) {
454
+ this._contentsEl.appendChild(content);
455
+ renderedTabs.push({ id: tab.id, label: tab.label });
456
+ }
457
+ }
458
+ Object.values(this._entries).forEach((entry) => entry.scope.get(entry.definition, entry.formNode));
459
+ let firstTab = null;
460
+ for (const tab of renderedTabs) {
461
+ firstTab ?? (firstTab = tab.id);
462
+ this._tabsEl.appendChild(
463
+ fromHtml(
464
+ `<li class="tab-sheet" data-tab-target="${tab.id}"><a href="#">${tab.label}</a></li>`
465
+ )
466
+ );
467
+ }
468
+ this._selectTab(firstTab);
469
+ }
470
+ };
471
+ _PropertiesPanel.$inject = [
472
+ "sideTabsProvider",
473
+ "eventBus",
474
+ "propertiesProvider",
475
+ "d3polytree.definitions.settings",
476
+ "commandStack"
477
+ ];
478
+ _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>';
479
+ var PropertiesPanel = _PropertiesPanel;
480
+
481
+ // src/properties-panel/index.ts
482
+ var propertiesPanelModule = {
483
+ __init__: ["propertiesPanel"],
484
+ propertiesPanel: ["type", PropertiesPanel]
485
+ };
486
+
487
+ // src/index.ts
18
488
  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
489
  var _Editor = class _Editor extends InteractiveViewer {
20
- constructor() {
21
- super(...arguments);
490
+ constructor(options = {}) {
491
+ super(options);
22
492
  /** The document a fresh editor opens with (used by {@link createDiagram}). */
23
493
  this.initialDiagram = INITIAL_DIAGRAM;
494
+ this._onKeydown = (event) => this._handleKeydown(event);
495
+ options.container?.addEventListener("keydown", this._onKeydown);
496
+ }
497
+ _handleKeydown(event) {
498
+ const target = event.target;
499
+ if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA")) {
500
+ return;
501
+ }
502
+ if (!this.getHost()) {
503
+ return;
504
+ }
505
+ const mod = event.ctrlKey || event.metaKey;
506
+ if (!mod) {
507
+ return;
508
+ }
509
+ const key = event.key.toLowerCase();
510
+ if (key === "z" && event.shiftKey) {
511
+ event.preventDefault();
512
+ this.redo();
513
+ } else if (key === "z") {
514
+ event.preventDefault();
515
+ this.undo();
516
+ } else if (key === "y") {
517
+ event.preventDefault();
518
+ this.redo();
519
+ }
520
+ }
521
+ /** Tear down the editor, removing the keyboard binding. */
522
+ destroy() {
523
+ this.options.container?.removeEventListener("keydown", this._onKeydown);
524
+ super.destroy();
24
525
  }
25
526
  /** (Re)open the initial diagram. */
26
527
  createDiagram() {
@@ -33,9 +534,16 @@ var _Editor = class _Editor extends InteractiveViewer {
33
534
  ...Viewer.modules
34
535
  ];
35
536
  }
36
- /** Create a node (and its associated label) at an optional position. */
537
+ /**
538
+ * Create a node (and its associated label) at an optional position.
539
+ *
540
+ * Routed through the command stack so it persists and is undoable (since B10
541
+ * the draw-layer `.created` event no longer persists on its own).
542
+ */
37
543
  createNode(parameters = {}) {
38
- return this.get("modellingNodes").create(parameters);
544
+ const ctx = { className: "node", parameters: [parameters] };
545
+ this.get("commandStack").execute("element.create", ctx);
546
+ return ctx.created;
39
547
  }
40
548
  /** Select an element by definition (e.g. to prepare a delete). */
41
549
  select(definition) {
@@ -48,6 +556,22 @@ var _Editor = class _Editor extends InteractiveViewer {
48
556
  deleteSelected() {
49
557
  this.get("selection").deleteSelected();
50
558
  }
559
+ /** Undo the last edit (a whole gesture is one step). No-op if nothing to undo. */
560
+ undo() {
561
+ this.get("commandStack").undo();
562
+ }
563
+ /** Redo the last undone edit. No-op if nothing to redo. */
564
+ redo() {
565
+ this.get("commandStack").redo();
566
+ }
567
+ /** Whether there is an edit to undo. */
568
+ canUndo() {
569
+ return this.get("commandStack").canUndo();
570
+ }
571
+ /** Whether there is an undone edit to redo. */
572
+ canRedo() {
573
+ return this.get("commandStack").canRedo();
574
+ }
51
575
  };
52
576
  /** Editing modules on top of the interaction layer. */
53
577
  _Editor.editionModules = [
@@ -68,6 +592,12 @@ var Editor = _Editor;
68
592
  var index_default = Editor;
69
593
  export {
70
594
  Editor,
71
- index_default as default
595
+ EntryFactory,
596
+ PfdnPropertiesProvider,
597
+ PropertiesPanel,
598
+ index_default as default,
599
+ entryFactoryModule,
600
+ pfdnPropertiesProviderModule,
601
+ propertiesPanelModule
72
602
  };
73
603
  //# sourceMappingURL=index.js.map