@stack-spot/portal-layout 0.0.46 → 0.0.48

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 (99) hide show
  1. package/dist/Layout.d.ts +2 -2
  2. package/dist/Layout.js +1 -1
  3. package/dist/LayoutOverlayManager.js +6 -6
  4. package/dist/LayoutOverlayManager.js.map +1 -1
  5. package/dist/components/Dialog.d.ts +2 -2
  6. package/dist/components/Dialog.d.ts.map +1 -1
  7. package/dist/components/Dialog.js +1 -1
  8. package/dist/components/Header.d.ts +1 -1
  9. package/dist/components/Header.js +1 -1
  10. package/dist/components/OverlayContent.d.ts +1 -1
  11. package/dist/components/OverlayContent.js +20 -20
  12. package/dist/components/PortalSwitcher.d.ts +1 -1
  13. package/dist/components/PortalSwitcher.d.ts.map +1 -1
  14. package/dist/components/PortalSwitcher.js +56 -55
  15. package/dist/components/PortalSwitcher.js.map +1 -1
  16. package/dist/components/SelectionList.d.ts +1 -1
  17. package/dist/components/SelectionList.d.ts.map +1 -1
  18. package/dist/components/SelectionList.js +58 -57
  19. package/dist/components/SelectionList.js.map +1 -1
  20. package/dist/components/Toaster.d.ts +1 -1
  21. package/dist/components/Toaster.js +1 -1
  22. package/dist/components/UserMenu.d.ts +1 -1
  23. package/dist/components/UserMenu.js +41 -41
  24. package/dist/components/error/ErrorBoundary.d.ts +1 -1
  25. package/dist/components/error/ErrorBoundary.js +1 -1
  26. package/dist/components/error/ErrorFeedback.d.ts +1 -1
  27. package/dist/components/error/ErrorFeedback.js +1 -1
  28. package/dist/components/error/SilentErrorBoundary.d.ts +1 -1
  29. package/dist/components/error/SilentErrorBoundary.js +1 -1
  30. package/dist/components/menu/MenuContent.d.ts +10 -12
  31. package/dist/components/menu/MenuContent.d.ts.map +1 -1
  32. package/dist/components/menu/MenuContent.js +146 -146
  33. package/dist/components/menu/MenuContent.js.map +1 -1
  34. package/dist/components/menu/MenuSections.d.ts +1 -1
  35. package/dist/components/menu/MenuSections.js +1 -1
  36. package/dist/components/menu/MenuSections.js.map +1 -1
  37. package/dist/components/menu/PageSelector.d.ts +1 -1
  38. package/dist/components/menu/PageSelector.js +65 -65
  39. package/dist/components/menu/PageSelector.js.map +1 -1
  40. package/dist/components/menu/use-check-text-overflow.js.map +1 -1
  41. package/dist/components/types.d.ts +1 -0
  42. package/dist/components/types.d.ts.map +1 -1
  43. package/dist/layout.css +465 -464
  44. package/dist/svg/AI.d.ts +1 -1
  45. package/dist/svg/AI.js +1 -1
  46. package/dist/svg/EDP.d.ts +1 -1
  47. package/dist/svg/EDP.js +1 -1
  48. package/dist/svg/Forbidden.d.ts +1 -1
  49. package/dist/svg/Forbidden.js +1 -1
  50. package/dist/svg/HUB.d.ts +1 -1
  51. package/dist/svg/HUB.js +1 -1
  52. package/dist/svg/Logo.d.ts +1 -1
  53. package/dist/svg/Logo.js +1 -1
  54. package/dist/svg/NotFound.d.ts +1 -1
  55. package/dist/svg/NotFound.js +1 -1
  56. package/dist/svg/ServerError.d.ts +1 -1
  57. package/dist/svg/ServerError.js +1 -1
  58. package/dist/svg/Unauthenticated.d.ts +1 -1
  59. package/dist/svg/Unauthenticated.js +1 -1
  60. package/dist/toaster.js +1 -1
  61. package/dist/utils.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/Layout.tsx +103 -103
  64. package/src/LayoutOverlayManager.tsx +273 -273
  65. package/src/components/Dialog.tsx +93 -93
  66. package/src/components/Header.tsx +29 -29
  67. package/src/components/OverlayContent.tsx +58 -58
  68. package/src/components/PortalSwitcher.tsx +147 -146
  69. package/src/components/SelectionList.tsx +268 -264
  70. package/src/components/Toaster.tsx +16 -16
  71. package/src/components/UserMenu.tsx +111 -111
  72. package/src/components/error/ErrorBoundary.tsx +38 -38
  73. package/src/components/error/ErrorFeedback.tsx +114 -114
  74. package/src/components/error/ErrorManager.ts +31 -31
  75. package/src/components/error/SilentErrorBoundary.tsx +54 -54
  76. package/src/components/menu/MenuContent.tsx +293 -293
  77. package/src/components/menu/MenuSections.tsx +268 -268
  78. package/src/components/menu/PageSelector.tsx +152 -152
  79. package/src/components/menu/constants.ts +2 -2
  80. package/src/components/menu/types.ts +112 -112
  81. package/src/components/menu/use-check-text-overflow.tsx +26 -26
  82. package/src/components/menu/use-keyboard-controls.tsx +70 -70
  83. package/src/components/types.ts +15 -14
  84. package/src/dictionary.ts +25 -25
  85. package/src/elements.ts +24 -24
  86. package/src/errors.ts +11 -11
  87. package/src/index.ts +17 -17
  88. package/src/layout.css +465 -464
  89. package/src/svg/AI.tsx +37 -37
  90. package/src/svg/EDP.tsx +35 -35
  91. package/src/svg/Forbidden.tsx +22 -22
  92. package/src/svg/HUB.tsx +35 -35
  93. package/src/svg/Logo.tsx +35 -35
  94. package/src/svg/NotFound.tsx +16 -16
  95. package/src/svg/ServerError.tsx +33 -33
  96. package/src/svg/Unauthenticated.tsx +16 -16
  97. package/src/toaster.tsx +76 -76
  98. package/src/utils.ts +114 -114
  99. package/tsconfig.json +8 -8
