@itwin/appui-react 3.4.0-dev.36 → 3.4.0-dev.40

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 +286 -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 +277 -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 };
715
+ }
716
+ if (widgetDef.popoutBounds) {
717
+ draft.popoutBounds = widgetDef.popoutBounds.toProps();
716
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,127 @@ 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, tabIndex } = tabLocation;
759
+ if (widgetId in state.widgets) {
760
+ // Add to an existing widget (by widget id).
761
+ return insertTabToWidget(state, tabId, widgetId, 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);
754
- }
763
+ else if (tabLocation.floatingWidget) {
764
+ const floatingWidget = tabLocation.floatingWidget;
765
+ assert(widgetId === floatingWidget.id);
766
+ // Add to a new floating widget.
767
+ const nzBounds = Rectangle.createFromSize(state.size);
768
+ const bounds = Rectangle.create(floatingWidget.bounds).containIn(nzBounds);
769
+ return addFloatingWidget(state, floatingWidget.id, [tabId], {
770
+ ...floatingWidget,
771
+ bounds: bounds.toProps(),
772
+ });
755
773
  }
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
- }
774
+ // Add to a panel section.
775
+ const panel = state.panels[tabLocation.side];
776
+ // Add to existing panel section.
777
+ if (panel.widgets.length >= panel.maxWidgetCount) {
778
+ const sectionIndex = Math.min(panel.maxWidgetCount - 1, tabLocation.widgetIndex);
779
+ const sectionId = panel.widgets[sectionIndex];
780
+ return insertTabToWidget(state, tabId, sectionId, tabLocation.tabIndex);
771
781
  }
782
+ // Create a new panel section.
783
+ const newSectionId = getUniqueId();
784
+ return insertPanelWidget(state, panel.side, newSectionId, [tabId], tabLocation.widgetIndex);
772
785
  }
773
786
  /** @internal */
