pne-ui 3.0.19 → 3.0.20

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 (30) hide show
  1. package/cjs/component/fab/PneFloatingActionButtons.d.ts +17 -4
  2. package/cjs/component/fab/PneFloatingActionButtons.js +39 -17
  3. package/cjs/component/fab/PneFloatingActionButtons.js.map +1 -1
  4. package/cjs/component/widget-board/PneLayoutsPanel.d.ts +18 -0
  5. package/cjs/component/widget-board/PneLayoutsPanel.js +86 -0
  6. package/cjs/component/widget-board/PneLayoutsPanel.js.map +1 -0
  7. package/cjs/component/widget-board/WidgetBoard.js +173 -43
  8. package/cjs/component/widget-board/WidgetBoard.js.map +1 -1
  9. package/cjs/component/widget-board/index.d.ts +1 -0
  10. package/cjs/component/widget-board/index.js +1 -0
  11. package/cjs/component/widget-board/index.js.map +1 -1
  12. package/cjs/component/widget-board/types.d.ts +15 -8
  13. package/cjs/exports/fab.d.ts +1 -1
  14. package/cjs/exports/fab.js.map +1 -1
  15. package/esm/component/fab/PneFloatingActionButtons.d.ts +17 -4
  16. package/esm/component/fab/PneFloatingActionButtons.js +41 -19
  17. package/esm/component/fab/PneFloatingActionButtons.js.map +1 -1
  18. package/esm/component/widget-board/PneLayoutsPanel.d.ts +18 -0
  19. package/esm/component/widget-board/PneLayoutsPanel.js +81 -0
  20. package/esm/component/widget-board/PneLayoutsPanel.js.map +1 -0
  21. package/esm/component/widget-board/WidgetBoard.js +175 -45
  22. package/esm/component/widget-board/WidgetBoard.js.map +1 -1
  23. package/esm/component/widget-board/index.d.ts +1 -0
  24. package/esm/component/widget-board/index.js +1 -0
  25. package/esm/component/widget-board/index.js.map +1 -1
  26. package/esm/component/widget-board/types.d.ts +15 -8
  27. package/esm/exports/fab.d.ts +1 -1
  28. package/esm/exports/fab.js +1 -1
  29. package/esm/exports/fab.js.map +1 -1
  30. package/package.json +2 -2
@@ -7,8 +7,18 @@ export type PneFabAction = {
7
7
  disabled?: boolean;
8
8
  tooltip?: string;
9
9
  };
