graphwise-reactodia 0.0.1-TR11 → 0.0.1-TR12

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.
@@ -23868,6 +23868,35 @@ function ToolbarAction(props) {
23868
23868
  });
23869
23869
  }
23870
23870
  /**
23871
+ * Toolbar action component to open a file selection dialog.
23872
+ *
23873
+ * @category Components
23874
+ */
23875
+ function ToolbarActionOpen(props) {
23876
+ const { className, fileAccept, onSelect, children, ...otherProps } = props;
23877
+ const inputRef = reactExports.useRef(null);
23878
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [/* @__PURE__ */ jsxRuntimeExports.jsx(ToolbarAction, {
23879
+ ...otherProps,
23880
+ className: clsx(className, `${CLASS_NAME$8}__open`),
23881
+ onSelect: () => {
23882
+ inputRef.current?.click();
23883
+ },
23884
+ children
23885
+ }), /* @__PURE__ */ jsxRuntimeExports.jsx("input", {
23886
+ ref: inputRef,
23887
+ role: "none",
23888
+ type: "file",
23889
+ className: `${CLASS_NAME$8}__open-input`,
23890
+ accept: fileAccept,
23891
+ onChange: (e) => {
23892
+ if (e.currentTarget.files) {
23893
+ const file = e.currentTarget.files[0];
23894
+ onSelect(file);
23895
+ }
23896
+ }
23897
+ })] });
23898
+ }
23899
+ /**
23871
23900
  * Toolbar action component to clear diagram content.
23872
23901
  *
23873
23902
  * Clearing the diagram adds a command to the command history.
@@ -30846,6 +30875,16 @@ function getContentFittingBoxForLayout(state) {
30846
30875
  };
30847
30876
  }
30848
30877
 
30878
+ const graphwise_toolbar_action$1 = {
30879
+ "download_diagram.label": "Export to JSON",
30880
+ "download_diagram.title": "Export the diagram to a JSON file",
30881
+ "upload_diagram.label": "Import from JSON",
30882
+ "upload_diagram.title": "Replace the diagram with one from a JSON file"
30883
+ };
30884
+ var enTranslation = {
30885
+ graphwise_toolbar_action: graphwise_toolbar_action$1
30886
+ };
30887
+
30849
30888
  const authoring_state = {
30850
30889
  "entity_action_delete.label": "supprimer",
30851
30890
  "entity_action_delete.title": "Supprimer l'entité",
@@ -31100,6 +31139,12 @@ const standard_link = {
31100
31139
  const toolbar = {
31101
31140
  "menu_toggle.title": "Ouvrir le menu"
31102
31141
  };
31142
+ const graphwise_toolbar_action = {
31143
+ "download_diagram.label": "Exporter en JSON",
31144
+ "download_diagram.title": "Exporter le diagramme en fichier JSON",
31145
+ "upload_diagram.label": "Importer depuis JSON",
31146
+ "upload_diagram.title": "Remplacer le diagramme par celui d'un fichier JSON"
31147
+ };
31103
31148
  const toolbar_action = {
31104
31149
  "clear_all.command": "Tout effacer",
31105
31150
  "clear_all.title": "Supprimer tous les éléments et liens du diagramme",
@@ -31210,6 +31255,7 @@ var frTranslation = {
31210
31255
  standard_element: standard_element,
31211
31256
  standard_link: standard_link,
31212
31257
  toolbar: toolbar,
31258
+ graphwise_toolbar_action: graphwise_toolbar_action,
31213
31259
  toolbar_action: toolbar_action,
31214
31260
  unified_search: unified_search,
31215
31261
  visual_authoring: visual_authoring,
@@ -31224,8 +31270,10 @@ var LanguageKey;
31224
31270
  LanguageKey["FR"] = "fr";
31225
31271
  })(LanguageKey || (LanguageKey = {}));
31226
31272
 
31227
- // English is provided by default from reactodia so we don't need to include it here, unless we override it
31273
+ // English is provided by default from reactodia, so the English bundle holds only our own keys and overrides. If
31274
+ // A key is missing, reactodia defaults to its own translation.
31228
31275
  const TRANSLATIONS = {
31276
+ [LanguageKey.EN]: enTranslation,
31229
31277
  [LanguageKey.FR]: frTranslation
31230
31278
  };
31231
31279
 
@@ -31285,7 +31333,7 @@ function Service(_serviceClass) {
31285
31333
  // no implementation needed. We only use the type checking of the parameter
31286
31334
  }
31287
31335
 
31288
- var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31336
+ var __decorate$4 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31289
31337
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31290
31338
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31291
31339
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -31296,6 +31344,20 @@ var DiagramService_1;
31296
31344
  * Service for diagram related operations and subscriptions
31297
31345
  */