@@ -1,293 +1,293 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
- import { Flex, IconBox, Text } from '@citric/core'
3
- import { ArrowLeft, ChevronDown } from '@citric/icons'
4
- import { LoadingCircular } from '@citric/ui'
5
- import { listToClass, theme } from '@stack-spot/portal-theme'
6
- import { useMemo, useState } from 'react'
7
- import { styled } from 'styled-components'
8
- import { hideOverlayImmediately } from './MenuSections'
9
- import { PageSelector } from './PageSelector'
10
- import { MENU_CONTENT_ITEM_PADDING as ITEM_PADDING, MENU_CONTENT_PADDING as PADDING } from './constants'
11
- import { ItemGroup, MenuAction, MenuItem, MenuSectionContent } from './types'
12
- import { useCheckTextOverflow } from './use-check-text-overflow'
13
-
14
- const BackLink = styled.a`
15
- display: flex;
16
- flex-direction: row;
17
- align-items: center;
18
- margin: ${PADDING}px;
19
- margin-bottom: 16px;
20
- gap: 6px;
21
- `
22
-
23
- export const MenuGroup = styled.ul`
24
- padding: 0 0 0 16px;
25
- display: flex;
26
- flex-direction: column;
27
- visibility: hidden;
28
- transition: visibility 0s 0.3s;
29
-
30
- &.no-indentation {
31
- padding: 0;
32
- }
33
-
34
- .item-row-group > a {
35
- padding: 0 16px;
36
- margin: 0;
37
- border-radius: 0;
38
- }
39
-
40
- .item-row-group > a::before {
41
- content: '';
42
- position: absolute;
43
- top: 0;
44
- left: 0;
45
- right: 0;
46
- bottom: 0;
47
- background-color: var(--light-300);
48
- opacity: 0.24;
49
- border-radius: inherit;
50
- }
51
-
52
- .item-row {
53
- display: flex;
54
- flex-direction: row;
55
- gap: 8px;
56
- align-items: center;
57
-
58
- .label {
59
- flex: 1;
60
- &.hidden, &.ellipsis {
61
- white-space: nowrap;
62
- overflow: hidden;
63
- }
64
- &.ellipsis {
65
- text-overflow: ellipsis;
66
- }
67
- }
68
- }
69
-
70
- .item-row-group {
71
- margin-top: 16px;
72
- }
73
-
74
- li a {
75
- position: relative;
76
- height: 0;
77
- overflow: hidden;
78
- transition: height 0.3s, background-color 0.2s;
79
- margin-left: ${PADDING - ITEM_PADDING}px;
80
- padding-left: ${ITEM_PADDING}px;
81
-
82
- &:hover {
83
- background-color: ${theme.color.light['500']};
84
- }
85
-
86
- &.action {
87
- &:before {
88
- content: '';
89
- position: absolute;
90
- left: 2px;
91
- width: 2px;
92
- height: 0;
93
- background: inherit;
94
- transition: height 0.2s;
95
- }
96
-
97
- &.active {
98
-
99
- &:hover {
100
- background-color: transparent;
101
- }
102
-
103
- &:before {
104
- background: ${theme.color.primary['500']};
105
- height: 24px;
106
- }
107
- }
108
-
109
- &:not(.active):hover:before {
110
- background: ${theme.color.light.contrastText};
111
- height: 24px;
112
- }
113
- }
114
-
115
- .chevron {
116
- transition: transform 0.3s;
117
- &:not(.open) {
118
- transform: rotate(-90deg);
119
- }
120
- }
121
-
122
- .item-row-title {
123
- opacity: 0.7;
124
- }
125
- }
126
-
127
- &.open {
128
- visibility: visible;
129
- transition: unset;
130
- & > li > a, & > li > .item-row-group > a {
131
- height: 40px;
132
- }
133
- }
134
-
135
- &:not(.open) &.open > li > a {
136
- height: 0;
137
- }
138
-
139
- &.root {
140
- margin-bottom: ${PADDING}px;
141
-
142
- & > li {
143
- .group-title {
144
- margin-left: ${PADDING}px;
145
- margin-bottom: 5px;
146
- margin-top: 40px;
147
- display: block;
148
- }
149
-
150
- &:first-child {
151
- .group-title {
152
- margin-top: 0;
153
- }
154
- }
155
- }
156
- }
157
- `
158
-
159
- export const Title = styled.header`
160
- display: flex;
161
- flex-direction: column;
162
- margin: ${PADDING}px 0 24px ${PADDING}px;
163
- `
164
-
165
- export const ActionItem = ({ label, onClick, href, active, icon, badge, overflow = 'wrap' }: MenuAction) => {
166
- const { ref, overflow: textOverflow } = useCheckTextOverflow()
167
- const isTextLabel = typeof label === 'string'
168
- return (
169
- <a
170
- href={href}
171
- onClick={() => {
172
- if (active) return
173
- if (onClick) onClick()
174
- hideOverlayImmediately()
175
- }}
176
- className={listToClass(['action', 'item-row', active ? 'active' : undefined])}
177
- {...(active ? { 'aria-current': 'page' } : undefined)}
178
- {...(!href ? { 'tabIndex': 0 } : undefined)}
179
- >
180
- {icon}
181
- {isTextLabel ?
182
- <Text ref={ref} appearance="body2" className={`label ${overflow}`} title={textOverflow ? label : ''}>{label}</Text> :
183
- label.element}
184
- {badge}
185
- </a>
186
- )
187
- }
188
-
189
- const CollapsibleGroupItem = ({ label, open: initiallyOpened, children, icon, badge, root, overflow = 'wrap' }:
190
- ItemGroup & { root?: boolean }) => {
191
- const [open, setOpen] = useState(initiallyOpened ?? children?.some(c => 'active' in c && c.active) ?? false)
192
- const items = useMemo(() => children?.filter(i => !i.hidden).map(renderOption), [children])
193
- const id = `menuGroup${label}`
194
-
195
- return (
196
- <>
197
- <a
198
- onClick={() => setOpen(!open)}
199
- onKeyDown={e => e.key === 'Enter' && setOpen(!open)}
200
- className="item-row"
201
- tabIndex={0}
202
- aria-controls={id}
203
- aria-expanded={open}
204
- >
205
- {icon}
206
- <Text appearance={root ? 'overheader2' : 'body2'}
207
- colorScheme="light.contrastText"
208
- className={`label ${overflow} ${root ? 'item-row-title' : ''}`}>
209
- {label}
210
- </Text>
211
- {badge}
212
- <IconBox sx={{ mr: root ? undefined : '5' }}>
213
- <ChevronDown className={listToClass(['chevron', open ? 'open' : ''])} />
214
- </IconBox>
215
- </a>
216
- <MenuGroup id={id}
217
- className={`${open ? 'open' : ''} ${root ? 'no-indentation' : ''}`}
218
- aria-hidden={!open}>{items}</MenuGroup>
219
- </>
220
- )
221
- }
222
-
223
- const RootGroupItem = (props: ItemGroup) => {
224
- const items = useMemo(() => props.children?.filter(i => !i.hidden).map(renderOption), [props.children])
225
-
226
- return <>
227
- {items.length ? <div className="item-row-group">
228
- <CollapsibleGroupItem {...props} open={true} root={true}/>
229
- </div> :
230
- <>
231
- <div className="item-row">
232
- {props.icon}
233
- <Text appearance="overheader2" colorScheme="light.700" className={`group-title label ${props.overflow}`}>{props.label}</Text>
234
- {props.badge}
235
- </div>
236
- <MenuGroup className="open no-indentation">{items}</MenuGroup>
237
- </>
238
- }
239
- </>
240
- }
241
-
242
- const GroupItem = ({ root, ...item }: ItemGroup & { root?: boolean }) => (
243
- root ? <RootGroupItem {...item} /> : <CollapsibleGroupItem {...item} />
244
- )
245
-
246
- function renderOption({ root, ...option }: MenuItem & { root?: boolean }) {
247
- const labelText = typeof option.label === 'string' ? option.label : option.label.id
248
- return (
249
- <li key={labelText} role="menuitem" aria-selected={'children' in option ? undefined : option.active}>
250
- {'children' in option ? <GroupItem root={root} {...option} /> : <ActionItem {...option} />}
251
- </li >
252
- )
253
- }
254
-
255
- export const MenuContent = ({ pageSelector, goBack, title, subtitle, afterTitle, options = [], loading, error }: MenuSectionContent) => {
256
- const items = useMemo(() => options.filter(o => !o.hidden).map(o => renderOption({ ...o, root: true })), [options])
257
-
258
- function renderContent() {
259
- if (loading) {
260
- return (
261
- <Flex justifyContent="center" alignItems="center" flex={1} sx={{ padding: '40px' }}>
262
- <LoadingCircular />
263
- </Flex>
264
- )
265
- }
266
- if (error) return <Text colorScheme="danger">{error}</Text>
267
- return <MenuGroup className="open root no-indentation">{items}</MenuGroup>
268
- }
269
-
270
- return (
271
- <>
272
- {goBack && (
273
- <BackLink href={goBack.href} onClick={goBack.onClick}>
274
- <IconBox colorScheme="inverse" size="sm">
275
- <ArrowLeft />
276
- </IconBox>
277
- {typeof goBack?.label === 'string' ?
278
- <Text appearance="body2" nowrapEllipsis>{goBack.label}</Text> :
279
- goBack.label.element}
280
- </BackLink>
281
- )}
282
- {title && (
283
- <Title>
284
- <Text appearance="overheader1" colorScheme="primary" sx={{ fontSize: '0.75rem', mt: 2, mb: 2 }}>{title}</Text>
285
- {subtitle && <Text appearance="h5">{subtitle}</Text>}
286
- </Title>
287
- )}
288
- {afterTitle}
289
- {pageSelector && <PageSelector {...pageSelector} />}
290
- {renderContent()}
291
- </>
292
- )
293
- }
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { Flex, IconBox, Text } from '@citric/core'
3
+ import { ArrowLeft, ChevronDown } from '@citric/icons'
4
+ import { LoadingCircular } from '@citric/ui'
5
+ import { listToClass, theme } from '@stack-spot/portal-theme'
6
+ import { useMemo, useState } from 'react'
7
+ import { styled } from 'styled-components'
8
+ import { hideOverlayImmediately } from './MenuSections'
9
+ import { PageSelector } from './PageSelector'
10
+ import { MENU_CONTENT_ITEM_PADDING as ITEM_PADDING, MENU_CONTENT_PADDING as PADDING } from './constants'
11
+ import { ItemGroup, MenuAction, MenuItem, MenuSectionContent } from './types'
12
+ import { useCheckTextOverflow } from './use-check-text-overflow'
13
+
14
+ const BackLink = styled.a`
15
+ display: flex;
16
+ flex-direction: row;
17
+ align-items: center;
18
+ margin: ${PADDING}px;
19
+ margin-bottom: 16px;
20
+ gap: 6px;
21
+ `
22
+
23
+ export const MenuGroup = styled.ul`
24
+ padding: 0 0 0 16px;
25
+ display: flex;
26
+ flex-direction: column;
27
+ visibility: hidden;
28
+ transition: visibility 0s 0.3s;
29
+
30
+ &.no-indentation {
31
+ padding: 0;
32
+ }
33
+
34
+ .item-row-group > a {
35
+ padding: 0 16px;
36
+ margin: 0;
37
+ border-radius: 0;
38
+ }
39
+
40
+ .item-row-group > a::before {
41
+ content: '';
42
+ position: absolute;
43
+ top: 0;
44
+ left: 0;
45
+ right: 0;
46
+ bottom: 0;
47
+ background-color: var(--light-300);
48
+ opacity: 0.24;
49
+ border-radius: inherit;
50
+ }
51
+
52
+ .item-row {
53
+ display: flex;
54
+ flex-direction: row;
55
+ gap: 8px;
56
+ align-items: center;
57
+
58
+ .label {
59
+ flex: 1;
60
+ &.hidden, &.ellipsis {
61
+ white-space: nowrap;
62
+ overflow: hidden;
63
+ }
64
+ &.ellipsis {
65
+ text-overflow: ellipsis;
66
+ }
67
+ }
68
+ }
69
+
70
+ .item-row-group {
71
+ margin-top: 16px;
72
+ }
73
+
74
+ li a {
75
+ position: relative;
76
+ height: 0;
77
+ overflow: hidden;
78
+ transition: height 0.3s, background-color 0.2s;
79
+ margin-left: ${PADDING - ITEM_PADDING}px;
80
+ padding-left: ${ITEM_PADDING}px;
81
+
82
+ &:hover {
83
+ background-color: ${theme.color.light['500']};
84
+ }
85
+
86
+ &.action {
87
+ &:before {
88
+ content: '';
89
+ position: absolute;
90
+ left: 2px;
91
+ width: 2px;
92
+ height: 0;
93
+ background: inherit;
94
+ transition: height 0.2s;
95
+ }
96
+
97
+ &.active {
98
+
99
+ &:hover {
100
+ background-color: transparent;
101
+ }
102
+
103
+ &:before {
104
+ background: ${theme.color.primary['500']};
105
+ height: 24px;
106
+ }
107
+ }
108
+
109
+ &:not(.active):hover:before {
110
+ background: ${theme.color.light.contrastText};
111
+ height: 24px;
112
+ }
113
+ }
114
+
115
+ .chevron {
116
+ transition: transform 0.3s;
117
+ &:not(.open) {
118
+ transform: rotate(-90deg);
119
+ }
120
+ }
121
+
122
+ .item-row-title {
123
+ opacity: 0.7;
124
+ }
125
+ }
126
+
127
+ &.open {
128
+ visibility: visible;
129
+ transition: unset;
130
+ & > li > a, & > li > .item-row-group > a {
131
+ height: 40px;
132
+ }
133
+ }
134
+
135
+ &:not(.open) &.open > li > a {
136
+ height: 0;
137
+ }
138
+
139
+ &.root {
140
+ margin-bottom: ${PADDING}px;
141
+
142
+ & > li {
143
+ .group-title {
144
+ margin-left: ${PADDING}px;
145
+ margin-bottom: 5px;
146
+ margin-top: 40px;
147
+ display: block;
148
+ }
149
+
150
+ &:first-child {
151
+ .group-title {
152
+ margin-top: 0;
153
+ }
154
+ }
155
+ }
156
+ }
157
+ `
158
+
159
+ export const Title = styled.header`
160
+ display: flex;
161
+ flex-direction: column;
162
+ margin: ${PADDING}px 0 24px ${PADDING}px;
163
+ `
164
+
165
+ export const ActionItem = ({ label, onClick, href, active, icon, badge, overflow = 'wrap' }: MenuAction) => {
166
+ const { ref, overflow: textOverflow } = useCheckTextOverflow()
167
+ const isTextLabel = typeof label === 'string'
168
+ return (
169
+ <a
170
+ href={href}
171
+ onClick={() => {
172
+ if (active) return
173
+ if (onClick) onClick()
174
+ hideOverlayImmediately()
175
+ }}
176
+ className={listToClass(['action', 'item-row', active ? 'active' : undefined])}
177
+ {...(active ? { 'aria-current': 'page' } : undefined)}
178
+ {...(!href ? { 'tabIndex': 0 } : undefined)}
179
+ >
180
+ {icon}
181
+ {isTextLabel ?
182
+ <Text ref={ref} appearance="body2" className={`label ${overflow}`} title={textOverflow ? label : ''}>{label}</Text> :
183
+ label.element}
184
+ {badge}
185
+ </a>
186
+ )
187
+ }
188
+
189
+ const CollapsibleGroupItem = ({ label, open: initiallyOpened, children, icon, badge, root, overflow = 'wrap' }:
190
+ ItemGroup & { root?: boolean }) => {
191
+ const [open, setOpen] = useState(initiallyOpened ?? children?.some(c => 'active' in c && c.active) ?? false)
192
+ const items = useMemo(() => children?.filter(i => !i.hidden).map(renderOption), [children])
193
+ const id = `menuGroup${label}`
194
+
195
+ return (
196
+ <>
197
+ <a
198
+ onClick={() => setOpen(!open)}
199
+ onKeyDown={e => e.key === 'Enter' && setOpen(!open)}
200
+ className="item-row"
201
+ tabIndex={0}
202
+ aria-controls={id}
203
+ aria-expanded={open}
204
+ >
205
+ {icon}
206
+ <Text appearance={root ? 'overheader2' : 'body2'}
207
+ colorScheme="light.contrastText"
208
+ className={`label ${overflow} ${root ? 'item-row-title' : ''}`}>
209
+ {label}
210
+ </Text>
211
+ {badge}
212
+ <IconBox sx={{ mr: root ? undefined : '5' }}>
213
+ <ChevronDown className={listToClass(['chevron', open ? 'open' : ''])} />
214
+ </IconBox>
215
+ </a>
216
+ <MenuGroup id={id}
217
+ className={`${open ? 'open' : ''} ${root ? 'no-indentation' : ''}`}
218
+ aria-hidden={!open}>{items}</MenuGroup>
219
+ </>
220
+ )
221
+ }
222
+
223
+ const RootGroupItem = (props: ItemGroup) => {
224
+ const items = useMemo(() => props.children?.filter(i => !i.hidden).map(renderOption), [props.children])
225
+
226
+ return <>
227
+ {items.length ? <div className="item-row-group">
228
+ <CollapsibleGroupItem {...props} open={true} root={true}/>
229
+ </div> :
230
+ <>
231
+ <div className="item-row">
232
+ {props.icon}
233
+ <Text appearance="overheader2" colorScheme="light.700" className={`group-title label ${props.overflow}`}>{props.label}</Text>
234
+ {props.badge}
235
+ </div>
236
+ <MenuGroup className="open no-indentation">{items}</MenuGroup>
237
+ </>
238
+ }
239
+ </>
240
+ }
241
+
242
+ const GroupItem = ({ root, ...item }: ItemGroup & { root?: boolean }) => (
243
+ root ? <RootGroupItem {...item} /> : <CollapsibleGroupItem {...item} />
244
+ )
245
+
246
+ function renderOption({ root, ...option }: MenuItem & { root?: boolean }) {
247
+ const labelText = typeof option.label === 'string' ? option.label : option.label.id
248
+ return (
249
+ <li key={labelText} role="menuitem" aria-selected={'children' in option ? undefined : option.active}>
250
+ {'children' in option ? <GroupItem root={root} {...option} /> : <ActionItem {...option} />}
251
+ </li >
252
+ )
253
+ }
254
+
255
+ export const MenuContent = ({ pageSelector, goBack, title, subtitle, afterTitle, options = [], loading, error }: MenuSectionContent) => {
256
+ const items = useMemo(() => options.filter(o => !o.hidden).map(o => renderOption({ ...o, root: true })), [options])
257
+
258
+ function renderContent() {
259
+ if (loading) {
260
+ return (
261
+ <Flex justifyContent="center" alignItems="center" flex={1} sx={{ padding: '40px' }}>
262
+ <LoadingCircular />
263
+ </Flex>
264
+ )
265
+ }
266
+ if (error) return <Text colorScheme="danger">{error}</Text>
267
+ return <MenuGroup className="open root no-indentation">{items}</MenuGroup>
268
+ }
269
+
270
+ return (
271
+ <>
272
+ {goBack && (
273
+ <BackLink href={goBack.href} onClick={goBack.onClick}>
274
+ <IconBox colorScheme="inverse" size="sm">
275
+ <ArrowLeft />
276
+ </IconBox>
277
+ {typeof goBack?.label === 'string' ?
278
+ <Text appearance="body2" nowrapEllipsis>{goBack.label}</Text> :
279
+ goBack.label.element}
280
+ </BackLink>
281
+ )}
282
+ {title && (
283
+ <Title>
284
+ <Text appearance="overheader1" colorScheme="primary" sx={{ fontSize: '0.75rem', mt: 2, mb: 2 }}>{title}</Text>
285
+ {subtitle && <Text appearance="h5">{subtitle}</Text>}
286
+ </Title>
287
+ )}
288
+ {afterTitle}
289
+ {pageSelector && <PageSelector {...pageSelector} />}
290
+ {renderContent()}
291
+ </>
292
+ )
293
+ }