10
+ export type PneFabContent = {
11
+ id: string;
12
+ kind: 'content';
13
+ node: React.ReactNode;
14
+ };
15
+ export type PneFabDivider = {
16
+ id: string;
17
+ kind: 'divider';
18
+ };
19
+ export type PneFabItem = PneFabAction | PneFabContent | PneFabDivider;
10
20
  export type PneFloatingActionButtonsProps = {
11
- actions: PneFabAction[];
21
+ actions: PneFabItem[];
12
22
  breakpoints?: readonly number[];
13
23
  mobileBreakpoint?: number;
14
24
  position?: {
@@ -18,10 +28,13 @@ export type PneFloatingActionButtonsProps = {
18
28
  fabLabel?: React.ReactNode;
19
29
  fabIcon?: React.ReactNode;
20
30
  className?: string;
31
+ bannerText?: React.ReactNode;
21
32
  };
22
33
  /**
23
- * Responsive FAB menu: shows a single floating button with a menu on small screens,
24
- * and a sticky action rail on larger screens. Breakpoint set defaults to 6-step layout.
34
+ * Floating action menu:
35
+ * - on mobile: actions/content rendered inside a Menu
36
+ * - on desktop: actions rendered as floating Fabs stacked above the trigger; Menu still opens for content/banner
37
+ * Actions array may include content blocks (`{ kind: 'content', node: <...> }`) to embed custom UI.
25
38
  */
26
- export declare function PneFloatingActionButtons({ actions, breakpoints, mobileBreakpoint, position, fabLabel, fabIcon, className, }: PneFloatingActionButtonsProps): React.JSX.Element;
39
+ export declare function PneFloatingActionButtons({ actions, breakpoints, mobileBreakpoint, position, fabLabel, fabIcon, className, bannerText, }: PneFloatingActionButtonsProps): React.JSX.Element;
27
40
  export default PneFloatingActionButtons;
@@ -3,15 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PneFloatingActionButtons = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importStar(require("react"));
6
- const Add_1 = tslib_1.__importDefault(require("@mui/icons-material/Add"));
6
+ const Edit_1 = tslib_1.__importDefault(require("@mui/icons-material/Edit"));
7
7
  const material_1 = require("@mui/material");
8
8
  const breakpoints_1 = require("../../common/responsive/breakpoints");
9
9
  const useBreakpoint_1 = require("../responsive/useBreakpoint");
10
+ const isContentItem = (item) => 'kind' in item && item.kind === 'content';
11
+ const isDividerItem = (item) => 'kind' in item && item.kind === 'divider';
12
+ const isActionItem = (item) => !isContentItem(item) && !isDividerItem(item);
10
13
  /**
11
- * Responsive FAB menu: shows a single floating button with a menu on small screens,
12
- * and a sticky action rail on larger screens. Breakpoint set defaults to 6-step layout.
14
+ * Floating action menu:
15
+ * - on mobile: actions/content rendered inside a Menu
16
+ * - on desktop: actions rendered as floating Fabs stacked above the trigger; Menu still opens for content/banner
17
+ * Actions array may include content blocks (`{ kind: 'content', node: <...> }`) to embed custom UI.
13
18
  */
14
- function PneFloatingActionButtons({ actions, breakpoints = breakpoints_1.DEFAULT_BREAKPOINTS, mobileBreakpoint = 800, position = { bottom: 24, right: 24 }, fabLabel = 'Actions', fabIcon = react_1.default.createElement(Add_1.default, null), className, }) {
19
+ function PneFloatingActionButtons({ actions, breakpoints = breakpoints_1.DEFAULT_BREAKPOINTS, mobileBreakpoint = 800, position = { bottom: 24, right: 24 }, fabLabel = 'Actions', fabIcon = react_1.default.createElement(Edit_1.default, null), className, bannerText, }) {
15
20
  const breakpoint = (0, useBreakpoint_1.useBreakpoint)({ breakpoints });
16
21
  const isMobile = breakpoint <= mobileBreakpoint;
17
22
  const [anchorEl, setAnchorEl] = (0, react_1.useState)(null);
@@ -21,20 +26,37 @@ function PneFloatingActionButtons({ actions, breakpoints = breakpoints_1.DEFAULT
21
26
  handleClose();
22
27
  action.onClick();
23
28
  };
24
- const renderedActions = (0, react_1.useMemo)(() => actions.map(action => (react_1.default.createElement(material_1.Tooltip, { key: action.id, title: action.tooltip ?? '', placement: 'left' },
25
- react_1.default.createElement("span", null,
26
- react_1.default.createElement(material_1.Button, { onClick: () => handleAction(action), startIcon: action.icon, disabled: action.disabled, fullWidth: true, variant: 'contained', color: 'primary', size: 'small', sx: { justifyContent: 'flex-start', textTransform: 'none' } }, action.label))))), [actions]);
27
- if (isMobile) {
28
- return (react_1.default.createElement(material_1.Box, { position: 'fixed', bottom: position.bottom ?? 24, right: position.right ?? 24, zIndex: 1300, className: className },
29
- react_1.default.createElement(material_1.Tooltip, { title: fabLabel },
30
- react_1.default.createElement(material_1.Fab, { color: 'primary', onClick: handleOpen, "aria-label": typeof fabLabel === 'string' ? fabLabel : 'Actions' }, fabIcon)),
31
- react_1.default.createElement(material_1.Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: handleClose, anchorOrigin: { vertical: 'top', horizontal: 'left' } }, actions.map(action => (react_1.default.createElement(material_1.MenuItem, { key: action.id, disabled: action.disabled, onClick: () => handleAction(action) },
32
- action.icon ? react_1.default.createElement(material_1.ListItemIcon, null, action.icon) : null,
33
- react_1.default.createElement(material_1.ListItemText, null, action.label)))))));
34
- }
29
+ const renderMenuItems = (items) => items.map(item => isContentItem(item) ? (react_1.default.createElement(material_1.Box, { key: item.id }, item.node)) : isDividerItem(item) ? (react_1.default.createElement(material_1.Divider, { key: item.id, component: 'li', role: 'presentation', sx: { my: 2 } })) : isActionItem(item) ? (react_1.default.createElement(material_1.MenuItem, { key: item.id, disabled: item.disabled, onClick: () => handleAction(item), sx: { fontSize: '0.875rem', display: 'flex', alignItems: 'center', gap: 1 } },
30
+ react_1.default.createElement(material_1.ListItemText, { primaryTypographyProps: { fontSize: '0.875rem' }, sx: { flex: 1 } }, item.label),
31
+ item.icon ? (react_1.default.createElement(material_1.Box, { component: 'span', sx: { display: 'inline-flex', color: 'inherit', lineHeight: 0 } }, item.icon)) : null)) : null);
32
+ const menuItems = isMobile ? actions : actions.filter(item => !isActionItem(item));
33
+ const actionItems = isMobile ? [] : actions.filter(isActionItem);
35
34
  return (react_1.default.createElement(material_1.Box, { position: 'fixed', bottom: position.bottom ?? 24, right: position.right ?? 24, zIndex: 1300, className: className },
36
- react_1.default.createElement(material_1.Paper, { elevation: 3, sx: { borderRadius: 2, p: 1, minWidth: 200 } },
37
- react_1.default.createElement(material_1.Stack, { spacing: 1 }, renderedActions))));
35
+ react_1.default.createElement(material_1.Stack, { spacing: 1, alignItems: 'flex-end' },
36
+ !isMobile
37
+ ? actionItems.map(item => {
38
+ const title = item.tooltip ?? (typeof item.label === 'string' ? item.label : typeof fabLabel === 'string' ? fabLabel : 'Action');
39
+ return (react_1.default.createElement(material_1.Tooltip, { key: item.id, title: title, placement: 'left' },
40
+ react_1.default.createElement("span", null,
41
+ react_1.default.createElement(material_1.Fab, { color: 'primary', onClick: () => handleAction(item), "aria-label": typeof title === 'string' ? title : 'Action' }, item.icon ?? (typeof item.label === 'string' ? item.label.charAt(0) : fabIcon)))));
42
+ })
43
+ : null,
44
+ react_1.default.createElement(material_1.Tooltip, { title: fabLabel },
45
+ react_1.default.createElement(material_1.Fab, { color: 'primary', onClick: handleOpen, "aria-label": typeof fabLabel === 'string' ? fabLabel : 'Actions' }, fabIcon))),
46
+ react_1.default.createElement(material_1.Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: handleClose, anchorOrigin: { vertical: 'top', horizontal: 'left' }, MenuListProps: { sx: { py: 0 } } },
47
+ renderMenuItems(menuItems),
48
+ bannerText ? (react_1.default.createElement(material_1.MenuItem, { disabled: true, sx: {
49
+ pointerEvents: 'none',
50
+ bgcolor: theme => theme.palette.primary.main,
51
+ color: theme => theme.palette.primary.contrastText,
52
+ '&.Mui-disabled': { opacity: 1 },
53
+ minHeight: 60,
54
+ } },
55
+ react_1.default.createElement(material_1.ListItemIcon, { sx: { minWidth: 36, color: 'inherit' } }, fabIcon),
56
+ react_1.default.createElement(material_1.ListItemText, { primaryTypographyProps: {
57
+ fontWeight: 600,
58
+ fontSize: '20px',
59
+ } }, bannerText))) : null)));
38
60
  }