774
- export const setWidgetState = produce((nineZone, widgetDef, state) => {
787
+ export function setWidgetState(state, widgetDef, widgetState) {
775
788
  var _a, _b;
776
- if (state === WidgetState.Open) {
789
+ if (widgetState === WidgetState.Hidden) {
790
+ return hideWidget(state, widgetDef);
791
+ }
792
+ else if (widgetState === WidgetState.Open) {
777
793
  const id = widgetDef.id;
778
- let location = findTab(nineZone, id);
794
+ let location = getTabLocation(state, id);
779
795
  if (!location) {
780
- addRemovedTab(nineZone, widgetDef);
781
- location = findTab(nineZone, id);
782
- assert(!!location);
796
+ state = addHiddenWidget(state, widgetDef);
797
+ location = getTabLocation(state, id);
783
798
  }
784
- if (!!widgetDef.tabLocation.floating) {
785
- nineZone.floatingWidgets.byId[(_a = widgetDef.floatingContainerId) !== null && _a !== void 0 ? _a : widgetDef.id].hidden = false;
786
- }
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;
799
+ return produce(state, (draft) => {
800
+ var _a;
801
+ assert(!!location);
802
+ const widget = draft.widgets[location.widgetId];
803
+ widget.minimized = false;
804
+ widget.activeTabId = id;
805
+ if (isFloatingTabLocation(location)) {
806
+ const floatingWidget = draft.floatingWidgets.byId[location.floatingWidgetId];
807
+ floatingWidget.hidden = false;
808
+ // istanbul ignore else
798
809
  }
799
- }
810
+ else if (isPanelTabLocation(location)) {
811
+ const panel = draft.panels[location.side];
812
+ panel.collapsed = false;
813
+ if (undefined === panel.size || 0 === panel.size) {
814
+ // istanbul ignore next
815
+ panel.size = (_a = panel.minSize) !== null && _a !== void 0 ? _a : 200;
816
+ }
817
+ }
818
+ });
800
819
  }
801
- else if (state === WidgetState.Closed) {
820
+ else if (widgetState === WidgetState.Closed) {
802
821
  const id = widgetDef.id;
803
- let location = findTab(nineZone, id);
822
+ let location = getTabLocation(state, id);
804
823
  if (!location) {
805
- addRemovedTab(nineZone, widgetDef);
806
- location = findTab(nineZone, id);
807
- assert(!!location);
808
- }
809
- if (isFloatingLocation(location)) {
810
- const widget = nineZone.widgets[location.widgetId];
811
- if (id !== widget.activeTabId)
812
- return;
813
- widget.minimized = true;
824
+ state = addHiddenWidget(state, widgetDef);
825
+ location = getTabLocation(state, id);
814
826
  }
827
+ // TODO: should change activeTabId of a widget with multiple tabs.
828
+ return produce(state, (draft) => {
829
+ assert(!!location);
830
+ const widget = draft.widgets[location.widgetId];
831
+ if (isFloatingTabLocation(location)) {
832
+ if (id === widget.activeTabId) {
833
+ widget.minimized = true;
834
+ }
835
+ }
836
+ });
815
837
  }
816
- else if (state === WidgetState.Hidden) {
817
- hideWidget(nineZone, widgetDef);
838
+ else if (widgetState === WidgetState.Floating) {
839
+ const id = widgetDef.id;
840
+ let location = getTabLocation(state, id);
841
+ if (!location) {
842
+ state = addHiddenWidget(state, widgetDef);
843
+ location = getTabLocation(state, id);
844
+ }
845
+ assert(!!location);
846
+ if (isFloatingTabLocation(location))
847
+ return state;
848
+ // Widget is not floating.
849
+ state = removeTabFromWidget(state, id);
850
+ const bounds = (_b = (_a = widgetDef.tabLocation) === null || _a === void 0 ? void 0 : _a.floatingWidget) === null || _b === void 0 ? void 0 : _b.bounds;
851
+ state = addFloatingWidget(state, getUniqueId(), [id], { bounds });
818
852
  }
819
- });
853
+ return state;
854
+ }
820
855
  /** Stores widget location and hides it in the UI. */
821
856
  function hideWidget(state, widgetDef) {
822
- const location = findTab(state, widgetDef.id);
857
+ const location = getTabLocation(state, widgetDef.id);
823
858
  if (!location)
824
- return;
825
- if (isFloatingLocation(location)) {
826
- const widget = state.floatingWidgets.byId[location.widgetId];
859
+ return state;
860
+ // istanbul ignore else
861
+ if (isFloatingTabLocation(location)) {
862
+ const floatingWidget = state.floatingWidgets.byId[location.widgetId];
827
863
  widgetDef.setFloatingContainerId(location.floatingWidgetId);
828
864
  widgetDef.tabLocation = {
829
- side: widget.home.side,
830
- tabIndex: widget.home.widgetIndex,
831
- widgetId: widgetDef.id,
832
- widgetIndex: widget.home.widgetIndex,
833
- floating: true,
865
+ side: floatingWidget.home.side,
866
+ tabIndex: floatingWidget.home.widgetIndex,
867
+ widgetId: floatingWidget.id,
868
+ widgetIndex: floatingWidget.home.widgetIndex,
869
+ floatingWidget,
834
870
  };
835
871
  }
836
- else
837
- // istanbul ignore else
838
- if (!isPopoutLocation(location)) {
872
+ else if (!isPopoutTabLocation(location)) {
839
873
  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;
874
+ const side = isPanelTabLocation(location) ? location.side : "left";
875
+ const widgetIndex = isPanelTabLocation(location) ? state.panels[side].widgets.indexOf(widgetId) : 0;
842
876
  const tabIndex = state.widgets[location.widgetId].tabs.indexOf(widgetDef.id);
843
877
  widgetDef.tabLocation = {
844
878
  side,
@@ -847,50 +881,53 @@ function hideWidget(state, widgetDef) {
847
881
  widgetIndex,
848
882
  };
849
883
  }
850
- removeTab(state, widgetDef.id);
884
+ return removeTabFromWidget(state, widgetDef.id);
851
885
  }
852
886
  /** @internal */
853
- export const showWidget = produce((nineZone, id) => {
854
- const location = findTab(nineZone, id);
887
+ export function showWidget(state, id) {
888
+ const location = getTabLocation(state, id);
855
889
  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;
890
+ return state;
891
+ state = produce(state, (draft) => {
892
+ const widget = draft.widgets[location.widgetId];
862
893
  widget.activeTabId = id;
863
- return;
894
+ widget.minimized = false;
895
+ if (isPanelTabLocation(location)) {
896
+ const panel = draft.panels[location.side];
897
+ panel.collapsed = false;
898
+ }
899
+ });
900
+ if (isFloatingTabLocation(location)) {
901
+ state = floatingWidgetBringToFront(state, location.floatingWidgetId);
864
902
  }
865
- widget.activeTabId = id;
866
- widget.minimized = false;
867
- isFloatingLocation(location) && floatingWidgetBringToFront(nineZone, location.floatingWidgetId);
868
- });
903
+ return state;
904
+ }
869
905
  /** @internal */
870
- export const expandWidget = produce((nineZone, id) => {
871
- const location = findTab(nineZone, id);
906
+ export function expandWidget(state, id) {
907
+ const location = getTabLocation(state, id);
872
908
  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
- });
909
+ return state;
910
+ return produce(state, (draft) => {
911
+ const widget = draft.widgets[location.widgetId];
912
+ if (isPanelTabLocation(location)) {
913
+ const panel = draft.panels[location.side];
914
+ panel.widgets.forEach((wId) => {
915
+ const w = draft.widgets[wId];
916
+ w.minimized = true;
917
+ });
918
+ }
881
919
  widget.minimized = false;
882
- return;
883
- }
884
- widget.minimized = false;
885
- return;
886
- });
920
+ });
921
+ }
887
922
  /** @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
- });
923
+ export function setWidgetLabel(state, id, label) {
924
+ if (!(id in state.tabs))
925
+ return state;
926
+ return produce(state, (draft) => {
927
+ const tab = draft.tabs[id];
928
+ tab.label = label;
929
+ });
930
+ }
894
931
  /** @internal */
895
932
  export function useSavedFrontstageState(frontstageDef) {
896
933
  const uiStateStorage = useUiStateStorageHandler();
@@ -911,20 +948,23 @@ export function useSavedFrontstageState(frontstageDef) {
911
948
  }
912
949
  const id = frontstageDef.id;
913
950
  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);
951
+ const settingResult = await uiStateStorageRef.current.getSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id));
952
+ if (!isMountedRef.current)
953
+ return;
954
+ if (isFrontstageStateSettingResult(settingResult)) {
955
+ const setting = settingResult.setting;
956
+ if (setting.version >= version && setting.stateVersion >= stateVersion) {
957
+ const restored = restoreNineZoneState(frontstageDef, setting.nineZone);
958
+ const savedWidgets = restoreSavedWidgets(setting.widgets, frontstageDef);
920
959
  let state = addMissingWidgets(frontstageDef, restored);
921
- state = removeHiddenWidgets(state, frontstageDef);
960
+ state = hideWidgets(state, frontstageDef);
922
961
  state = processPopoutWidgets(state, frontstageDef);
923
962
  frontstageDef.nineZoneState = state;
963
+ frontstageDef.savedWidgetDefs = savedWidgets;
924
964
  return;
925
965
  }
926
- frontstageDef.nineZoneState = initializeNineZoneState(frontstageDef);
927
966
  }
