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.
@@ -23866,6 +23866,35 @@ function ToolbarAction(props) {
23866
23866
  });
23867
23867
  }
23868
23868
  /**
23869
+ * Toolbar action component to open a file selection dialog.
23870
+ *
23871
+ * @category Components
23872
+ */
23873
+ function ToolbarActionOpen(props) {
23874
+ const { className, fileAccept, onSelect, children, ...otherProps } = props;
23875
+ const inputRef = reactExports.useRef(null);
23876
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [/* @__PURE__ */ jsxRuntimeExports.jsx(ToolbarAction, {
23877
+ ...otherProps,
23878
+ className: clsx(className, `${CLASS_NAME$8}__open`),
23879
+ onSelect: () => {
23880
+ inputRef.current?.click();
23881
+ },
23882
+ children
23883
+ }), /* @__PURE__ */ jsxRuntimeExports.jsx("input", {
23884
+ ref: inputRef,
23885
+ role: "none",
23886
+ type: "file",
23887
+ className: `${CLASS_NAME$8}__open-input`,
23888
+ accept: fileAccept,
23889
+ onChange: (e) => {
23890
+ if (e.currentTarget.files) {
23891
+ const file = e.currentTarget.files[0];
23892
+ onSelect(file);
23893
+ }
23894
+ }
23895
+ })] });
23896
+ }
23897
+ /**
23869
23898
  * Toolbar action component to clear diagram content.
23870
23899
  *
23871
23900
  * Clearing the diagram adds a command to the command history.
@@ -30844,6 +30873,16 @@ function getContentFittingBoxForLayout(state) {
30844
30873
  };
30845
30874
  }
30846
30875
 
30876
+ const graphwise_toolbar_action$1 = {
30877
+ "download_diagram.label": "Export to JSON",
30878
+ "download_diagram.title": "Export the diagram to a JSON file",
30879
+ "upload_diagram.label": "Import from JSON",
30880
+ "upload_diagram.title": "Replace the diagram with one from a JSON file"
30881
+ };
30882
+ var enTranslation = {
30883
+ graphwise_toolbar_action: graphwise_toolbar_action$1
30884
+ };
30885
+
30847
30886
  const authoring_state = {
30848
30887
  "entity_action_delete.label": "supprimer",
30849
30888
  "entity_action_delete.title": "Supprimer l'entité",
@@ -31098,6 +31137,12 @@ const standard_link = {
31098
31137
  const toolbar = {
31099
31138
  "menu_toggle.title": "Ouvrir le menu"
31100
31139
  };
31140
+ const graphwise_toolbar_action = {
31141
+ "download_diagram.label": "Exporter en JSON",
31142
+ "download_diagram.title": "Exporter le diagramme en fichier JSON",
31143
+ "upload_diagram.label": "Importer depuis JSON",
31144
+ "upload_diagram.title": "Remplacer le diagramme par celui d'un fichier JSON"
31145
+ };
31101
31146
  const toolbar_action = {
31102
31147
  "clear_all.command": "Tout effacer",
31103
31148
  "clear_all.title": "Supprimer tous les éléments et liens du diagramme",
@@ -31208,6 +31253,7 @@ var frTranslation = {
31208
31253
  standard_element: standard_element,
31209
31254
  standard_link: standard_link,
31210
31255
  toolbar: toolbar,
31256
+ graphwise_toolbar_action: graphwise_toolbar_action,
31211
31257
  toolbar_action: toolbar_action,
31212
31258
  unified_search: unified_search,
31213
31259
  visual_authoring: visual_authoring,
@@ -31222,8 +31268,10 @@ var LanguageKey;
31222
31268
  LanguageKey["FR"] = "fr";
31223
31269
  })(LanguageKey || (LanguageKey = {}));
31224
31270
 
31225
- // English is provided by default from reactodia so we don't need to include it here, unless we override it
31271
+ // English is provided by default from reactodia, so the English bundle holds only our own keys and overrides. If
31272
+ // A key is missing, reactodia defaults to its own translation.
31226
31273
  const TRANSLATIONS = {
31274
+ [LanguageKey.EN]: enTranslation,
31227
31275
  [LanguageKey.FR]: frTranslation
31228
31276
  };
31229
31277
 
@@ -31283,7 +31331,7 @@ function Service(_serviceClass) {
31283
31331
  // no implementation needed. We only use the type checking of the parameter
31284
31332
  }
31285
31333
 
31286
- var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31334
+ var __decorate$4 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31287
31335
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31288
31336
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31289
31337
  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;
@@ -31294,6 +31342,20 @@ var DiagramService_1;
31294
31342
  * Service for diagram related operations and subscriptions
31295
31343
  */