39
61
  exports.PneFloatingActionButtons = PneFloatingActionButtons;
40
62
  exports.default = PneFloatingActionButtons;
@@ -1 +1 @@
1
- {"version":3,"file":"PneFloatingActionButtons.js","sourceRoot":"","sources":["../../../src/component/fab/PneFloatingActionButtons.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PneFloatingActionButtons.js","sourceRoot":"","sources":["../../../src/component/fab/PneFloatingActionButtons.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ export type PneLayoutOption = {
3
+ id: string;
4
+ name: string;
5
+ };
6
+ export type PneLayoutsPanelProps = {
7
+ items: PneLayoutOption[];
8
+ selectedId?: string;
9
+ onSelect?: (id: string) => void;
10
+ onDelete?: (id: string) => void;
11
+ onUpdate?: (id: string) => void;
12
+ onAdd?: (name: string) => void;
13
+ title?: React.ReactNode;
14
+ addLabel?: React.ReactNode;
15
+ className?: string;
16
+ };
17
+ export declare const PneLayoutsPanel: React.FC<PneLayoutsPanelProps>;
18
+ export default PneLayoutsPanel;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PneLayoutsPanel = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importStar(require("react"));
6
+ const Delete_1 = tslib_1.__importDefault(require("@mui/icons-material/Delete"));
7
+ const Refresh_1 = tslib_1.__importDefault(require("@mui/icons-material/Refresh"));
8
+ const material_1 = require("@mui/material");
9
+ const PneModal_1 = tslib_1.__importDefault(require("../PneModal"));
10
+ const PneTextField_1 = tslib_1.__importDefault(require("../PneTextField"));
11
+ const PneButton_1 = tslib_1.__importDefault(require("../PneButton"));
12
+ const defaultTitle = 'Layouts';
13
+ const defaultAddLabel = 'Add new layout';
14
+ const PneLayoutsPanel = ({ items, selectedId, onSelect, onDelete, onUpdate, onAdd, title = defaultTitle, addLabel = defaultAddLabel, className, }) => {
15
+ const [modalOpen, setModalOpen] = (0, react_1.useState)(false);
16
+ const [name, setName] = (0, react_1.useState)('');
17
+ const handleSave = () => {
18
+ const trimmed = name.trim();
19
+ if (!trimmed || !onAdd)
20
+ return;
21
+ onAdd(trimmed);
22
+ setName('');
23
+ setModalOpen(false);
24
+ };
25
+ const hasAdd = Boolean(onAdd);
26
+ return (react_1.default.createElement(material_1.Box, { sx: { width: 320, maxWidth: '100%', bgcolor: '#fff', fontSize: '14px', lineHeight: '20px' }, className: className },
27
+ react_1.default.createElement(material_1.Box, { sx: {
28
+ bgcolor: '#fff',
29
+ color: '#8A94A6',
30
+ px: 2,
31
+ minHeight: 32,
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ borderBottom: '1px solid #0000001F',
35
+ } },
36
+ react_1.default.createElement(material_1.Typography, { variant: 'subtitle2', sx: { lineHeight: '20px', fontWeight: 400, fontSize: '14px' } }, title)),
37
+ react_1.default.createElement(material_1.Stack, null, items.length === 0 ? (react_1.default.createElement(material_1.Box, { sx: { px: 2, minHeight: 32, display: 'flex', alignItems: 'center', color: 'text.secondary' } }, "No layouts yet")) : (items.map(item => {
38
+ const selected = item.id === selectedId;
39
+ return (react_1.default.createElement(material_1.Box, { key: item.id, onClick: () => onSelect?.(item.id), sx: {
40
+ display: 'flex',
41
+ alignItems: 'center',
42
+ gap: 0.5,
43
+ px: 2,
44
+ minHeight: 32,
45
+ cursor: onSelect ? 'pointer' : 'default',
46
+ bgcolor: selected ? '#EFF2F5' : '#fff',
47
+ color: selected ? 'primary.main' : 'text.primary',
48
+ transition: 'background-color 0.2s ease, color 0.2s ease',
49
+ } },
50
+ react_1.default.createElement(material_1.Box, { sx: { flex: 1, fontWeight: 400, fontSize: '14px', lineHeight: '20px' } }, item.name),
51
+ onUpdate && selected ? (react_1.default.createElement(material_1.IconButton, { size: 'small', color: 'inherit', onClick: event => {
52
+ event.stopPropagation();
53
+ onUpdate(item.id);
54
+ } },
55
+ react_1.default.createElement(Refresh_1.default, { fontSize: 'small' }))) : null,
56
+ onDelete ? (react_1.default.createElement(material_1.IconButton, { size: 'small', color: 'inherit', onClick: event => {
57
+ event.stopPropagation();
58
+ onDelete(item.id);
59
+ } },
60
+ react_1.default.createElement(Delete_1.default, { fontSize: 'small' }))) : null));
61
+ }))),
62
+ hasAdd ? (react_1.default.createElement(material_1.Box, null,
63
+ react_1.default.createElement(PneButton_1.default, { fullWidth: true, pneStyle: 'text', size: 'small', onClick: () => setModalOpen(true), sx: {
64
+ height: 32,
65
+ justifyContent: 'flex-start',
66
+ px: 2,
67
+ fontSize: '14px',
68
+ lineHeight: '20px',
69
+ color: 'primary.main',
70
+ textDecoration: 'none',
71
+ minWidth: 0,
72
+ '&:hover': {
73
+ backgroundColor: 'transparent',
74
+ textDecoration: 'none',
75
+ },
76
+ } }, addLabel))) : null,
77
+ hasAdd ? (react_1.default.createElement(PneModal_1.default, { open: modalOpen, onClose: () => setModalOpen(false), title: 'New layout' },
78
+ react_1.default.createElement(material_1.Stack, { spacing: 2 },
79
+ react_1.default.createElement(PneTextField_1.default, { label: 'Template name', fullWidth: true, value: name, onChange: event => setName(event.target.value), autoFocus: true }),
80
+ react_1.default.createElement(material_1.Box, { sx: { display: 'flex', gap: 1, justifyContent: 'flex-end' } },
81
+ react_1.default.createElement(PneButton_1.default, { pneStyle: 'text', size: 'small', onClick: () => setModalOpen(false), sx: { height: 32, fontSize: '14px', lineHeight: '20px' } }, "Cancel"),
82
+ react_1.default.createElement(PneButton_1.default, { pneStyle: 'contained', size: 'small', onClick: handleSave, disabled: !name.trim(), sx: { height: 32, fontSize: '14px', lineHeight: '20px' } }, "Save"))))) : null));
83
+ };
84
+ exports.PneLayoutsPanel = PneLayoutsPanel;
85
+ exports.default = exports.PneLayoutsPanel;
86
+ //# sourceMappingURL=PneLayoutsPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PneLayoutsPanel.js","sourceRoot":"","sources":["../../../src/component/widget-board/PneLayoutsPanel.tsx"],"names":[],"mappings":"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"}
@@ -8,9 +8,7 @@ const board_item_1 = tslib_1.__importDefault(require("@cloudscape-design/board-c
8
8
  const Close_1 = tslib_1.__importDefault(require("@mui/icons-material/Close"));
9
9
  const ExpandLess_1 = tslib_1.__importDefault(require("@mui/icons-material/ExpandLess"));
10
10
  const ExpandMore_1 = tslib_1.__importDefault(require("@mui/icons-material/ExpandMore"));
11
- const RestartAlt_1 = tslib_1.__importDefault(require("@mui/icons-material/RestartAlt"));
12
11
  const material_1 = require("@mui/material");
13
- const PneButton_1 = tslib_1.__importDefault(require("../PneButton"));
14
12
  const CloudscapeBoardStyles_1 = require("../cloudscape/CloudscapeBoardStyles");
15
13
  const CloudscapeThemeProvider_1 = require("../cloudscape/CloudscapeThemeProvider");
16
14
  const boardI18n_1 = require("../cloudscape/boardI18n");
@@ -87,20 +85,168 @@ const getLayoutConfigForWidth = (width, layoutMap, breakpoints) => {
87
85
  const breakpoint = resolveBreakpoint(width, breakpoints);
88
86
  return { breakpoint, layout: layoutMap[breakpoint] };
89
87
  };
90
- const buildStoragePayload = (state) => JSON.stringify({
88
+ const buildStoragePayload = (state) => ({
91
89
  items: state.items.map(({ id, columnSpan, columnOffset, rowSpan }) => ({ id, columnSpan, columnOffset, rowSpan })),
92
90
  hidden: state.hidden,
93
91
  collapsed: state.collapsed,
94
92
  sizeMemory: state.sizeMemory,
95
93
  });
96
- const hydrateStoragePayload = (raw) => JSON.parse(raw);
97
- exports.WidgetBoard = (0, react_1.forwardRef)(function WidgetBoard({ widgets, layoutByBreakpoint, breakpoints: customBreakpoints, storageKey = 'pne-widget-board-layout', loadRemoteLayout, controls, empty, hideNavigationArrows = true, onLayoutPersist, isWidgetEnabled, className, }, ref) {
98
- const breakpoints = (0, react_1.useMemo)(() => customBreakpoints ?? Object.keys(layoutByBreakpoint).map(Number).sort((a, b) => a - b), [customBreakpoints, layoutByBreakpoint]);
94
+ const hydrateStoragePayload = (raw) => (typeof raw === 'string' ? JSON.parse(raw) : raw);
95
+ const readPersistedLayouts = (storageKey) => {
96
+ if (typeof window === 'undefined')
97
+ return null;
98
+ const raw = window.localStorage.getItem(storageKey);
99
+ if (!raw)
100
+ return null;
101
+ try {
102
+ const parsed = JSON.parse(raw);
103
+ if (parsed && typeof parsed === 'object' && 'layouts' in parsed) {
104
+ const layouts = parsed.layouts ?? {};
105
+ const selectedLayoutId = parsed.selectedLayoutId;
106
+ return { layouts, selectedLayoutId };
107
+ }
108
+ if (parsed && typeof parsed === 'object' && 'items' in parsed) {
109
+ return {
110
+ layouts: { __legacy: parsed },
111
+ selectedLayoutId: '__legacy',
112
+ };
113
+ }
114
+ }
115
+ catch {
116
+ // ignore corrupt storage payloads
117
+ }
118
+ return null;
119
+ };
120
+ const buildLayoutOptions = (options, layoutByBreakpoint) => {
121
+ if (options?.length) {
122
+ return options.map(option => ({
123
+ ...option,
124
+ preset: option.preset ?? {
125
+ layoutByBreakpoint,
126
+ source: 'static',
127
+ },
128
+ }));
129
+ }
130
+ return [
131
+ {
132
+ id: 'default',
133
+ name: 'Default layout',
134
+ preset: {
135
+ layoutByBreakpoint,
136
+ source: 'static',
137
+ },
138
+ },
139
+ ];
140
+ };
141
+ const layoutOptionsEqual = (a, b) => a.length === b.length && a.every((option, index) => option.id === b[index]?.id && option.name === b[index]?.name && option.preset === b[index]?.preset);
142
+ exports.WidgetBoard = (0, react_1.forwardRef)(function WidgetBoard({ widgets, layoutByBreakpoint, breakpoints: customBreakpoints, storageKey = 'pne-widget-board-layout', loadLayouts, layouts, empty, hideNavigationArrows = true, onLayoutPersist, isWidgetEnabled, className, }, ref) {
143
+ const initialPersistedLayouts = typeof window !== 'undefined' ? readPersistedLayouts(storageKey) : null;
144
+ const persistedLayoutsRef = (0, react_1.useRef)(initialPersistedLayouts ?? { layouts: {} });
145
+ const [layoutOptions, setLayoutOptions] = (0, react_1.useState)(() => buildLayoutOptions(layouts?.options, layoutByBreakpoint));
146
+ (0, react_1.useEffect)(() => {
147
+ if (loadLayouts)
148
+ return;
149
+ const nextOptions = buildLayoutOptions(layouts?.options, layoutByBreakpoint);
150
+ setLayoutOptions(prev => (layoutOptionsEqual(prev, nextOptions) ? prev : nextOptions));
151
+ }, [layoutByBreakpoint, layouts?.options, loadLayouts]);
152
+ const layoutOptionsMap = (0, react_1.useMemo)(() => new Map(layoutOptions.map(option => [option.id, option])), [layoutOptions]);
153
+ const initialLayoutId = (0, react_1.useMemo)(() => {
154
+ const fromLayouts = layouts?.selectedId;
155
+ if (fromLayouts && layoutOptionsMap.has(fromLayouts))
156
+ return fromLayouts;
157
+ const persistedId = persistedLayoutsRef.current.selectedLayoutId;
158
+ if (persistedId && layoutOptionsMap.has(persistedId))
159
+ return persistedId;
160
+ if (layouts?.initialSelectedId && layoutOptionsMap.has(layouts.initialSelectedId))
161
+ return layouts.initialSelectedId;
162
+ return layoutOptions[0]?.id;
163
+ }, [layoutOptions, layoutOptionsMap, layouts?.initialSelectedId, layouts?.selectedId]);
99
164
  const [layoutSource, setLayoutSource] = (0, react_1.useState)(() => ({
100
- layoutByBreakpoint,
101
- source: 'static',
165
+ ...(layoutOptionsMap.get(initialLayoutId ?? '')?.preset ?? { layoutByBreakpoint, source: 'static' }),
102
166
  }));
167
+ const breakpoints = (0, react_1.useMemo)(() => customBreakpoints ?? Object.keys(layoutSource.layoutByBreakpoint).map(Number).sort((a, b) => a - b), [customBreakpoints, layoutSource.layoutByBreakpoint]);
103
168
  const [layoutPreset, setLayoutPreset] = (0, react_1.useState)(() => getLayoutConfigForWidth(typeof window !== 'undefined' ? window.innerWidth : undefined, layoutSource.layoutByBreakpoint, breakpoints));
169
+ (0, react_1.useEffect)(() => {
170
+ setLayoutPreset(getLayoutConfigForWidth(typeof window !== 'undefined' ? window.innerWidth : undefined, layoutSource.layoutByBreakpoint, breakpoints));
171
+ }, [breakpoints, layoutSource.layoutByBreakpoint]);
172
+ const [selectedLayoutId, setSelectedLayoutId] = (0, react_1.useState)(initialLayoutId);
173
+ const selectedLayoutRef = (0, react_1.useRef)(initialLayoutId);
174
+ (0, react_1.useEffect)(() => {
175
+ selectedLayoutRef.current = selectedLayoutId;
176
+ }, [selectedLayoutId]);
177
+ (0, react_1.useEffect)(() => {
178
+ if (!selectedLayoutId || layoutOptionsMap.has(selectedLayoutId))
179
+ return;
180
+ const fallbackId = layoutOptions[0]?.id;
181
+ if (fallbackId && fallbackId !== selectedLayoutId) {
182
+ setSelectedLayoutId(fallbackId);
183
+ layouts?.onSelect?.(fallbackId);
184
+ }
185
+ }, [layoutOptions, layoutOptionsMap, layouts?.onSelect, selectedLayoutId]);
186
+ (0, react_1.useEffect)(() => {
187
+ const externalSelected = layouts?.selectedId;
188
+ if (!externalSelected)
189
+ return;
190
+ if (!layoutOptionsMap.has(externalSelected))
191
+ return;
192
+ if (externalSelected !== selectedLayoutId) {
193
+ setSelectedLayoutId(externalSelected);
194
+ }
195
+ }, [layoutOptionsMap, layouts?.selectedId, selectedLayoutId]);
196
+ (0, react_1.useEffect)(() => {
197
+ if (!selectedLayoutId || !layouts?.onSelect)
198
+ return;
199
+ if (layouts.selectedId === selectedLayoutId)
200
+ return;
201
+ layouts.onSelect(selectedLayoutId);
202
+ }, [layouts?.onSelect, layouts?.selectedId, selectedLayoutId]);
203
+ (0, react_1.useEffect)(() => {
204
+ const nextSource = layoutOptionsMap.get(selectedLayoutId ?? '')?.preset ?? {
205
+ layoutByBreakpoint,
206
+ source: 'static',
207
+ };
208
+ setLayoutSource(prev => (prev === nextSource ? prev : nextSource));
209
+ }, [layoutByBreakpoint, layoutOptionsMap, selectedLayoutId]);
210
+ (0, react_1.useEffect)(() => {
211
+ if (typeof window === 'undefined')
212
+ return;
213
+ const nextPersisted = readPersistedLayouts(storageKey) ?? { layouts: {} };
214
+ persistedLayoutsRef.current = nextPersisted;
215
+ if (nextPersisted.selectedLayoutId &&
216
+ layoutOptionsMap.has(nextPersisted.selectedLayoutId) &&
217
+ nextPersisted.selectedLayoutId !== selectedLayoutId) {
218
+ setSelectedLayoutId(nextPersisted.selectedLayoutId);
219
+ }
220
+ }, [layoutOptionsMap, selectedLayoutId, storageKey]);
221
+ (0, react_1.useEffect)(() => {
222
+ let cancelled = false;
223
+ if (!loadLayouts)
224
+ return undefined;
225
+ loadLayouts().then(result => {
226
+ if (cancelled || !result?.options)
227
+ return;
228
+ const nextOptions = buildLayoutOptions(result.options, layoutByBreakpoint);
229
+ setLayoutOptions(prev => (layoutOptionsEqual(prev, nextOptions) ? prev : nextOptions));
230
+ const currentSelected = selectedLayoutRef.current;
231
+ let nextSelected = currentSelected && nextOptions.some(option => option.id === currentSelected) ? currentSelected : undefined;
232
+ if (result.selectedId && nextOptions.some(option => option.id === result.selectedId)) {
233
+ nextSelected = result.selectedId;
234
+ }
235
+ else if (!nextSelected) {
236
+ nextSelected = nextOptions[0]?.id;
237
+ }
238
+ if (nextSelected && nextSelected !== currentSelected) {
239
+ setSelectedLayoutId(nextSelected);
240
+ layouts?.onSelect?.(nextSelected);
241
+ }
242
+ else if (!currentSelected && nextSelected) {
243
+ setSelectedLayoutId(nextSelected);
244
+ }
245
+ });
246
+ return () => {
247
+ cancelled = true;
248
+ };
249
+ }, [layoutByBreakpoint, loadLayouts, layouts?.onSelect]);
104
250
  const fallbackLayoutConfig = (0, react_1.useMemo)(() => {
105
251
  const firstKey = breakpoints[0];
106
252
  return layoutSource.layoutByBreakpoint[firstKey] ?? Object.values(layoutSource.layoutByBreakpoint)[0];
@@ -110,10 +256,11 @@ exports.WidgetBoard = (0, react_1.forwardRef)(function WidgetBoard({ widgets, la
110
256
  const definitionsMap = (0, react_1.useMemo)(() => new Map(activeDefinitions.map(def => [def.id, def])), [activeDefinitions]);
111
257
  const [layoutState, setLayoutState] = (0, react_1.useState)(() => buildDefaultState(activeDefinitions));
112
258
  (0, react_1.useEffect)(() => {
113
- const savedState = typeof window !== 'undefined' ? window.localStorage.getItem(storageKey) : null;
114
- if (savedState) {
259
+ const storagePayload = (selectedLayoutId && persistedLayoutsRef.current.layouts[selectedLayoutId]) ??
260
+ persistedLayoutsRef.current.layouts.__legacy;
261
+ if (storagePayload) {
115
262
  try {
116
- const parsed = hydrateStoragePayload(savedState);
263
+ const parsed = hydrateStoragePayload(storagePayload);
117
264
  const items = (parsed.items || []).flatMap(item => {
118
265
  const definition = definitionsMap.get(item.id);
119
266
  if (!definition)
@@ -142,7 +289,7 @@ exports.WidgetBoard = (0, react_1.forwardRef)(function WidgetBoard({ widgets, la
142
289
  }
143
290
  }
144
291
  setLayoutState(buildDefaultState(activeDefinitions));
145
- }, [activeDefinitions, definitionsMap, storageKey]);
292
+ }, [activeDefinitions, definitionsMap, selectedLayoutId]);
146
293
  (0, react_1.useEffect)(() => {
147
294
  if (typeof window === 'undefined')
148
295
  return;
@@ -151,35 +298,26 @@ exports.WidgetBoard = (0, react_1.forwardRef)(function WidgetBoard({ widgets, la
151
298
  return () => window.removeEventListener('resize', handleResize);
152
299
  }, [breakpoints, layoutSource.layoutByBreakpoint]);
153
300
  (0, react_1.useEffect)(() => {
154
- let mounted = true;
155
- if (!loadRemoteLayout)
156
- return undefined;
157
- loadRemoteLayout().then(preset => {
158
- if (!mounted || !preset?.layoutByBreakpoint)
159
- return;
160
- setLayoutSource(preset);
161
- const nextPreset = getLayoutConfigForWidth(typeof window !== 'undefined' ? window.innerWidth : undefined, preset.layoutByBreakpoint, breakpoints);
162
- const nextLayout = nextPreset.layout ?? Object.values(preset.layoutByBreakpoint)[0] ?? fallbackLayoutConfig;
163
- setLayoutPreset(nextPreset);
164
- const nextDefinitions = withLayout(widgets, nextLayout);
165
- const nextActiveDefinitions = nextDefinitions.filter(def => (isWidgetEnabled ? isWidgetEnabled(def) : true));
166
- setLayoutState(buildDefaultState(nextActiveDefinitions));
167
- });
168
- return () => {
169
- mounted = false;
170
- };
171
- }, [breakpoints, fallbackLayoutConfig, isWidgetEnabled, loadRemoteLayout, widgets]);
172
- (0, react_1.useEffect)(() => {
173
- if (typeof window === 'undefined')
301
+ if (typeof window === 'undefined' || !selectedLayoutId)
174
302
  return;
175
303
  try {
176
- window.localStorage.setItem(storageKey, buildStoragePayload(layoutState));
304
+ const payload = buildStoragePayload(layoutState);
305
+ const nextPersisted = {
306
+ ...persistedLayoutsRef.current,
307
+ selectedLayoutId,
308
+ layouts: {
309
+ ...persistedLayoutsRef.current.layouts,
310
+ [selectedLayoutId]: payload,
311
+ },
312
+ };
313
+ persistedLayoutsRef.current = nextPersisted;
314
+ window.localStorage.setItem(storageKey, JSON.stringify(nextPersisted));
177
315
  onLayoutPersist?.(layoutState);
178
316
  }
179
317
  catch {
180
318
  // ignore storage failures
181
319
  }
182
- }, [layoutState, onLayoutPersist, storageKey]);
320
+ }, [layoutState, onLayoutPersist, selectedLayoutId, storageKey]);
183
321
  const hideItem = (0, react_1.useCallback)((id) => {
184
322
  setLayoutState(prev => ({
185
323
  ...prev,
@@ -286,17 +424,9 @@ exports.WidgetBoard = (0, react_1.forwardRef)(function WidgetBoard({ widgets, la
286
424
  return (react_1.default.createElement(board_item_1.default, { key: item.id, i18nStrings: boardI18n_1.boardItemI18nStrings, header: headerElement, settings: settingsElement, disableContentPaddings: true },
287
425
  react_1.default.createElement(material_1.Box, { sx: { p: 2, height: '100%', boxSizing: 'border-box', overflow: 'hidden' } }, isCollapsed ? react_1.default.createElement(material_1.Typography, { sx: { color: 'text.secondary', fontSize: 14 } }, "Collapsed") : definition.render())));
288
426
  };
289
- const controlsConfig = controls ?? {};
290
- const showControls = controlsConfig.hideReset !== true || (controlsConfig.hideRestore !== true && layoutState.hidden.length > 0);
291
427
  return (react_1.default.createElement(CloudscapeThemeProvider_1.CloudscapeThemeProvider, null,
292
428
  react_1.default.createElement(CloudscapeBoardStyles_1.CloudscapeBoardStyles, { hideNavigationArrows: hideNavigationArrows }),
293
- react_1.default.createElement(material_1.Stack, { spacing: 1.5, className: className },
294
- showControls && (react_1.default.createElement(material_1.Stack, { direction: { xs: 'column', sm: 'row' }, alignItems: { xs: 'flex-start', sm: 'center' }, justifyContent: 'space-between', spacing: 1 },
295
- react_1.default.createElement(material_1.Typography, { variant: 'h6', fontWeight: 700 }, controlsConfig.title ?? 'Widgets layout'),
296
- react_1.default.createElement(material_1.Stack, { direction: 'row', spacing: 1 },
297
- controlsConfig.hideReset !== true && (react_1.default.createElement(PneButton_1.default, { onClick: resetLayout, pneStyle: 'outlined', startIcon: react_1.default.createElement(RestartAlt_1.default, null) }, controlsConfig.resetLabel ?? 'Reset to default')),
298
- layoutState.hidden.length > 0 && controlsConfig.hideRestore !== true && (react_1.default.createElement(PneButton_1.default, { pneStyle: 'text', onClick: () => layoutState.hidden.forEach(restoreItem) }, controlsConfig.restoreLabel ?? 'Restore hidden'))))),
299
- layoutState.hidden.length > 0 && (react_1.default.createElement(material_1.Stack, { direction: 'row', spacing: 1, flexWrap: 'wrap' }, layoutState.hidden.map(id => (react_1.default.createElement(material_1.Chip, { key: id, variant: 'outlined', label: `Show ${definitionsMap.get(id)?.title ?? id}`, onClick: () => restoreItem(id), size: 'small' }))))),
429
+ react_1.default.createElement(material_1.Box, { className: className },
300
430
  react_1.default.createElement(board_1.default, { items: visibleItems, renderItem: renderItem, i18nStrings: boardI18nStrings, onItemsChange: handleItemsChange, empty: empty ?? react_1.default.createElement(material_1.Box, { sx: { p: 2, color: 'text.secondary' } }, "No widgets available") }))));
301
431
  });
302
432
  //# sourceMappingURL=WidgetBoard.js.map