967
+ frontstageDef.nineZoneState = initializeNineZoneState(frontstageDef);
928
968
  }
929
969
  fetchFrontstageState(); // eslint-disable-line @typescript-eslint/no-floating-promises
930
970
  }, [frontstageDef]);
@@ -933,16 +973,19 @@ export function useSavedFrontstageState(frontstageDef) {
933
973
  export function useSaveFrontstageSettings(frontstageDef) {
934
974
  const nineZone = useNineZoneState(frontstageDef);
935
975
  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]);
976
+ const saveSetting = React.useMemo(() => {
977
+ return debounce(async (frontstage, state, savedWidgets) => {
978
+ const id = frontstage.id;
979
+ const setting = {
980
+ id,
981
+ version: frontstage.version,
982
+ stateVersion,
983
+ nineZone: packNineZoneState(state),
984
+ widgets: packSavedWidgets(savedWidgets, frontstage),
985
+ };
986
+ await uiSettingsStorage.saveSetting(FRONTSTAGE_SETTINGS_NAMESPACE, getFrontstageStateSettingName(id), setting);
987
+ }, 1000);
988
+ }, [uiSettingsStorage]);
946
989
  React.useEffect(() => {
947
990
  return () => {
948
991
  saveSetting.cancel();
@@ -951,7 +994,8 @@ export function useSaveFrontstageSettings(frontstageDef) {
951
994
  React.useEffect(() => {
952
995
  if (!nineZone || nineZone.draggedTab)
953
996
  return;
954
- saveSetting(frontstageDef.id, frontstageDef.version, nineZone);
997
+ const savedWidgets = frontstageDef.savedWidgetDefs || [];
998
+ saveSetting(frontstageDef, nineZone, savedWidgets);
955
999
  }, [frontstageDef, nineZone, saveSetting]);
956
1000
  }
957
1001
  /** @internal */
@@ -1085,7 +1129,7 @@ export function useItemsManager(frontstageDef) {
1085
1129
  return;
1086
1130
  state = addMissingWidgets(def, state);
1087
1131
  state = removeMissingWidgets(def, state);
1088
- state = removeHiddenWidgets(state, def); // TODO: should only apply to widgets provided by registered provider in onUiProviderRegisteredEvent.
1132
+ state = hideWidgets(state, def); // TODO: should only apply to widgets provided by registered provider in onUiProviderRegisteredEvent.
1089
1133
  def.nineZoneState = state;
1090
1134
  };
1091
1135
  React.useEffect(() => {
@@ -1098,47 +1142,8 @@ export function useItemsManager(frontstageDef) {
1098
1142
  return remove;
1099
1143
  }, [frontstageDef]);
1100
1144
  }
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
1145
  // istanbul ignore next
1111
1146
  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;
1147
+ return (defs || []).filter((def) => !(def.id in state.tabs));
1143
1148
  }
1144
1149
  //# sourceMappingURL=Frontstage.js.map