31296
31344
  let DiagramService = DiagramService_1 = class DiagramService {
31345
+ /**
31346
+ * Replaces the diagram content with the given serialization. Element data is requested from the data provider and
31347
+ * the links between the imported elements are validated against it.
31348
+ *
31349
+ * @param model The diagram model to import into.
31350
+ * @param dataProvider The data provider to bind to the diagram.
31351
+ * @param diagram The serialization to import. When `undefined`, the diagram is cleared.
31352
+ * @param signal Optional signal to abort the import.
31353
+ */
31354
+ importDiagram(model, dataProvider, diagram, signal) {
31355
+ // selection is not cleared on import, so we clear it manually to avoid painting the previous selection
31356
+ model.setSelection([]);
31357
+ return model.importLayout({ dataProvider, diagram, signal, validateLinks: true });
31358
+ }
31297
31359
  /**
31298
31360
  * Subscribes to diagram changes. On every debounced change the current layout is
31299
31361
  * exported and passed to the callback.
@@ -31328,11 +31390,11 @@ let DiagramService = DiagramService_1 = class DiagramService {
31328
31390
  DiagramService.ID = 'DiagramService';
31329
31391
  DiagramService.NOTIFY_DEBOUNCE_MS = 300;
31330
31392
  DiagramService.HISTORY_CHANGED_EVENT = 'historyChanged';
31331
- DiagramService = DiagramService_1 = __decorate$3([
31393
+ DiagramService = DiagramService_1 = __decorate$4([
31332
31394
  Service
31333
31395
  ], DiagramService);
31334
31396
 
31335
- var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31397
+ var __decorate$3 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31336
31398
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31337
31399
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31338
31400
  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;
@@ -31347,11 +31409,11 @@ let WindowService = class WindowService {
31347
31409
  }
31348
31410
  };
31349
31411
  WindowService.ID = 'WindowService';
31350
- WindowService = __decorate$2([
31412
+ WindowService = __decorate$3([
31351
31413
  Service
31352
31414
  ], WindowService);
31353
31415
 
31354
- var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31416
+ var __decorate$2 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31355
31417
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31356
31418
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31357
31419
  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;
@@ -31395,11 +31457,11 @@ let DiagramStorageService = DiagramStorageService_1 = class DiagramStorageServic
31395
31457
  };
31396
31458
  DiagramStorageService.ID = 'DiagramStorageService';
31397
31459
  DiagramStorageService.STORAGE_KEY = 'diagram.state';
31398
- DiagramStorageService = DiagramStorageService_1 = __decorate$1([
31460
+ DiagramStorageService = DiagramStorageService_1 = __decorate$2([
31399
31461
  Service
31400
31462
  ], DiagramStorageService);
31401
31463
 
31402
- var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31464
+ var __decorate$1 = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31403
31465
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31404
31466
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31405
31467
  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;
@@ -31422,7 +31484,7 @@ let EventService = class EventService {
31422
31484
  }
31423
31485
  };
31424
31486
  EventService.ID = 'EventService';
31425
- EventService = __decorate([
31487
+ EventService = __decorate$1([
31426
31488
  Service
31427
31489
  ], EventService);
31428
31490
 
@@ -31516,8 +31578,102 @@ const resetColors = () => {
31516
31578
  typeToIndex.clear();
31517
31579
  };
31518
31580
 
31581
+ var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) {
31582
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
31583
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
31584
+ 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;
31585
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
31586
+ };
31587
+ var DiagramFileService_1;
31588
+ /**
31589
+ * Service for downloading and uploading the diagram as a file. The file content is the same
31590
+ * {@link SerializedDiagram} that is persisted to the local store.
31591
+ */
31592
+ let DiagramFileService = DiagramFileService_1 = class DiagramFileService {
31593
+ /**
31594
+ * Downloads the given diagram serialization as a file with the given name.
31595
+ */
31596
+ download(diagram, fileName) {
31597
+ const window = service(WindowService).getWindow();
31598
+ const blob = new Blob([JSON.stringify(diagram)], { type: DiagramFileService_1.MIME_TYPE });
31599
+ const url = window.URL.createObjectURL(blob);
31600
+ const link = window.document.createElement('a');
31601
+ link.href = url;
31602
+ link.download = fileName;
31603
+ link.click();
31604
+ window.URL.revokeObjectURL(url);
31605
+ }
31606
+ /**
31607
+ * Reads a diagram serialization from the given file.
31608
+ *
31609
+ * @throws Error when the file is not valid JSON or is not a serialized diagram.
31610
+ */
31611
+ async read(file) {
31612
+ let diagram;
31613
+ try {
31614
+ diagram = JSON.parse(await file.text());
31615
+ }
31616
+ catch (error) {
31617
+ throw new Error(`File "${file.name}" is not valid JSON: ${error.message}`);
31618
+ }
31619
+ if (!this.isSerializedDiagram(diagram)) {
31620
+ throw new Error(`File "${file.name}" is not a serialized diagram`);
31621
+ }
31622
+ return diagram;
31623
+ }
31624
+ /**
31625
+ * Checks if the provided value is a serialized diagram
31626
+ */
31627
+ isSerializedDiagram(value) {
31628
+ var _a, _b;
31629
+ const diagram = value;
31630
+ return (diagram === null || diagram === void 0 ? void 0 : diagram['@type']) === 'Diagram'
31631
+ && Array.isArray((_a = diagram.layoutData) === null || _a === void 0 ? void 0 : _a.elements)
31632
+ && Array.isArray((_b = diagram.layoutData) === null || _b === void 0 ? void 0 : _b.links);
31633
+ }
31634
+ };
31635
+ DiagramFileService.ID = 'DiagramFileService';
31636
+ DiagramFileService.FILE_EXTENSION = '.json';
31637
+ DiagramFileService.MIME_TYPE = 'application/json';
31638
+ DiagramFileService = DiagramFileService_1 = __decorate([
31639
+ Service
31640
+ ], DiagramFileService);
31641
+
31642
+ /**
31643
+ * The workspace main menu: Reactodia's default actions, followed by the diagram download and upload.
31644
+ *
31645
+ * Passing a `menu` to the `DefaultWorkspace` replaces its default actions, so they are listed here again.
31646
+ * Rendered inside the workspace, so the actions can use the workspace hooks.
31647
+ */
31648
+ function GraphwiseMenu() {
31649
+ const { model } = useWorkspace();
31650
+ const t = useTranslation();
31651
+ const diagramFileService = service(DiagramFileService);
31652
+ const diagramService = service(DiagramService);
31653
+ const downloadDiagram = () => {
31654
+ const timestamp = new Date().toISOString().replace(/[-:]|\.\d+Z$/g, '');
31655
+ diagramFileService.download(model.exportLayout(), `diagram-${timestamp}${DiagramFileService.FILE_EXTENSION}`);
31656
+ };
31657
+ const uploadDiagram = async (file) => {
31658
+ try {
31659
+ const diagram = await diagramFileService.read(file);
31660
+ await diagramService.importDiagram(model, model.dataProvider, diagram);
31661
+ }
31662
+ catch (error) {
31663
+ console.error('Failed to upload the diagram', error);
31664
+ }
31665
+ };
31666
+ 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,
31667
+ // The same icon as ToolbarActionSave. We dont use that action, since it disables saving on refresh
31668
+ { 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, {
31669
+ title: t.text('graphwise_toolbar_action.upload_diagram.title'),
31670
+ fileAccept: DiagramFileService.FILE_EXTENSION,
31671
+ onSelect: (file) => void uploadDiagram(file)
31672
+ }, t.text('graphwise_toolbar_action.upload_diagram.label')));
31673
+ }
31674
+
31519
31675
  /**
31520
- * Reactodia ships English as its built-in default bundle, so English needs no override.
31676
+ * Reactodia ships English as its built-in default bundle, so the English bundle only adds our own keys.
31521
31677
  * Other languages are layered on top as partial bundles; unknown codes fall back to English.
31522
31678
  *
31523
31679
  * Reactodia never re-reads the translations, so the component must be remounted
@@ -31635,10 +31791,11 @@ function ReactodiaApp(props) {
31635
31791
  const dataProvider = createDataProvider(props);
31636
31792
  const isReload = props.isReload;
31637
31793
  const savedDiagram = service(DiagramStorageService).load();
31794
+ const diagramService = service(DiagramService);
31638
31795
  if (isReload) {
31639
31796
  // Simply reload without using the current state. This may happen when the user switches the language at runtime.
31640
31797
  // since there is no existing mechanism to re-translate the UI at runtime (not for the UI labels at least)
31641
- await model.importLayout({ dataProvider, diagram: currentDiagram, signal, validateLinks: true });
31798
+ await diagramService.importDiagram(model, dataProvider, currentDiagram, signal);
31642
31799
  }
31643
31800
  else if ((_a = config.seedIris) === null || _a === void 0 ? void 0 : _a.length) {
31644
31801
  await seedIrisToCanvas(context, dataProvider, config.seedIris, signal);
@@ -31649,7 +31806,7 @@ function ReactodiaApp(props) {
31649
31806
  else {
31650
31807
  // Restore the previously saved layout when present, otherwise start empty. Either way this binds the
31651
31808
  // data provider (savedDiagram is undefined -> empty diagram), so the unified search/lookup works.
31652
- await model.importLayout({ dataProvider, diagram: savedDiagram, signal, validateLinks: true });
31809
+ await diagramService.importDiagram(model, dataProvider, savedDiagram, signal);
31653
31810
  }
31654
31811
  }, [language]);
31655
31812
  /**
@@ -31668,7 +31825,7 @@ function ReactodiaApp(props) {
31668
31825
  defaultLanguage: language,
31669
31826
  translations: translationsForLanguage(language),
31670
31827
  typeStyleResolver: resolveTypeStyle
31671
- }, reactExports.createElement(DefaultWorkspace, {}));
31828
+ }, reactExports.createElement(DefaultWorkspace, { menu: reactExports.createElement(GraphwiseMenu) }));
31672
31829
  }
31673
31830
  /**
31674
31831
  * Mounts the Reactodia application into the given container and returns the React root.
@@ -1 +1 @@
1
- import{p as e,b as r}from"./p-Cypw7ydc.js";export{s as setNonce}from"./p-Cypw7ydc.js";import{g as t}from"./p-CalY52z6.js";(()=>{const r=import.meta.url,t={};return""!==r&&(t.resourcesUrl=new URL(".",r).href),e(t)})().then((async e=>(await t(),r([["p-81eeb4c3",[[0,"graphwise-reactodia",{currentRepository:[1,"current-repository"],config:[16],language:[1],providerSettings:[16,"provider-settings"],theme:[1]},null,{currentRepository:["onProviderSettingsChange"],providerSettings:["onProviderSettingsChange"],language:["onLanguageChange"],theme:["onThemeChange"]}]]]],e))));
1
+ import{p as e,b as r}from"./p-Cypw7ydc.js";export{s as setNonce}from"./p-Cypw7ydc.js";import{g as t}from"./p-CalY52z6.js";(()=>{const r=import.meta.url,t={};return""!==r&&(t.resourcesUrl=new URL(".",r).href),e(t)})().then((async e=>(await t(),r([["p-52bd1b0e",[[0,"graphwise-reactodia",{currentRepository:[1,"current-repository"],config:[16],language:[1],providerSettings:[16,"provider-settings"],theme:[1]},null,{currentRepository:["onProviderSettingsChange"],providerSettings:["onProviderSettingsChange"],language:["onLanguageChange"],theme:["onThemeChange"]}]]]],e))));