@itwin/appui-react 3.4.0-dev.34 → 3.4.0-dev.37

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.
Files changed (39) hide show
  1. package/lib/cjs/appui-react/childwindow/ChildWindowManager.d.ts.map +1 -1
  2. package/lib/cjs/appui-react/childwindow/ChildWindowManager.js +4 -5
  3. package/lib/cjs/appui-react/childwindow/ChildWindowManager.js.map +1 -1
  4. package/lib/cjs/appui-react/frontstage/FrontstageDef.d.ts +13 -1
  5. package/lib/cjs/appui-react/frontstage/FrontstageDef.d.ts.map +1 -1
  6. package/lib/cjs/appui-react/frontstage/FrontstageDef.js +140 -96
  7. package/lib/cjs/appui-react/frontstage/FrontstageDef.js.map +1 -1
  8. package/lib/cjs/appui-react/stagepanels/StagePanelDef.d.ts +0 -6
  9. package/lib/cjs/appui-react/stagepanels/StagePanelDef.d.ts.map +1 -1
  10. package/lib/cjs/appui-react/widget-panels/Frontstage.d.ts +20 -697
  11. package/lib/cjs/appui-react/widget-panels/Frontstage.d.ts.map +1 -1
  12. package/lib/cjs/appui-react/widget-panels/Frontstage.js +275 -278
  13. package/lib/cjs/appui-react/widget-panels/Frontstage.js.map +1 -1
  14. package/lib/cjs/appui-react/widget-panels/useWidgetDirection.js +1 -1
  15. package/lib/cjs/appui-react/widget-panels/useWidgetDirection.js.map +1 -1
  16. package/lib/cjs/appui-react/widgets/WidgetDef.d.ts +13 -6
  17. package/lib/cjs/appui-react/widgets/WidgetDef.d.ts.map +1 -1
  18. package/lib/cjs/appui-react/widgets/WidgetDef.js +6 -1
  19. package/lib/cjs/appui-react/widgets/WidgetDef.js.map +1 -1
  20. package/lib/esm/appui-react/childwindow/ChildWindowManager.d.ts.map +1 -1
  21. package/lib/esm/appui-react/childwindow/ChildWindowManager.js +4 -5
  22. package/lib/esm/appui-react/childwindow/ChildWindowManager.js.map +1 -1
  23. package/lib/esm/appui-react/frontstage/FrontstageDef.d.ts +13 -1
  24. package/lib/esm/appui-react/frontstage/FrontstageDef.d.ts.map +1 -1
  25. package/lib/esm/appui-react/frontstage/FrontstageDef.js +142 -98
  26. package/lib/esm/appui-react/frontstage/FrontstageDef.js.map +1 -1
  27. package/lib/esm/appui-react/stagepanels/StagePanelDef.d.ts +0 -6
  28. package/lib/esm/appui-react/stagepanels/StagePanelDef.d.ts.map +1 -1
  29. package/lib/esm/appui-react/widget-panels/Frontstage.d.ts +20 -697
  30. package/lib/esm/appui-react/widget-panels/Frontstage.d.ts.map +1 -1
  31. package/lib/esm/appui-react/widget-panels/Frontstage.js +266 -272
  32. package/lib/esm/appui-react/widget-panels/Frontstage.js.map +1 -1
  33. package/lib/esm/appui-react/widget-panels/useWidgetDirection.js +2 -2
  34. package/lib/esm/appui-react/widget-panels/useWidgetDirection.js.map +1 -1
  35. package/lib/esm/appui-react/widgets/WidgetDef.d.ts +13 -6
  36. package/lib/esm/appui-react/widgets/WidgetDef.d.ts.map +1 -1
  37. package/lib/esm/appui-react/widgets/WidgetDef.js +6 -1
  38. package/lib/esm/appui-react/widgets/WidgetDef.js.map +1 -1
  39. package/package.json +32 -32
@@ -8,13 +8,13 @@
8
8
  */
9
9
  // cSpell:ignore popout
10
10
  import "./Frontstage.scss";
11
- import produce, { castDraft } from "immer";
11
+ import produce from "immer";
12
12
  import * as React from "react";
13
13
  import { assert, Logger, ProcessDetector } from "@itwin/core-bentley";
14
14
  import { StagePanelLocation, UiItemsManager, WidgetState } from "@itwin/appui-abstract";
15
- import { Size, UiStateStorageStatus } from "@itwin/core-react";
15
+ import { Rectangle, Size, UiStateStorageStatus } from "@itwin/core-react";
16
16
  import { ToolbarPopupAutoHideContext } from "@itwin/components-react";
17
- import { addPanelWidget, addTab, addWidgetTabToDraftFloatingPanel, addWidgetTabToFloatingPanel, addWidgetTabToPanelSection, convertAllPopupWidgetContainersToFloating, createNineZoneState, createTabsState, createTabState, createWidgetState, findTab, findWidget, floatingWidgetBringToFront, FloatingWidgets, getUniqueId, getWidgetPanelSectionId, isFloatingLocation, isHorizontalPanelSide, isPopoutLocation, NineZone, NineZoneStateReducer, panelSides, removeTab, toolSettingsTabId, WidgetPanels, } from "@itwin/appui-layout-react";
17
+ import { addFloatingWidget, addPanelWidget, addTab, addTabToWidget, convertAllPopupWidgetContainersToFloating, createNineZoneState, floatingWidgetBringToFront, FloatingWidgets, getTabLocation, getUniqueId, getWidgetPanelSectionId, insertPanelWidget, insertTabToWidget, isFloatingTabLocation, isHorizontalPanelSide, isPanelTabLocation, isPopoutTabLocation, NineZone, NineZoneStateReducer, panelSides, removeTab, removeTabFromWidget, toolSettingsTabId, WidgetPanels, } from "@itwin/appui-layout-react";
18
18
  import { useActiveFrontstageDef } from "../frontstage/Frontstage";