31298
31346
  let DiagramService = DiagramService_1 = class DiagramService {
31347
+ /**
31348
+ * Replaces the diagram content with the given serialization. Element data is requested from the data provider and
31349
+ * the links between the imported elements are validated against it.
31350
+ *
31351
+ * @param model The diagram model to import into.
31352
+ * @param dataProvider The data provider to bind to the diagram.
31353
+ * @param diagram The serialization to import. When `undefined`, the diagram is cleared.
31354
+ * @param signal Optional signal to abort the import.
31355
+ */
31356
+ importDiagram(model, dataProvider, diagram, signal) {
31357
+ // selection is not cleared on import, so we clear it manually to avoid painting the previous selection
31358
+ model.setSelection([]);
31359
+ return model.importLayout({ dataProvider, diagram, signal, validateLinks: true });
31360
+ }
31299
31361
  /**
31300
31362
  * Subscribes to diagram changes. On every debounced change the current layout is
31301
31363
  * exported and passed to the callback.
@@ -31330,11 +31392,11 @@ let DiagramService = DiagramService_1 = class DiagramService {
31330
31392
  DiagramService.ID = 'DiagramService';
31331
31393
  DiagramService.NOTIFY_DEBOUNCE_MS = 300;
31332
31394
  DiagramService.HISTORY_CHANGED_EVENT = 'historyChanged';
31333
- DiagramService = DiagramService_1 = __decorate$3([
31395
+ DiagramService = DiagramService_1 = __decorate$4([
31334
31396
  Service
31335
31397
  ], DiagramService);
31336
31398
 
31337
- var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31399
+ var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31338
31400
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31339
31401
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31340
31402
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -31349,11 +31411,11 @@ let WindowService = class WindowService {
31349
31411
  }
31350
31412
  };
31351
31413
  WindowService.ID = 'WindowService';
31352
- WindowService = __decorate$2([
31414
+ WindowService = __decorate$3([
31353
31415
  Service
31354
31416
  ], WindowService);
31355
31417
 
31356
- var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31418
+ var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31357
31419
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31358
31420
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31359
31421
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -31397,11 +31459,11 @@ let DiagramStorageService = DiagramStorageService_1 = class DiagramStorageServic
31397
31459
  };
31398
31460
  DiagramStorageService.ID = 'DiagramStorageService';
31399
31461
  DiagramStorageService.STORAGE_KEY = 'diagram.state';
31400
- DiagramStorageService = DiagramStorageService_1 = __decorate$1([
31462
+ DiagramStorageService = DiagramStorageService_1 = __decorate$2([
31401
31463
  Service
31402
31464
  ], DiagramStorageService);
31403
31465
 
31404
- var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31466
+ var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31405
31467
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31406
31468
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31407
31469
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -31424,7 +31486,7 @@ let EventService = class EventService {
31424
31486
  }
31425
31487
  };
31426
31488
  EventService.ID = 'EventService';
31427
- EventService = __decorate([
31489
+ EventService = __decorate$1([
31428
31490
  Service
31429
31491
  ], EventService);
31430
31492
 
@@ -31518,8 +31580,102 @@ const resetColors = () => {
31518
31580
  typeToIndex.clear();
31519
31581
  };
31520
31582
 
31583
+ var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31584
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31585
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31586
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
31587
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
31588
+ };
31589
+ var DiagramFileService_1;
31590
+ /**
31591
+ * Service for downloading and uploading the diagram as a file. The file content is the same
31592
+ * {@link SerializedDiagram} that is persisted to the local store.
31593
+ */
31594
+ let DiagramFileService = DiagramFileService_1 = class DiagramFileService {
31595
+ /**
31596
+ * Downloads the given diagram serialization as a file with the given name.
31597
+ */
31598
+ download(diagram, fileName) {
31599
+ const window = service(WindowService).getWindow();
31600
+ const blob = new Blob([JSON.stringify(diagram)], { type: DiagramFileService_1.MIME_TYPE });
31601
+ const url = window.URL.createObjectURL(blob);
31602
+ const link = window.document.createElement('a');
31603
+ link.href = url;
31604
+ link.download = fileName;
31605
+ link.click();
31606
+ window.URL.revokeObjectURL(url);
31607
+ }
31608
+ /**
31609
+ * Reads a diagram serialization from the given file.
31610
+ *
31611
+ * @throws Error when the file is not valid JSON or is not a serialized diagram.
31612
+ */
31613
+ async read(file) {
31614
+ let diagram;
31615
+ try {
31616
+ diagram = JSON.parse(await file.text());
31617
+ }
31618
+ catch (error) {
31619
+ throw new Error(`File "${file.name}" is not valid JSON: ${error.message}`);
31620
+ }
31621
+ if (!this.isSerializedDiagram(diagram)) {
31622
+ throw new Error(`File "${file.name}" is not a serialized diagram`);
31623
+ }
31624
+ return diagram;
31625
+ }
31626
+ /**
31627
+ * Checks if the provided value is a serialized diagram
31628
+ */
31629
+ isSerializedDiagram(value) {
31630
+ var _a, _b;
31631
+ const diagram = value;
31632
+ return (diagram === null || diagram === void 0 ? void 0 : diagram['@type']) === 'Diagram'
31633
+ && Array.isArray((_a = diagram.layoutData) === null || _a === void 0 ? void 0 : _a.elements)
31634
+ && Array.isArray((_b = diagram.layoutData) === null || _b === void 0 ? void 0 : _b.links);
31635
+ }
31636
+ };
31637
+ DiagramFileService.ID = 'DiagramFileService';
31638
+ DiagramFileService.FILE_EXTENSION = '.json';
31639
+ DiagramFileService.MIME_TYPE = 'application/json';
31640
+ DiagramFileService = DiagramFileService_1 = __decorate([
31641
+ Service
31642
+ ], DiagramFileService);
31643
+
31644
+ /**
31645
+ * The workspace main menu: Reactodia's default actions, followed by the diagram download and upload.
31646
+ *
31647
+ * Passing a `menu` to the `DefaultWorkspace` replaces its default actions, so they are listed here again.
31648
+ * Rendered inside the workspace, so the actions can use the workspace hooks.
31649
+ */
31650
+ function GraphwiseMenu() {
31651
+ const { model } = useWorkspace();
31652
+ const t = useTranslation();
31653
+ const diagramFileService = service(DiagramFileService);
31654
+ const diagramService = service(DiagramService);
31655
+ const downloadDiagram = () => {
31656
+ const timestamp = new Date().toISOString().replace(/[-:]|\.\d+Z$/g, '');
31657
+ diagramFileService.download(model.exportLayout(), `diagram-${timestamp}${DiagramFileService.FILE_EXTENSION}`);
31658
+ };
31659
+ const uploadDiagram = async (file) => {
31660
+ try {
31661
+ const diagram = await diagramFileService.read(file);
31662
+ await diagramService.importDiagram(model, model.dataProvider, diagram);
31663
+ }
31664
+ catch (error) {
31665
+ console.error('Failed to upload the diagram', error);
31666
+ }
31667
+ };
31668
+ return reactExports.createElement(reactExports.Fragment, null, reactExports.createElement(ToolbarActionClearAll, {}), reactExports.createElement(ToolbarActionExport, { kind: 'exportRaster' }), reactExports.createElement(ToolbarActionExport, { kind: 'exportSvg' }), reactExports.createElement(ToolbarActionExport, { kind: 'print' }), reactExports.createElement(ToolbarAction,
31669
+ // The same icon as ToolbarActionSave. We dont use that action, since it disables saving on refresh
31670
+ { className: 'reactodia-toolbar-action__save', title: t.text('graphwise_toolbar_action.download_diagram.title'), onSelect: downloadDiagram }, t.text('graphwise_toolbar_action.download_diagram.label')), reactExports.createElement(ToolbarActionOpen, {
31671
+ title: t.text('graphwise_toolbar_action.upload_diagram.title'),
31672
+ fileAccept: DiagramFileService.FILE_EXTENSION,
31673
+ onSelect: (file) => void uploadDiagram(file)
31674
+ }, t.text('graphwise_toolbar_action.upload_diagram.label')));
31675
+ }
31676
+
31521
31677
  /**
31522
- * Reactodia ships English as its built-in default bundle, so English needs no override.
31678
+ * Reactodia ships English as its built-in default bundle, so the English bundle only adds our own keys.
31523
31679
  * Other languages are layered on top as partial bundles; unknown codes fall back to English.
31524
31680
  *
31525
31681
  * Reactodia never re-reads the translations, so the component must be remounted
@@ -31637,10 +31793,11 @@ function ReactodiaApp(props) {
31637
31793
  const dataProvider = createDataProvider(props);
31638
31794
  const isReload = props.isReload;
31639
31795
  const savedDiagram = service(DiagramStorageService).load();
31796
+ const diagramService = service(DiagramService);
31640
31797
  if (isReload) {
31641
31798
  // Simply reload without using the current state. This may happen when the user switches the language at runtime.
31642
31799
  // since there is no existing mechanism to re-translate the UI at runtime (not for the UI labels at least)
31643
- await model.importLayout({ dataProvider, diagram: currentDiagram, signal, validateLinks: true });
31800
+ await diagramService.importDiagram(model, dataProvider, currentDiagram, signal);
31644
31801
  }
31645
31802
  else if ((_a = config.seedIris) === null || _a === void 0 ? void 0 : _a.length) {
31646
31803
  await seedIrisToCanvas(context, dataProvider, config.seedIris, signal);
@@ -31651,7 +31808,7 @@ function ReactodiaApp(props) {
31651
31808
  else {
31652
31809
  // Restore the previously saved layout when present, otherwise start empty. Either way this binds the
31653
31810
  // data provider (savedDiagram is undefined -> empty diagram), so the unified search/lookup works.
31654
- await model.importLayout({ dataProvider, diagram: savedDiagram, signal, validateLinks: true });
31811
+ await diagramService.importDiagram(model, dataProvider, savedDiagram, signal);
31655
31812
  }
31656
31813
  }, [language]);
31657
31814
  /**
@@ -31670,7 +31827,7 @@ function ReactodiaApp(props) {
31670
31827
  defaultLanguage: language,
31671
31828
  translations: translationsForLanguage(language),
31672
31829
  typeStyleResolver: resolveTypeStyle
31673
- }, reactExports.createElement(DefaultWorkspace, {}));
31830
+ }, reactExports.createElement(DefaultWorkspace, { menu: reactExports.createElement(GraphwiseMenu) }));
31674
31831
  }
31675
31832
  /**
31676
31833
  * Mounts the Reactodia application into the given container and returns the React root.
@@ -1,6 +1,9 @@
1
+ import enTranslation from "./en.reactodia.translation.json";
1
2
  import frTranslation from "./fr.reactodia.translation.json";
2
3
  import { LanguageKey } from "./language-key";
3
- // English is provided by default from reactodia so we don't need to include it here, unless we override it
4
+ // English is provided by default from reactodia, so the English bundle holds only our own keys and overrides. If
5
+ // A key is missing, reactodia defaults to its own translation.
4
6
  export const TRANSLATIONS = {
7
+ [LanguageKey.EN]: enTranslation,
5
8
  [LanguageKey.FR]: frTranslation
6
9
  };
@@ -0,0 +1,37 @@
1
+ import { createElement, Fragment } from "react";
2
+ import { ToolbarAction, ToolbarActionClearAll, ToolbarActionExport, ToolbarActionOpen, useTranslation, useWorkspace } from "@reactodia/workspace";
3
+ import { service } from "../providers/service/service-inject";
4
+ import { DiagramService } from "../services/diagram/diagram.service";
5
+ import { DiagramFileService } from "../services/diagram-file/diagram-file.service";
6
+ /**
7
+ * The workspace main menu: Reactodia's default actions, followed by the diagram download and upload.
8
+ *
9
+ * Passing a `menu` to the `DefaultWorkspace` replaces its default actions, so they are listed here again.
10
+ * Rendered inside the workspace, so the actions can use the workspace hooks.
11
+ */
12
+ export function GraphwiseMenu() {
13
+ const { model } = useWorkspace();
14
+ const t = useTranslation();
15
+ const diagramFileService = service(DiagramFileService);
16
+ const diagramService = service(DiagramService);
17
+ const downloadDiagram = () => {
18
+ const timestamp = new Date().toISOString().replace(/[-:]|\.\d+Z$/g, '');
19
+ diagramFileService.download(model.exportLayout(), `diagram-${timestamp}${DiagramFileService.FILE_EXTENSION}`);
20
+ };
21
+ const uploadDiagram = async (file) => {
22
+ try {
23
+ const diagram = await diagramFileService.read(file);
24
+ await diagramService.importDiagram(model, model.dataProvider, diagram);
25
+ }
26
+ catch (error) {
27
+ console.error('Failed to upload the diagram', error);
28
+ }
29
+ };
30
+ return createElement(Fragment, null, createElement(ToolbarActionClearAll, {}), createElement(ToolbarActionExport, { kind: 'exportRaster' }), createElement(ToolbarActionExport, { kind: 'exportSvg' }), createElement(ToolbarActionExport, { kind: 'print' }), createElement(ToolbarAction,
31
+ // The same icon as ToolbarActionSave. We dont use that action, since it disables saving on refresh
32
+ { className: 'reactodia-toolbar-action__save', title: t.text('graphwise_toolbar_action.download_diagram.title'), onSelect: downloadDiagram }, t.text('graphwise_toolbar_action.download_diagram.label')), createElement(ToolbarActionOpen, {
33
+ title: t.text('graphwise_toolbar_action.upload_diagram.title'),
34
+ fileAccept: DiagramFileService.FILE_EXTENSION,
35
+ onSelect: (file) => void uploadDiagram(file)
36
+ }, t.text('graphwise_toolbar_action.upload_diagram.label')));
37
+ }
@@ -9,8 +9,9 @@ import { DiagramStorageService } from "./services/diagram-storage/diagram-storag
9
9
  import { EventService } from "./services/event/event.service";
10
10
  import { SubscriptionList } from "./models/subscription-list";
11
11
  import { resolveTypeStyle } from "./styles/type-style.resolver";
12
+ import { GraphwiseMenu } from "./menu/graphwise-menu";
12
13
  /**
13
- * Reactodia ships English as its built-in default bundle, so English needs no override.
14
+ * Reactodia ships English as its built-in default bundle, so the English bundle only adds our own keys.
14
15
  * Other languages are layered on top as partial bundles; unknown codes fall back to English.
15
16
  *
16
17
  * Reactodia never re-reads the translations, so the component must be remounted
@@ -128,10 +129,11 @@ function ReactodiaApp(props) {
128
129
  const dataProvider = createDataProvider(props);
129
130
  const isReload = props.isReload;
130
131
  const savedDiagram = service(DiagramStorageService).load();
132
+ const diagramService = service(DiagramService);
131
133
  if (isReload) {
132
134
  // Simply reload without using the current state. This may happen when the user switches the language at runtime.
133
135
  // since there is no existing mechanism to re-translate the UI at runtime (not for the UI labels at least)
134
- await model.importLayout({ dataProvider, diagram: currentDiagram, signal, validateLinks: true });
136
+ await diagramService.importDiagram(model, dataProvider, currentDiagram, signal);
135
137
  }
136
138
  else if ((_a = config.seedIris) === null || _a === void 0 ? void 0 : _a.length) {
137
139
  await seedIrisToCanvas(context, dataProvider, config.seedIris, signal);
@@ -142,7 +144,7 @@ function ReactodiaApp(props) {
142
144
  else {
143
145
  // Restore the previously saved layout when present, otherwise start empty. Either way this binds the
144
146
  // data provider (savedDiagram is undefined -> empty diagram), so the unified search/lookup works.
145
- await model.importLayout({ dataProvider, diagram: savedDiagram, signal, validateLinks: true });
147
+ await diagramService.importDiagram(model, dataProvider, savedDiagram, signal);
146
148
  }
147
149
  }, [language]);
148
150
  /**
@@ -161,7 +163,7 @@ function ReactodiaApp(props) {
161
163
  defaultLanguage: language,
162
164
  translations: translationsForLanguage(language),
163
165
  typeStyleResolver: resolveTypeStyle
164
- }, createElement(DefaultWorkspace, {}));
166
+ }, createElement(DefaultWorkspace, { menu: createElement(GraphwiseMenu) }));
165
167
  }
166
168
  /**
167
169
  * Mounts the Reactodia application into the given container and returns the React root.
@@ -14,6 +14,20 @@ import { Service } from "../../providers/service/service";
14
14
  * Service for diagram related operations and subscriptions
15
15
  */
16
16
  let DiagramService = DiagramService_1 = class DiagramService {
17
+ /**
18
+ * Replaces the diagram content with the given serialization. Element data is requested from the data provider and
19
+ * the links between the imported elements are validated against it.
20
+ *
21
+ * @param model The diagram model to import into.
22
+ * @param dataProvider The data provider to bind to the diagram.
23
+ * @param diagram The serialization to import. When `undefined`, the diagram is cleared.
24
+ * @param signal Optional signal to abort the import.
25
+ */
26
+ importDiagram(model, dataProvider, diagram, signal) {
27
+ // selection is not cleared on import, so we clear it manually to avoid painting the previous selection
28
+ model.setSelection([]);
29
+ return model.importLayout({ dataProvider, diagram, signal, validateLinks: true });
30
+ }
17
31
  /**
18
32
  * Subscribes to diagram changes. On every debounced change the current layout is
19
33
  * exported and passed to the callback.
@@ -0,0 +1,68 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
4
+ r = Reflect.decorate(decorators, target, key, desc);
5
+ else
6
+ for (var i = decorators.length - 1; i >= 0; i--)
7
+ if (d = decorators[i])
8
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var DiagramFileService_1;
12
+ import { Service } from "../../providers/service/service";
13
+ import { service } from "../../providers/service/service-inject";
14
+ import { WindowService } from "../window/window.service";
15
+ /**
16
+ * Service for downloading and uploading the diagram as a file. The file content is the same
17
+ * {@link SerializedDiagram} that is persisted to the local store.
18
+ */
19
+ let DiagramFileService = DiagramFileService_1 = class DiagramFileService {
20
+ /**
21
+ * Downloads the given diagram serialization as a file with the given name.
22
+ */
23
+ download(diagram, fileName) {
24
+ const window = service(WindowService).getWindow();
25
+ const blob = new Blob([JSON.stringify(diagram)], { type: DiagramFileService_1.MIME_TYPE });
26
+ const url = window.URL.createObjectURL(blob);
27
+ const link = window.document.createElement('a');
28
+ link.href = url;
29
+ link.download = fileName;
30
+ link.click();
31
+ window.URL.revokeObjectURL(url);
32
+ }
33
+ /**
34
+ * Reads a diagram serialization from the given file.
35
+ *
36
+ * @throws Error when the file is not valid JSON or is not a serialized diagram.
37
+ */
38
+ async read(file) {
39
+ let diagram;
40
+ try {
41
+ diagram = JSON.parse(await file.text());
42
+ }
43
+ catch (error) {
44
+ throw new Error(`File "${file.name}" is not valid JSON: ${error.message}`);
45
+ }
46
+ if (!this.isSerializedDiagram(diagram)) {
47
+ throw new Error(`File "${file.name}" is not a serialized diagram`);
48
+ }
49
+ return diagram;
50
+ }
51
+ /**
52
+ * Checks if the provided value is a serialized diagram
53
+ */
54
+ isSerializedDiagram(value) {
55
+ var _a, _b;
56
+ const diagram = value;
57
+ return (diagram === null || diagram === void 0 ? void 0 : diagram['@type']) === 'Diagram'
58
+ && Array.isArray((_a = diagram.layoutData) === null || _a === void 0 ? void 0 : _a.elements)
59
+ && Array.isArray((_b = diagram.layoutData) === null || _b === void 0 ? void 0 : _b.links);
60
+ }
61
+ };
62
+ DiagramFileService.ID = 'DiagramFileService';
63
+ DiagramFileService.FILE_EXTENSION = '.json';
64
+ DiagramFileService.MIME_TYPE = 'application/json';
65
+ DiagramFileService = DiagramFileService_1 = __decorate([
66
+ Service
67
+ ], DiagramFileService);
68
+ export { DiagramFileService };