@d3-polytree/editor 0.2.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.cjs CHANGED
@@ -310,7 +310,7 @@ function fromHtml(html) {
310
310
  }
311
311
  var FORM_CONTROLS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
312
312
  var _PropertiesPanel = class _PropertiesPanel {
313
- constructor(sideTabsProvider, eventBus, propertiesProvider, diagramSettings) {
313
+ constructor(sideTabsProvider, eventBus, propertiesProvider, diagramSettings, commandStack) {
314
314
  this._entries = {};
315
315
  this._container = null;
316
316
  this._tabsEl = null;
@@ -318,9 +318,27 @@ var _PropertiesPanel = class _PropertiesPanel {
318
318
  this._eventBus = eventBus;
319
319
  this._propertiesProvider = propertiesProvider;
320
320
  this._diagramSettings = diagramSettings;
321
+ this._commandStack = commandStack;
322
+ this._registerUpdatePropertiesCommand();
321
323
  this._registerSideTab(sideTabsProvider);
322
324
  this._registerSelectionListener();
323
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
+ }
324
342
  _registerSideTab(provider) {
325
343
  provider.registerSideTab(
326
344
  {
@@ -402,10 +420,16 @@ var _PropertiesPanel = class _PropertiesPanel {
402
420
  if (!entry) {
403
421
  return;
404
422
  }
405
- const props = {};
406
- deepSet(props, entryId, newValue);
407
- entry.scope.set(entry.definition, props);
408
- this._propertiesProvider.updateDrawing(entry.definition);
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
+ });
409
433
  }
410
434
  _selectTab(tabId) {
411
435
  this._tabsEl?.querySelectorAll(".tab-sheet").forEach((tab) => {
@@ -471,7 +495,8 @@ _PropertiesPanel.$inject = [
471
495
  "sideTabsProvider",
472
496
  "eventBus",
473
497
  "propertiesProvider",
474
- "d3polytree.definitions.settings"
498
+ "d3polytree.definitions.settings",
499
+ "commandStack"
475
500
  ];
476
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>';
477
502
  var PropertiesPanel = _PropertiesPanel;
@@ -485,10 +510,41 @@ var propertiesPanelModule = {
485
510
  // src/index.ts
486
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>';
487
512
  var _Editor = class _Editor extends import_interactive_viewer.InteractiveViewer {
488
- constructor() {
489
- super(...arguments);
513
+ constructor(options = {}) {
514
+ super(options);
490
515
  /** The document a fresh editor opens with (used by {@link createDiagram}). */
491
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();
492
548
  }
493
549
  /** (Re)open the initial diagram. */
494
550
  createDiagram() {
@@ -501,9 +557,16 @@ var _Editor = class _Editor extends import_interactive_viewer.InteractiveViewer
501
557
  ...import_viewer.Viewer.modules
502
558
  ];
503
559
  }
504
- /** Create a node (and its associated label) at an optional position. */
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
+ */
505
566
  createNode(parameters = {}) {
506
- return this.get("modellingNodes").create(parameters);
567
+ const ctx = { className: "node", parameters: [parameters] };
568
+ this.get("commandStack").execute("element.create", ctx);
569
+ return ctx.created;
507
570
  }
508
571
  /** Select an element by definition (e.g. to prepare a delete). */
509
572
  select(definition) {
@@ -516,6 +579,22 @@ var _Editor = class _Editor extends import_interactive_viewer.InteractiveViewer
516
579
  deleteSelected() {
517
580
  this.get("selection").deleteSelected();
518
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
+ }
519
598
  };
520
599
  /** Editing modules on top of the interaction layer. */
521
600
  _Editor.editionModules = [
@@ -1 +1 @@
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":[]}
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 ModellingModelElement,\n type CreateParameters,\n type CommandStack,\n type CreateContext,\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 private readonly _onKeydown = (event: KeyboardEvent): void => this._handleKeydown(event);\n\n constructor(options: EditorOptions = {}) {\n super(options);\n // Keyboard undo/redo. Scoped to the editor container (greenfield — the\n // components had no keyboard handling); removed in destroy().\n options.container?.addEventListener('keydown', this._onKeydown);\n }\n\n private _handleKeydown(event: KeyboardEvent): void {\n // Never steal typing from a panel field.\n const target = event.target as HTMLElement | null;\n if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA')) {\n return;\n }\n // Undo/redo only make sense once a document is open.\n if (!this.getHost()) {\n return;\n }\n const mod = event.ctrlKey || event.metaKey;\n if (!mod) {\n return;\n }\n const key = event.key.toLowerCase();\n if (key === 'z' && event.shiftKey) {\n event.preventDefault();\n this.redo();\n } else if (key === 'z') {\n event.preventDefault();\n this.undo();\n } else if (key === 'y') {\n event.preventDefault();\n this.redo();\n }\n }\n\n /** Tear down the editor, removing the keyboard binding. */\n destroy(): void {\n this.options.container?.removeEventListener('keydown', this._onKeydown);\n super.destroy();\n }\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 /**\n * Create a node (and its associated label) at an optional position.\n *\n * Routed through the command stack so it persists and is undoable (since B10\n * the draw-layer `.created` event no longer persists on its own).\n */\n createNode(parameters: CreateParameters = {}): ModellingModelElement {\n const ctx: CreateContext = { className: 'node', parameters: [parameters] };\n this.get<CommandStack>('commandStack').execute('element.create', ctx);\n return ctx.created as ModellingModelElement;\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 /** Undo the last edit (a whole gesture is one step). No-op if nothing to undo. */\n undo(): void {\n this.get<CommandStack>('commandStack').undo();\n }\n\n /** Redo the last undone edit. No-op if nothing to redo. */\n redo(): void {\n this.get<CommandStack>('commandStack').redo();\n }\n\n /** Whether there is an edit to undo. */\n canUndo(): boolean {\n return this.get<CommandStack>('commandStack').canUndo();\n }\n\n /** Whether there is an undone edit to redo. */\n canRedo(): boolean {\n return this.get<CommandStack>('commandStack').canRedo();\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 type { DiagramEventMap } from '@d3-polytree/core';\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<DiagramEventMap>;\n\n constructor(eventBus: EventEmitter<DiagramEventMap>) {\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 { DiagramEventMap } from '@d3-polytree/core';\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<DiagramEventMap>;\n\n constructor(icons: IconMap, entryFactory: EntryFactory, eventBus: EventEmitter<DiagramEventMap>) {\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 { DiagramEventMap } from '@d3-polytree/core';\nimport type { CommandStack, CommandContext } from '@d3-polytree/core';\nimport type { EntryResource } from './EntryFactory';\nimport type { PropertiesProvider } from './PfdnPropertiesProvider';\nimport { debounce, deepGet, deepSet, type Definition } from './utils';\n\n/** The memento for an `element.updateProperties` command. */\ninterface UpdatePropsContext extends CommandContext {\n scope: EntryResource;\n definition: Definition;\n before: Record<string, unknown>;\n after: Record<string, unknown>;\n}\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 'commandStack'\n ];\n\n private readonly _eventBus: EventEmitter<DiagramEventMap>;\n private readonly _propertiesProvider: PropertiesProvider;\n private readonly _diagramSettings: Definition;\n private readonly _commandStack: CommandStack;\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<DiagramEventMap>,\n propertiesProvider: PropertiesProvider,\n diagramSettings: Definition,\n commandStack: CommandStack\n ) {\n this._eventBus = eventBus;\n this._propertiesProvider = propertiesProvider;\n this._diagramSettings = diagramSettings;\n this._commandStack = commandStack;\n this._registerUpdatePropertiesCommand();\n this._registerSideTab(sideTabsProvider);\n this._registerSelectionListener();\n }\n\n /**\n * Register the `element.updateProperties` command. Its apply logic is\n * editor-specific (a property `scope` plus the provider's drawing update), so\n * the panel — which owns both — registers it on the shared stack rather than\n * the core modelling orchestrator.\n */\n private _registerUpdatePropertiesCommand(): void {\n const apply = (ctx: UpdatePropsContext, props: Record<string, unknown>): void => {\n ctx.scope.set(ctx.definition, props);\n this._propertiesProvider.updateDrawing(ctx.definition);\n };\n this._commandStack.registerHandler('element.updateProperties', {\n execute: (ctx) => apply(ctx as UpdatePropsContext, (ctx as UpdatePropsContext).after),\n revert: (ctx) => apply(ctx as UpdatePropsContext, (ctx as UpdatePropsContext).before)\n });\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 after: Record<string, unknown> = {};\n deepSet(after, entryId, newValue);\n // Capture the prior value of the same path so the edit is undoable.\n const before: Record<string, unknown> = {};\n deepSet(before, entryId, deepGet(entry.definition, entryId));\n this._commandStack.execute('element.updateProperties', {\n scope: entry.scope,\n definition: entry.definition,\n before,\n after\n } satisfies UpdatePropsContext);\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/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { InteractiveViewer, InteractiveViewerOptions } from '@d3-polytree/interactive-viewer';
2
- import { DiagramModule, CreateParameters, ModellingModelElement } from '@d3-polytree/core';
2
+ import { DiagramEventMap, CommandStack, DiagramModule, CreateParameters, ModellingModelElement } from '@d3-polytree/core';
3
3
  import EventEmitter from 'eventemitter3';
4
4
 
5
5
  /** A moddle model element as read/written by the properties panel. */
@@ -43,7 +43,7 @@ interface EntryResource extends EntryOptions {
43
43
  declare class EntryFactory {
44
44
  static readonly $inject: string[];
45
45
  private readonly _eventBus;
46
- constructor(eventBus: EventEmitter);
46
+ constructor(eventBus: EventEmitter<DiagramEventMap>);
47
47
  setDefaultParameters(options: EntryOptions): EntryResource;
48
48
  textField(options: EntryOptions): EntryResource;
49
49
  selectBox(options: EntryOptions): EntryResource;
@@ -85,7 +85,7 @@ declare class PfdnPropertiesProvider implements PropertiesProvider {
85
85
  private readonly _icons;
86
86
  private readonly _entryFactory;
87
87
  private readonly _eventBus;
88
- constructor(icons: IconMap, entryFactory: EntryFactory, eventBus: EventEmitter);
88
+ constructor(icons: IconMap, entryFactory: EntryFactory, eventBus: EventEmitter<DiagramEventMap>);
89
89
  updateDrawing(definition: Definition): void;
90
90
  getTabs(element: Definition): PropertiesTab[];
91
91
  private _propertiesTab;
@@ -120,11 +120,19 @@ declare class PropertiesPanel {
120
120
  private readonly _eventBus;
121
121
  private readonly _propertiesProvider;
122
122
  private readonly _diagramSettings;
123
+ private readonly _commandStack;
123
124
  private _entries;
124
125
  private _container;
125
126
  private _tabsEl;
126
127
  private _contentsEl;
127
- constructor(sideTabsProvider: SideTabsRegistrar, eventBus: EventEmitter, propertiesProvider: PropertiesProvider, diagramSettings: Definition);
128
+ constructor(sideTabsProvider: SideTabsRegistrar, eventBus: EventEmitter<DiagramEventMap>, propertiesProvider: PropertiesProvider, diagramSettings: Definition, commandStack: CommandStack);
129
+ /**
130
+ * Register the `element.updateProperties` command. Its apply logic is
131
+ * editor-specific (a property `scope` plus the provider's drawing update), so
132
+ * the panel — which owns both — registers it on the shared stack rather than
133
+ * the core modelling orchestrator.
134
+ */
135
+ private _registerUpdatePropertiesCommand;
128
136
  private _registerSideTab;
129
137
  private _registerSelectionListener;
130
138
  private _drawPanel;
@@ -169,15 +177,33 @@ declare class Editor extends InteractiveViewer {
169
177
  static readonly editionModules: readonly DiagramModule[];
170
178
  /** The document a fresh editor opens with (used by {@link createDiagram}). */
171
179
  initialDiagram: string;
180
+ private readonly _onKeydown;
181
+ constructor(options?: EditorOptions);
182
+ private _handleKeydown;
183
+ /** Tear down the editor, removing the keyboard binding. */
184
+ destroy(): void;
172
185
  /** (Re)open the initial diagram. */
173
186
  createDiagram(): Promise<void>;
174
187
  getModules(): readonly DiagramModule[];
175
- /** Create a node (and its associated label) at an optional position. */
188
+ /**
189
+ * Create a node (and its associated label) at an optional position.
190
+ *
191
+ * Routed through the command stack so it persists and is undoable (since B10
192
+ * the draw-layer `.created` event no longer persists on its own).
193
+ */
176
194
  createNode(parameters?: CreateParameters): ModellingModelElement;
177
195
  /** Select an element by definition (e.g. to prepare a delete). */
178
196
  select(definition: ModellingModelElement): void;
179
197
  /** Delete the current selection (cascading to associated labels). */
180
198
  deleteSelected(): void;
199
+ /** Undo the last edit (a whole gesture is one step). No-op if nothing to undo. */
200
+ undo(): void;
201
+ /** Redo the last undone edit. No-op if nothing to redo. */
202
+ redo(): void;
203
+ /** Whether there is an edit to undo. */
204
+ canUndo(): boolean;
205
+ /** Whether there is an undone edit to redo. */
206
+ canRedo(): boolean;
181
207
  }
182
208
 
183
209
  export { type Definition, Editor, type EditorOptions, EntryFactory, type EntryOptions, type EntryResource, type IconMap, PfdnPropertiesProvider, type PropertiesGroup, PropertiesPanel, type PropertiesProvider, type PropertiesTab, type SelectOption, type SideTabRegistration, type SideTabsRegistrar, Editor as default, entryFactoryModule, pfdnPropertiesProviderModule, propertiesPanelModule };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { InteractiveViewer, InteractiveViewerOptions } from '@d3-polytree/interactive-viewer';
2
- import { DiagramModule, CreateParameters, ModellingModelElement } from '@d3-polytree/core';
2
+ import { DiagramEventMap, CommandStack, DiagramModule, CreateParameters, ModellingModelElement } from '@d3-polytree/core';
3
3
  import EventEmitter from 'eventemitter3';
4
4
 
5
5
  /** A moddle model element as read/written by the properties panel. */
@@ -43,7 +43,7 @@ interface EntryResource extends EntryOptions {
43
43
  declare class EntryFactory {
44
44
  static readonly $inject: string[];
45
45
  private readonly _eventBus;
46
- constructor(eventBus: EventEmitter);
46
+ constructor(eventBus: EventEmitter<DiagramEventMap>);
47
47
  setDefaultParameters(options: EntryOptions): EntryResource;
48
48
  textField(options: EntryOptions): EntryResource;
49
49
  selectBox(options: EntryOptions): EntryResource;
@@ -85,7 +85,7 @@ declare class PfdnPropertiesProvider implements PropertiesProvider {
85
85
  private readonly _icons;
86
86
  private readonly _entryFactory;
87
87
  private readonly _eventBus;
88
- constructor(icons: IconMap, entryFactory: EntryFactory, eventBus: EventEmitter);
88
+ constructor(icons: IconMap, entryFactory: EntryFactory, eventBus: EventEmitter<DiagramEventMap>);
89
89
  updateDrawing(definition: Definition): void;
90
90
  getTabs(element: Definition): PropertiesTab[];
91
91
  private _propertiesTab;
@@ -120,11 +120,19 @@ declare class PropertiesPanel {
120
120
  private readonly _eventBus;
121
121
  private readonly _propertiesProvider;
122
122
  private readonly _diagramSettings;
123
+ private readonly _commandStack;
123
124
  private _entries;
124
125
  private _container;
125
126
  private _tabsEl;
126
127
  private _contentsEl;
127
- constructor(sideTabsProvider: SideTabsRegistrar, eventBus: EventEmitter, propertiesProvider: PropertiesProvider, diagramSettings: Definition);
128
+ constructor(sideTabsProvider: SideTabsRegistrar, eventBus: EventEmitter<DiagramEventMap>, propertiesProvider: PropertiesProvider, diagramSettings: Definition, commandStack: CommandStack);
129
+ /**
130
+ * Register the `element.updateProperties` command. Its apply logic is
131
+ * editor-specific (a property `scope` plus the provider's drawing update), so
132
+ * the panel — which owns both — registers it on the shared stack rather than
133
+ * the core modelling orchestrator.
134
+ */
135
+ private _registerUpdatePropertiesCommand;
128
136
  private _registerSideTab;
129
137
  private _registerSelectionListener;
130
138
  private _drawPanel;
@@ -169,15 +177,33 @@ declare class Editor extends InteractiveViewer {
169
177
  static readonly editionModules: readonly DiagramModule[];
170
178
  /** The document a fresh editor opens with (used by {@link createDiagram}). */
171
179
  initialDiagram: string;
180
+ private readonly _onKeydown;
181
+ constructor(options?: EditorOptions);
182
+ private _handleKeydown;
183
+ /** Tear down the editor, removing the keyboard binding. */
184
+ destroy(): void;
172
185
  /** (Re)open the initial diagram. */
173
186
  createDiagram(): Promise<void>;
174
187
  getModules(): readonly DiagramModule[];
175
- /** Create a node (and its associated label) at an optional position. */
188
+ /**
189
+ * Create a node (and its associated label) at an optional position.
190
+ *
191
+ * Routed through the command stack so it persists and is undoable (since B10
192
+ * the draw-layer `.created` event no longer persists on its own).
193
+ */
176
194
  createNode(parameters?: CreateParameters): ModellingModelElement;
177
195
  /** Select an element by definition (e.g. to prepare a delete). */
178
196
  select(definition: ModellingModelElement): void;
179
197
  /** Delete the current selection (cascading to associated labels). */
180
198
  deleteSelected(): void;
199
+ /** Undo the last edit (a whole gesture is one step). No-op if nothing to undo. */
200
+ undo(): void;
201
+ /** Redo the last undone edit. No-op if nothing to redo. */
202
+ redo(): void;
203
+ /** Whether there is an edit to undo. */
204
+ canUndo(): boolean;
205
+ /** Whether there is an undone edit to redo. */
206
+ canRedo(): boolean;
181
207
  }
182
208
 
183
209
  export { type Definition, Editor, type EditorOptions, EntryFactory, type EntryOptions, type EntryResource, type IconMap, PfdnPropertiesProvider, type PropertiesGroup, PropertiesPanel, type PropertiesProvider, type PropertiesTab, type SelectOption, type SideTabRegistration, type SideTabsRegistrar, Editor as default, entryFactoryModule, pfdnPropertiesProviderModule, propertiesPanelModule };