19
19
  import { FrontstageManager } from "../frontstage/FrontstageManager";
20
20
  import { StagePanelState } from "../stagepanels/StagePanelDef";
@@ -259,7 +259,6 @@ export function appendWidgets(state, widgetDefs, side, preferredWidgetIndex) {
259
259
  const saveTab = state.tabs[widgetDef.id];
260
260
  const preferredPanelWidgetSize = saveTab ? saveTab.preferredPanelWidgetSize : widgetDef.preferredPanelSize;
261
261
  const preferredFloatingWidgetSize = saveTab ? saveTab.preferredFloatingWidgetSize : widgetDef.defaultFloatingSize;
262
- const preferredPopoutWidgetSize = saveTab ? saveTab.preferredPopoutWidgetSize : undefined;
263
262
  // if a defaultFloatingSize is specified then this mean the widget can't determine its intrinsic size and must be explicitly sized
264
263
  const userSized = saveTab ? (saveTab.userSized || !!widgetDef.defaultFloatingSize && !!saveTab.preferredFloatingWidgetSize)
265
264
  : !!widgetDef.defaultFloatingSize;
@@ -269,7 +268,6 @@ export function appendWidgets(state, widgetDefs, side, preferredWidgetIndex) {
269
268
  canPopout: widgetDef.canPopout,
270
269
  preferredPanelWidgetSize,
271
270
  preferredFloatingWidgetSize,
272
- preferredPopoutWidgetSize,
273
271
  userSized,
274
272
  isFloatingStateWindowResizable: widgetDef.isFloatingStateWindowResizable,
275
273
  hideWithUiWhenFloating: !!widgetDef.hideWithUiWhenFloating,
@@ -277,13 +275,35 @@ export function appendWidgets(state, widgetDefs, side, preferredWidgetIndex) {
277
275
  if (widgetDef.isFloatingStateSupported && widgetDef.defaultState === WidgetState.Floating) {
278
276
  const floatingContainerId = (_a = widgetDef.floatingContainerId) !== null && _a !== void 0 ? _a : getUniqueId();
279
277
  const widgetContainerId = getWidgetId(side, panelZoneKeys[preferredWidgetIndex]);
280
- const homePanelInfo = { side, widgetId: widgetContainerId, widgetIndex: 0 };
278
+ const home = { side, widgetId: widgetContainerId, widgetIndex: 0 };
281
279
  const preferredPosition = widgetDef.defaultFloatingPosition;
282
- state = addWidgetTabToFloatingPanel(state, floatingContainerId, widgetDef.id, homePanelInfo, preferredFloatingWidgetSize, preferredPosition, userSized, widgetDef.isFloatingStateWindowResizable);
280
+ if (floatingContainerId in state.widgets) {
281
+ state = addTabToWidget(state, widgetDef.id, floatingContainerId);
282
+ }
283
+ else {
284
+ const tab = state.tabs[widgetDef.id];
285
+ const size = { height: 200, width: 300, ...tab.preferredFloatingWidgetSize };
286
+ const preferredPoint = preferredPosition !== null && preferredPosition !== void 0 ? preferredPosition : { x: (state.size.width - size.width) / 2, y: (state.size.height - size.height) / 2 };
287
+ const nzBounds = Rectangle.createFromSize(state.size);
288
+ const bounds = Rectangle.createFromSize(size).offset(preferredPoint);
289
+ const containedBounds = bounds.containIn(nzBounds);
290
+ state = addFloatingWidget(state, floatingContainerId, [widgetDef.id], {
291
+ bounds: containedBounds.toProps(),
292
+ home,
293
+ userSized,
294
+ }, {
295
+ isFloatingStateWindowResizable: widgetDef.isFloatingStateWindowResizable,
296
+ });
297
+ }
283
298
  }
284
299
  else {
285
300
  const widgetPanelSectionId = getWidgetPanelSectionId(side, preferredWidgetIndex);
286
- state = addWidgetTabToPanelSection(state, side, widgetPanelSectionId, widgetDef.id);
301
+ if (widgetPanelSectionId in state.widgets) {
302
+ state = addTabToWidget(state, widgetDef.id, widgetPanelSectionId);
303
+ }
304
+ else {
305
+ state = addPanelWidget(state, side, widgetPanelSectionId, [widgetDef.id]);
306
+ }
287
307
  }
288
308
  }
289
309
  return state;
@@ -329,17 +349,15 @@ export function addMissingWidgets(frontstageDef, initialState) {
329
349
  state = appendWidgets(state, determineNewWidgets((_t = frontstageDef.bottomPanel) === null || _t === void 0 ? void 0 : _t.panelZones.end.widgetDefs, state), "bottom", 1);
330
350
  return state;
331
351
  }
332
- /** Removes widgets with Hidden defaultState. */
333
- function removeHiddenWidgets(state, frontstageDef) {
352
+ /** Hide widgets with Hidden defaultState. */
353
+ function hideWidgets(state, frontstageDef) {
334
354
  const tabs = Object.values(state.tabs);
335
355
  for (const tab of tabs) {
336
356
  const widgetDef = frontstageDef.findWidgetDef(tab.id);
337
357
  if (!widgetDef)
338
358
  continue;
339
359
  if (widgetDef.defaultState === WidgetState.Hidden) {
340
- state = produce(state, (draft) => {
341
- hideWidget(draft, widgetDef);
342
- });
360
+ state = hideWidget(state, widgetDef);
343
361
  }
344
362
  }
345
363
  return state;
@@ -348,16 +366,15 @@ function removeHiddenWidgets(state, frontstageDef) {
348
366
  * @internal
349
367
  */
350
368
  export function removeMissingWidgets(frontstageDef, initialState) {
351
- const state = produce(initialState, (draft) => {
352
- for (const [, tab] of Object.entries(draft.tabs)) {
353
- if (tab.id === toolSettingsTabId)
354
- continue;
355
- const widgetDef = frontstageDef.findWidgetDef(tab.id);
356
- if (widgetDef)
357
- continue;
358
- removeTab(draft, tab.id);
359
- }
360
- });
369
+ let state = initialState;
370
+ for (const [, tab] of Object.entries(state.tabs)) {
371
+ if (tab.id === toolSettingsTabId)
372
+ continue;
373
+ const widgetDef = frontstageDef.findWidgetDef(tab.id);
374
+ if (widgetDef)
375
+ continue;
376
+ state = removeTab(state, tab.id);
377
+ }
361
378
  return state;
362
379
  }
363
380
  function getWidgetLabel(label) {
@@ -508,23 +525,23 @@ export function isFrontstageStateSettingResult(settingsResult) {
508
525
  return false;
509
526
  }
510
527
  /** @internal */
511
- export function initializePanel(nineZone, frontstageDef, panelSide) {
512
- nineZone = addPanelWidgets(nineZone, frontstageDef, panelSide);
528
+ export function initializePanel(state, frontstageDef, panelSide) {
529
+ state = addPanelWidgets(state, frontstageDef, panelSide);
513
530
  const location = toStagePanelLocation(panelSide);
514
531
  const panelDef = frontstageDef.getStagePanelDef(location);
515
- nineZone = produce(nineZone, (draft) => {
532
+ state = produce(state, (draft) => {
516
533
  var _a, _b, _c;
517
534
  const panel = draft.panels[panelSide];
518
535
  panel.minSize = (_a = panelDef === null || panelDef === void 0 ? void 0 : panelDef.minSize) !== null && _a !== void 0 ? _a : panel.minSize;
519
536
  panel.pinned = (_b = panelDef === null || panelDef === void 0 ? void 0 : panelDef.pinned) !== null && _b !== void 0 ? _b : panel.pinned;
520
537
  panel.resizable = (_c = panelDef === null || panelDef === void 0 ? void 0 : panelDef.resizable) !== null && _c !== void 0 ? _c : panel.resizable;
521
538
  if (panelDef === null || panelDef === void 0 ? void 0 : panelDef.maxSizeSpec) {
522
- panel.maxSize = getPanelMaxSize(panelDef.maxSizeSpec, panelSide, nineZone.size);
539
+ panel.maxSize = getPanelMaxSize(panelDef.maxSizeSpec, panelSide, state.size);
523
540
  }
524
541
  const size = panelDef === null || panelDef === void 0 ? void 0 : panelDef.size;
525
542
  panel.size = size === undefined ? size : Math.min(Math.max(size, panel.minSize), panel.maxSize);
526
543
  });
527
- return nineZone;
544
+ return state;
528
545
  }
529
546
  function getPanelMaxSize(maxSizeSpec, panel, nineZoneSize) {
530
547
  if (typeof maxSizeSpec === "number") {
@@ -533,7 +550,7 @@ function getPanelMaxSize(maxSizeSpec, panel, nineZoneSize) {
533
550
  const size = isHorizontalPanelSide(panel) ? nineZoneSize.height : nineZoneSize.width;
534
551
  return maxSizeSpec.percentage / 100 * size;
535
552
  }
536
- const stateVersion = 12; // this needs to be bumped when NineZoneState is changed (to recreate layout).
553
+ const stateVersion = 13; // this needs to be bumped when NineZoneState is changed (to recreate the layout).
537
554
  /** @internal */
538
555
  export function initializeNineZoneState(frontstageDef) {
539
556
  let nineZone = defaultNineZone;
@@ -555,6 +572,7 @@ export function initializeNineZoneState(frontstageDef) {
555
572
  for (const [, panel] of Object.entries(stateDraft.panels)) {
556
573
  const expanded = panel.widgets.find((widgetId) => stateDraft.widgets[widgetId].minimized === false);
557
574
  const firstWidget = panel.widgets.length > 0 ? stateDraft.widgets[panel.widgets[0]] : undefined;
575
+ // istanbul ignore next
558
576
  if (!expanded && firstWidget) {
559
577
  firstWidget.minimized = false;
560
578
  }
@@ -583,7 +601,7 @@ export function initializeNineZoneState(frontstageDef) {
583
601
  }
584
602
  });
585
603
  nineZone = addMissingWidgets(frontstageDef, nineZone);
586
- nineZone = removeHiddenWidgets(nineZone, frontstageDef);
604
+ nineZone = hideWidgets(nineZone, frontstageDef);
587
605
  nineZone = processPopoutWidgets(nineZone, frontstageDef);
588
606
  return nineZone;
589
607
  }
@@ -595,29 +613,32 @@ export function initializeNineZoneState(frontstageDef) {
595
613
  export function restoreNineZoneState(frontstageDef, saved) {
596
614
  let restored = {
597
615
  ...saved,
598
- tabs: createTabsState(),
616
+ tabs: defaultNineZone.tabs,
599
617
  };
600
- restored = produce(restored, (draft) => {
601
- var _a;
602
- for (const [, tab] of Object.entries(saved.tabs)) {
603
- const widgetDef = frontstageDef.findWidgetDef(tab.id);
604
- if (!widgetDef) {
605
- Logger.logInfo(UiFramework.loggerCategory(restoreNineZoneState), "WidgetDef is not found for saved tab.", () => ({
606
- frontstageId: frontstageDef.id,
607
- tabId: tab.id,
608
- }));
609
- removeTab(draft, tab.id);
610
- // let fall through so we preserve preferred sizes of widgets load via UiItemsProvider
611
- }
618
+ for (const [, tab] of Object.entries(saved.tabs)) {
619
+ // Re-add the placeholder tab in order to correctly remove it from the state if WidgetDef is not found.
620
+ restored = addTab(restored, tab.id);
621
+ const widgetDef = frontstageDef.findWidgetDef(tab.id);
622
+ if (!widgetDef) {
623
+ Logger.logInfo(UiFramework.loggerCategory(restoreNineZoneState), "WidgetDef is not found for saved tab.", () => ({
624
+ frontstageId: frontstageDef.id,
625
+ tabId: tab.id,
626
+ }));
627
+ restored = removeTab(restored, tab.id);
628
+ continue;
629
+ }
630
+ restored = produce(restored, (draft) => {
612
631
  draft.tabs[tab.id] = {
613
632
  ...tab,
614
- label: getWidgetLabel((_a = widgetDef === null || widgetDef === void 0 ? void 0 : widgetDef.label) !== null && _a !== void 0 ? _a : "undefined"),
615
- hideWithUiWhenFloating: widgetDef === null || widgetDef === void 0 ? void 0 : widgetDef.hideWithUiWhenFloating,
616
- iconSpec: widgetDef === null || widgetDef === void 0 ? void 0 : widgetDef.iconSpec,
617
- canPopout: widgetDef === null || widgetDef === void 0 ? void 0 : widgetDef.canPopout,
618
- isFloatingStateWindowResizable: widgetDef === null || widgetDef === void 0 ? void 0 : widgetDef.isFloatingStateWindowResizable,
633
+ label: getWidgetLabel(widgetDef.label),
634
+ hideWithUiWhenFloating: widgetDef.hideWithUiWhenFloating,
635
+ iconSpec: widgetDef.iconSpec,
636
+ canPopout: widgetDef.canPopout,
637
+ isFloatingStateWindowResizable: widgetDef.isFloatingStateWindowResizable,
619
638
  };
620
- }
639
+ });
640
+ }
641
+ restored = produce(restored, (draft) => {
621
642
  // remove center panel section if one is found in left or right panels
622
643
  const oldLeftMiddleIndex = saved.panels.left.widgets.findIndex((value) => value === "leftMiddle");
623
644
  // istanbul ignore next
@@ -645,14 +666,6 @@ export function restoreNineZoneState(frontstageDef, saved) {
645
666
  }
646
667
  return;
647
668
  });
648
- // TODO: remove when isUUID check is refactored.
649
- restored = produce(restored, (draft) => {
650
- // Floating widget might have been removed when saving, dock tool settings if tab is not found.
651
- const location = findTab(draft, toolSettingsTabId);
652
- if (!location) {
653
- draft.toolSettings.type = "docked";
654
- }
655
- });
656
669
  if (FrontstageManager.nineZoneSize && (0 !== FrontstageManager.nineZoneSize.height || 0 !== FrontstageManager.nineZoneSize.width)) {
657
670
  restored = FrameworkStateReducer(restored, {
658
671
  type: "RESIZE",
@@ -664,20 +677,6 @@ export function restoreNineZoneState(frontstageDef, saved) {
664
677
  }
665
678
  return restored;
666
679
  }
667
- /**
668
- * Checks to see whether a widget id is one of our generated uuids
669
- * @param uuid the string value to test
670
- * @returns boolean
671
- */
672
- function isUUID(uuid) {
673
- const s = `${uuid}`;
674
- const result = s.match("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
675
- // istanbul ignore else
676
- if (result === null) {
677
- return false;
678
- }
679
- return true;
680
- }
681
680
  /** Prepares NineZoneState to be saved.
682
681
  * @note Removes toolSettings tab.
683
682
  * @note Removes tab labels and iconSpec.
@@ -692,31 +691,60 @@ export function packNineZoneState(state) {
692
691
  for (const [, tab] of Object.entries(state.tabs)) {
693
692
  if (tab.id === toolSettingsTabId)
694
693
  continue;
695
- // istanbul ignore else
696
- if (isUUID(tab.id))
697
- continue;
698
694
  draft.tabs[tab.id] = {
699
695
  id: tab.id,
700
696
  preferredFloatingWidgetSize: tab.preferredFloatingWidgetSize,
701
- preferredPopoutWidgetSize: tab.preferredPopoutWidgetSize,
702
697
  allowedPanelTargets: tab.allowedPanelTargets,
703
698
  userSized: tab.userSized,
704
699
  };
705
700
  }
706
701
  });
707
- packed = produce(packed, (draft) => {
708
- for (const [, floatingWidget] of Object.entries(state.floatingWidgets.byId)) {
709
- const widgetId = floatingWidget.id;
710
- // istanbul ignore else
711
- if (isUUID(widgetId)) {
712
- const idIndex = draft.floatingWidgets.allIds.indexOf(widgetId);
713
- draft.floatingWidgets.allIds.splice(idIndex, 1);
714
- delete draft.floatingWidgets.byId[widgetId];
715
- delete draft.widgets[widgetId];
702
+ return packed;
703
+ }
704
+ // TODO: clean-up of saved widgets.
705
+ // istanbul ignore next
706
+ function packSavedWidgets(savedWidgets, frontstageDef) {
707
+ for (const widgetDef of frontstageDef.widgetDefs) {
708
+ const id = widgetDef.id;
709
+ const initialWidget = {
710
+ id,
711
+ };
712
+ const widget = produce(initialWidget, (draft) => {
713
+ if (widgetDef.tabLocation) {
714
+ draft.tabLocation = { ...widgetDef.tabLocation };
716
715
  }
716
+ if (widgetDef.popoutBounds) {
717
+ draft.popoutBounds = widgetDef.popoutBounds.toProps();
718
+ }
719
+ });
720
+ if (widget === initialWidget)
721
+ continue;
722
+ savedWidgets = produce(savedWidgets, (draft) => {
723
+ draft.push(widget);
724
+ });
725
+ }
726
+ return savedWidgets;
727
+ }
728
+ // istanbul ignore next
729
+ function restoreSavedWidgets(savedWidgets, frontstage) {
730
+ let i = savedWidgets.length;
731
+ while (i--) {
732
+ const savedWidget = savedWidgets[i];
733
+ const widgetId = savedWidget.id;
734
+ const widgetDef = frontstage.findWidgetDef(widgetId);
735
+ if (!widgetDef)
736
+ continue;
737
+ if (savedWidget.tabLocation) {
738
+ widgetDef.tabLocation = { ...savedWidget.tabLocation };
717
739
  }
718
- });
719
- return packed;
740
+ if (savedWidget.popoutBounds) {
741
+ widgetDef.popoutBounds = Rectangle.create(savedWidget.popoutBounds);
742
+ }
743
+ savedWidgets = produce(savedWidgets, (draft) => {
744
+ draft.splice(i, 1);
745
+ });
746
+ }
747
+ return savedWidgets;
720
748
  }
721
749
  /** @internal */
722
750
  export function isPanelCollapsed(zoneStates, panelStates) {
@@ -724,121 +752,116 @@ export function isPanelCollapsed(zoneStates, panelStates) {
724
752
  const openPanel = panelStates.find((panelState) => panelState === StagePanelState.Open);
725
753
  return !openZone && !openPanel;
726
754
  }
727
- function addRemovedTab(nineZone, widgetDef) {
728
- var _a, _b;
729
- const newTab = createTabState(widgetDef.id, {
730
- label: getWidgetLabel(widgetDef.label),
731
- canPopout: widgetDef.canPopout,
732
- iconSpec: widgetDef.iconSpec,
733
- preferredPanelWidgetSize: widgetDef.preferredPanelSize,
734
- isFloatingStateWindowResizable: widgetDef.isFloatingStateWindowResizable,
735
- hideWithUiWhenFloating: !!widgetDef.hideWithUiWhenFloating,
736
- });
737
- nineZone.tabs[newTab.id] = newTab;
738
- if (widgetDef.tabLocation.widgetId in nineZone.widgets) {
739
- // Add to existing widget (by widget id).
740
- const widgetId = widgetDef.tabLocation.widgetId;
741
- const newTabWidget = nineZone.widgets[widgetId];
742
- newTabWidget.tabs.splice(widgetDef.tabLocation.tabIndex, 0, newTab.id);
755
+ function addHiddenWidget(state, widgetDef) {
756
+ const tabLocation = widgetDef.tabLocation || widgetDef.defaultTabLocation;
757
+ const tabId = widgetDef.id;
758
+ const widgetId = tabLocation.widgetId;
759
+ if (widgetId in state.widgets) {
760
+ // Add to an existing widget (by widget id).
761
+ return insertTabToWidget(state, tabId, widgetId, tabLocation.tabIndex);
743
762
  }
744
- else if (widgetDef.tabLocation.floating) {
745
- const id = widgetDef.tabLocation.widgetId;
746
- if (id in nineZone.floatingWidgets.byId) {
747
- nineZone.floatingWidgets.allIds.push(id);
748
- nineZone.floatingWidgets.byId[id].hidden = false;
749
- addWidgetTabToDraftFloatingPanel(nineZone, (_a = widgetDef.floatingContainerId) !== null && _a !== void 0 ? _a : widgetDef.id, widgetDef.id, nineZone.floatingWidgets.byId[id].home, newTab);
750
- }
751
- else {
752
- const home = { side: widgetDef.tabLocation.side, widgetId: widgetDef.floatingContainerId, widgetIndex: 0 };
753
- addWidgetTabToDraftFloatingPanel(nineZone, (_b = widgetDef.floatingContainerId) !== null && _b !== void 0 ? _b : widgetDef.id, widgetDef.id, home, newTab);
763
+ else if (tabLocation.floatingWidget) {
764
+ const floatingWidget = tabLocation.floatingWidget;
765
+ const floatingWidgetId = floatingWidget.id;
766
+ // Add to an existing floating widget.
767
+ // istanbul ignore next
768
+ if (floatingWidgetId in state.widgets) {
769
+ return insertTabToWidget(state, tabId, floatingWidgetId, tabLocation.tabIndex);
754
770
  }
771
+ // Add to a new floating widget.
772
+ const nzBounds = Rectangle.createFromSize(state.size);
773
+ const bounds = Rectangle.create(floatingWidget.bounds).containIn(nzBounds);
774
+ return addFloatingWidget(state, floatingWidgetId, [tabId], {
775
+ ...floatingWidget,
776
+ bounds: bounds.toProps(),
777
+ });
755
778
  }
756
- else {
757
- const newTabPanel = nineZone.panels[widgetDef.tabLocation.side];
758
- if (newTabPanel.maxWidgetCount === newTabPanel.widgets.length) {
759
- // Add to existing panel widget.
760
- const widgetIndex = Math.min(newTabPanel.maxWidgetCount - 1, widgetDef.tabLocation.widgetIndex);
761
- const newTabWidgetId = newTabPanel.widgets[widgetIndex];
762
- const newTabWidget = nineZone.widgets[newTabWidgetId];
763
- newTabWidget.tabs.splice(widgetDef.tabLocation.tabIndex, 0, newTab.id);
764
- }
765
- else {
766
- // Create a new panel widget.
767
- const newWidget = createWidgetState(getUniqueId(), [newTab.id]);
768
- nineZone.widgets[newWidget.id] = castDraft(newWidget);
769
- newTabPanel.widgets.splice(widgetDef.tabLocation.widgetIndex, 0, newWidget.id);
770
- }
779
+ // Add to a panel section.
780
+ const panel = state.panels[tabLocation.side];
781
+ // Add to existing panel section.
782
+ if (panel.widgets.length >= panel.maxWidgetCount) {
783
+ const sectionIndex = Math.min(panel.maxWidgetCount - 1, tabLocation.widgetIndex);
784
+ const sectionId = panel.widgets[sectionIndex];
785
+ return insertTabToWidget(state, tabId, sectionId, tabLocation.tabIndex);
771
786
  }
787
+ // Create a new panel section.
788
+ const newSectionId = getUniqueId();
789
+ return insertPanelWidget(state, panel.side, newSectionId, [tabId], tabLocation.widgetIndex);
772
790
  }
773
791
  /** @internal */
774
- export const setWidgetState = produce((nineZone, widgetDef, state) => {
775
- var _a, _b;
776
- if (state === WidgetState.Open) {
792
+ export function setWidgetState(state, widgetDef, widgetState) {
793
+ if (widgetState === WidgetState.Hidden) {
794
+ return hideWidget(state, widgetDef);
795
+ }
796
+ else if (widgetState === WidgetState.Open) {
777
797
  const id = widgetDef.id;
778
- let location = findTab(nineZone, id);
798
+ let location = getTabLocation(state, id);
779
799
  if (!location) {
780
- addRemovedTab(nineZone, widgetDef);
781
- location = findTab(nineZone, id);
782
- assert(!!location);
783
- }
784
- if (!!widgetDef.tabLocation.floating) {
785
- nineZone.floatingWidgets.byId[(_a = widgetDef.floatingContainerId) !== null && _a !== void 0 ? _a : widgetDef.id].hidden = false;
800
+ state = addHiddenWidget(state, widgetDef);
801
+ location = getTabLocation(state, id);
786
802
  }
787
- const widget = nineZone.widgets[location.widgetId];
788
- widget.minimized = false;
789
- widget.activeTabId = id;
790
- // ensure panel containing widget is not collapsed
791
- // istanbul ignore else
792
- if ("side" in location) {
793
- const panel = nineZone.panels[location.side];
794
- panel.collapsed && (panel.collapsed = false);
795
- // istanbul ignore next
796
- if (undefined === panel.size || 0 === panel.size) {
797
- panel.size = (_b = panel.minSize) !== null && _b !== void 0 ? _b : 200;
803
+ return produce(state, (draft) => {
804
+ var _a;
805
+ assert(!!location);
806
+ const widget = draft.widgets[location.widgetId];
807
+ widget.minimized = false;
808
+ widget.activeTabId = id;
809
+ if (isFloatingTabLocation(location)) {
810
+ const floatingWidget = draft.floatingWidgets.byId[location.floatingWidgetId];
811
+ floatingWidget.hidden = false;
812
+ // istanbul ignore else
798
813
  }
799
- }
814
+ else if (isPanelTabLocation(location)) {
815
+ const panel = draft.panels[location.side];
816
+ panel.collapsed = false;
817
+ if (undefined === panel.size || 0 === panel.size) {
818
+ // istanbul ignore next
819
+ panel.size = (_a = panel.minSize) !== null && _a !== void 0 ? _a : 200;
820
+ }
821
+ }
822
+ });
800
823
  }
801
- else if (state === WidgetState.Closed) {
824
+ else if (widgetState === WidgetState.Closed) {
802
825
  const id = widgetDef.id;
803
- let location = findTab(nineZone, id);
826
+ let location = getTabLocation(state, id);
804
827
  if (!location) {
805
- addRemovedTab(nineZone, widgetDef);
806
- location = findTab(nineZone, id);
807
- assert(!!location);
828
+ state = addHiddenWidget(state, widgetDef);
829
+ location = getTabLocation(state, id);
808
830
  }
809
- if (isFloatingLocation(location)) {
810
- const widget = nineZone.widgets[location.widgetId];
811
- if (id !== widget.activeTabId)
812
- return;
813
- widget.minimized = true;
814
- }
815
- }
816
- else if (state === WidgetState.Hidden) {
817
- hideWidget(nineZone, widgetDef);
831
+ // TODO: should change activeTabId of a widget with multiple tabs.
832
+ return produce(state, (draft) => {
833
+ assert(!!location);
834
+ const widget = draft.widgets[location.widgetId];
835
+ if (isFloatingTabLocation(location)) {
836
+ if (id === widget.activeTabId) {
837
+ widget.minimized = true;
838
+ }
839
+ }
840
+ });
818
841
  }
819
- });
842
+ return state;
843
+ }
820
844
  /** Stores widget location and hides it in the UI. */
821
845
  function hideWidget(state, widgetDef) {
822
- const location = findTab(state, widgetDef.id);
846
+ const location = getTabLocation(state, widgetDef.id);
823
847
  if (!location)
824
- return;
825
- if (isFloatingLocation(location)) {
826
- const widget = state.floatingWidgets.byId[location.widgetId];
848
+ return state;
849
+ // istanbul ignore else
850
+ if (isFloatingTabLocation(location)) {
851
+ const floatingWidget = state.floatingWidgets.byId[location.widgetId];
827
852
  widgetDef.setFloatingContainerId(location.floatingWidgetId);
828
853
  widgetDef.tabLocation = {
829
- side: widget.home.side,
830
- tabIndex: widget.home.widgetIndex,
854
+ side: floatingWidget.home.side,
855
+ tabIndex: floatingWidget.home.widgetIndex,
831
856
  widgetId: widgetDef.id,
832
- widgetIndex: widget.home.widgetIndex,
833
- floating: true,
857
+ widgetIndex: floatingWidget.home.widgetIndex,
858
+ floatingWidget,
834
859
  };
835
860
  }
836
- else
837
- // istanbul ignore else
838
- if (!isPopoutLocation(location)) {
861
+ else if (!isPopoutTabLocation(location)) {
839
862
  const widgetId = location.widgetId;
840
- const side = "side" in location ? location.side : "left";
841
- const widgetIndex = "side" in location ? state.panels[side].widgets.indexOf(widgetId) : 0;
863
+ const side = isPanelTabLocation(location) ? location.side : "left";
864
+ const widgetIndex = isPanelTabLocation(location) ? state.panels[side].widgets.indexOf(widgetId) : 0;
842
865
  const tabIndex = state.widgets[location.widgetId].tabs.indexOf(widgetDef.id);
843
866
  widgetDef.tabLocation = {
844
867
  side,
@@ -847,50 +870,53 @@ function hideWidget(state, widgetDef) {
847
870
  widgetIndex,
848
871
  };
849
872
  }
850
- removeTab(state, widgetDef.id);
873
+ return removeTabFromWidget(state, widgetDef.id);
851
874
  }
852
875
  /** @internal */
853
- export const showWidget = produce((nineZone, id) => {
854
- const location = findTab(nineZone, id);
876
+ export function showWidget(state, id) {
877
+ const location = getTabLocation(state, id);
855
878
  if (!location)
856
- return;
857
- const widget = nineZone.widgets[location.widgetId];
858
- if ("side" in location) {
859
- const panel = nineZone.panels[location.side];
860
- panel.collapsed = false;
861
- widget.minimized = false;
879
+ return state;
880
+ state = produce(state, (draft) => {
881
+ const widget = draft.widgets[location.widgetId];
862
882
  widget.activeTabId = id;
863
- return;
883
+ widget.minimized = false;
884
+ if (isPanelTabLocation(location)) {
885
+ const panel = draft.panels[location.side];
886
+ panel.collapsed = false;
887
+ }
888
+ });
889
+ if (isFloatingTabLocation(location)) {
890
+ state = floatingWidgetBringToFront(state, location.floatingWidgetId);
864
891
  }
865
- widget.activeTabId = id;
866
- widget.minimized = false;
867
- isFloatingLocation(location) && floatingWidgetBringToFront(nineZone, location.floatingWidgetId);
868
- });
892
+ return state;
893
+ }
869
894
  /** @internal */
870
- export const expandWidget = produce((nineZone, id) => {
871
- const location = findTab(nineZone, id);
895
+ export function expandWidget(state, id) {
896
+ const location = getTabLocation(state, id);
872
897
  if (!location)
873
- return;
874
- const widget = nineZone.widgets[location.widgetId];
875
- if ("side" in location) {
876
- const panel = nineZone.panels[location.side];
877
- panel.widgets.forEach((wId) => {
878
- const w = nineZone.widgets[wId];
879
- w.minimized = true;
880
- });
898
+ return state;
899
+ return produce(state, (draft) => {
900
+ const widget = draft.widgets[location.widgetId];
901
+ if (isPanelTabLocation(location)) {
902
+ const panel = draft.panels[location.side];
903
+ panel.widgets.forEach((wId) => {
904
+ const w = draft.widgets[wId];
905
+ w.minimized = true;
906
+ });
907
+ }
881
908
  widget.minimized = false;
882
- return;
883
- }
884
- widget.minimized = false;
885
- return;
886
- });
909
+ });
910
+ }
887
911
  /** @internal */
888
- export const setWidgetLabel = produce((nineZone, id, label) => {
889
- if (!(id in nineZone.tabs))
890
- return;
891
- const tab = nineZone.tabs[id];
892
- tab.label = label;
893
- });
912
+ export function setWidgetLabel(state, id, label) {
913
+ if (!(id in state.tabs))
914
+ return state;
915
+ return produce(state, (draft) => {
916
+ const tab = draft.tabs[id];
917
+ tab.label = label;
918
+ });
919
+ }
894
920
  /** @internal */
895
921
  export function useSavedFrontstageState(frontstageDef) {
896
922
  const uiStateStorage = useUiStateStorageHandler();
@@ -911,20 +937,23 @@ export function useSavedFrontstageState(frontstageDef) {
911
937
  }
912
938
  const id = frontstageDef.id;
913
939
  const version = frontstageDef.version;
914
- const settingsResult = await uiStateStorageRef.current.getSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id));
915
- if (isMountedRef.current) {
916
- if (isFrontstageStateSettingResult(settingsResult) &&
917
- settingsResult.setting.version >= version &&
918
- settingsResult.setting.stateVersion >= stateVersion) {
919
- const restored = restoreNineZoneState(frontstageDef, settingsResult.setting.nineZone);
940
+ const settingResult = await uiStateStorageRef.current.getSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id));
941
+ if (!isMountedRef.current)
942
+ return;
943
+ if (isFrontstageStateSettingResult(settingResult)) {
944
+ const setting = settingResult.setting;
945
+ if (setting.version >= version && setting.stateVersion >= stateVersion) {
946
+ const restored = restoreNineZoneState(frontstageDef, setting.nineZone);
947
+ const savedWidgets = restoreSavedWidgets(setting.widgets, frontstageDef);
920
948
  let state = addMissingWidgets(frontstageDef, restored);
921
- state = removeHiddenWidgets(state, frontstageDef);
949
+ state = hideWidgets(state, frontstageDef);
922
950
  state = processPopoutWidgets(state, frontstageDef);
923
951
  frontstageDef.nineZoneState = state;
952
+ frontstageDef.savedWidgetDefs = savedWidgets;
924
953
  return;
925
954
  }
926
- frontstageDef.nineZoneState = initializeNineZoneState(frontstageDef);
927
955
  }
956
+ frontstageDef.nineZoneState = initializeNineZoneState(frontstageDef);
928
957
  }
929
958
  fetchFrontstageState(); // eslint-disable-line @typescript-eslint/no-floating-promises
930
959
  }, [frontstageDef]);
@@ -933,16 +962,19 @@ export function useSavedFrontstageState(frontstageDef) {
933
962
  export function useSaveFrontstageSettings(frontstageDef) {
934
963
  const nineZone = useNineZoneState(frontstageDef);
935
964
  const uiSettingsStorage = useUiStateStorageHandler();
936
- // eslint-disable-next-line react-hooks/exhaustive-deps
937
- const saveSetting = React.useCallback(debounce(async (id, version, state) => {
938
- const setting = {
939
- id,
940
- nineZone: packNineZoneState(state),
941
- stateVersion,
942
- version,
943
- };
944
- await uiSettingsStorage.saveSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id), setting);
945
- }, 1000), [uiSettingsStorage]);
965
+ const saveSetting = React.useMemo(() => {
966
+ return debounce(async (frontstage, state, savedWidgets) => {
967
+ const id = frontstage.id;
968
+ const setting = {
969
+ id,
970
+ version: frontstage.version,
971
+ stateVersion,
972
+ nineZone: packNineZoneState(state),
973
+ widgets: packSavedWidgets(savedWidgets, frontstage),
974
+ };
975
+ await uiSettingsStorage.saveSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id), setting);
976
+ }, 1000);
977
+ }, [uiSettingsStorage]);
946
978
  React.useEffect(() => {
947
979
  return () => {
948
980
  saveSetting.cancel();
@@ -951,7 +983,8 @@ export function useSaveFrontstageSettings(frontstageDef) {
951
983
  React.useEffect(() => {
952
984
  if (!nineZone || nineZone.draggedTab)
953
985
  return;
954
- saveSetting(frontstageDef.id, frontstageDef.version, nineZone);
986
+ const savedWidgets = frontstageDef.savedWidgetDefs || [];
987
+ saveSetting(frontstageDef, nineZone, savedWidgets);
955
988
  }, [frontstageDef, nineZone, saveSetting]);
956
989
  }
957
990
  /** @internal */
@@ -1085,7 +1118,7 @@ export function useItemsManager(frontstageDef) {
1085
1118
  return;
1086
1119
  state = addMissingWidgets(def, state);
1087
1120
  state = removeMissingWidgets(def, state);
1088
- state = removeHiddenWidgets(state, def); // TODO: should only apply to widgets provided by registered provider in onUiProviderRegisteredEvent.
1121
+ state = hideWidgets(state, def); // TODO: should only apply to widgets provided by registered provider in onUiProviderRegisteredEvent.
1089
1122
  def.nineZoneState = state;
1090
1123
  };
1091
1124
  React.useEffect(() => {
@@ -1098,47 +1131,8 @@ export function useItemsManager(frontstageDef) {
1098
1131
  return remove;
1099
1132
  }, [frontstageDef]);
1100
1133
  }
1101
- function tabShownInCurrentWidget(def, state) {
1102
- for (const [, widget] of Object.entries(state.widgets)) {
1103
- const foundTab = widget.tabs.find((tabId) => tabId === def.id);
1104
- // istanbul ignore else
1105
- if (foundTab)
1106
- return true;
1107
- }
1108
- return false;
1109
- }
1110
1134
  // istanbul ignore next
1111
1135
  function determineNewWidgets(defs, state) {
1112
- // if tab for widget.id is not found or the label does not match widget label (label is set to "undefined" when widgetDef is not initially available)
1113
- return (defs || []).filter((def) => !(def.id in state.tabs) || !tabShownInCurrentWidget(def, state));
1114
- }
1115
- /** @internal */
1116
- export async function saveFrontstagePopoutWidgetSizeAndPosition(state, stageId, stageVersion, childWindowId, childWindow) {
1117
- const location = findWidget(state, childWindowId);
1118
- // istanbul ignore else
1119
- if (location) {
1120
- const adjustmentWidth = ProcessDetector.isElectronAppFrontend ? 16 : 0;
1121
- const adjustmentHeight = ProcessDetector.isElectronAppFrontend ? 39 : 0;
1122
- const newState = produce(state, (draft) => {
1123
- const widget = draft.widgets[childWindowId];
1124
- const tab = draft.tabs[widget.activeTabId];
1125
- tab.preferredPopoutWidgetSize = {
1126
- x: childWindow.screenX,
1127
- y: childWindow.screenY,
1128
- width: childWindow.innerWidth + adjustmentWidth,
1129
- height: childWindow.innerHeight + adjustmentHeight,
1130
- };
1131
- });
1132
- const setting = {
1133
- id: stageId,
1134
- nineZone: packNineZoneState(newState),
1135
- stateVersion,
1136
- version: stageVersion,
1137
- };
1138
- await UiFramework.getUiStateStorage().saveSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(stageId), setting);
1139
- return newState;
1140
- }
1141
- // istanbul ignore next
1142
- return state;
1136
+ return (defs || []).filter((def) => !(def.id in state.tabs));
1143
1137
  }
1144
1138
  //# sourceMappingURL=Frontstage.js.map