@stack-spot/portal-layout 0.0.64 → 1.0.0

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 (209) hide show
  1. package/dist/Layout.d.ts +57 -5
  2. package/dist/Layout.d.ts.map +1 -1
  3. package/dist/Layout.js +12 -5
  4. package/dist/Layout.js.map +1 -1
  5. package/dist/LayoutOverlayManager.d.ts +173 -6
  6. package/dist/LayoutOverlayManager.d.ts.map +1 -1
  7. package/dist/LayoutOverlayManager.js +118 -9
  8. package/dist/LayoutOverlayManager.js.map +1 -1
  9. package/dist/components/Dialog.d.ts +48 -5
  10. package/dist/components/Dialog.d.ts.map +1 -1
  11. package/dist/components/Dialog.js +7 -2
  12. package/dist/components/Dialog.js.map +1 -1
  13. package/dist/components/Header.d.ts +29 -1
  14. package/dist/components/Header.d.ts.map +1 -1
  15. package/dist/components/Header.js +6 -2
  16. package/dist/components/Header.js.map +1 -1
  17. package/dist/components/OverlayContent.d.ts +22 -0
  18. package/dist/components/OverlayContent.d.ts.map +1 -1
  19. package/dist/components/OverlayContent.js +4 -0
  20. package/dist/components/OverlayContent.js.map +1 -1
  21. package/dist/components/PortalSwitcher.d.ts +14 -0
  22. package/dist/components/PortalSwitcher.d.ts.map +1 -1
  23. package/dist/components/PortalSwitcher.js +9 -6
  24. package/dist/components/PortalSwitcher.js.map +1 -1
  25. package/dist/components/Toaster.d.ts +4 -0
  26. package/dist/components/Toaster.d.ts.map +1 -1
  27. package/dist/components/Toaster.js +5 -1
  28. package/dist/components/Toaster.js.map +1 -1
  29. package/dist/components/UserMenu.d.ts +14 -1
  30. package/dist/components/UserMenu.d.ts.map +1 -1
  31. package/dist/components/UserMenu.js +5 -1
  32. package/dist/components/UserMenu.js.map +1 -1
  33. package/dist/components/error/ErrorBoundary.d.ts +10 -1
  34. package/dist/components/error/ErrorBoundary.d.ts.map +1 -1
  35. package/dist/components/error/ErrorBoundary.js +10 -1
  36. package/dist/components/error/ErrorBoundary.js.map +1 -1
  37. package/dist/components/error/ErrorManager.d.ts +22 -6
  38. package/dist/components/error/ErrorManager.d.ts.map +1 -1
  39. package/dist/components/error/ErrorManager.js +21 -1
  40. package/dist/components/error/ErrorManager.js.map +1 -1
  41. package/dist/components/error/SilentErrorBoundary.d.ts +11 -2
  42. package/dist/components/error/SilentErrorBoundary.d.ts.map +1 -1
  43. package/dist/components/error/SilentErrorBoundary.js +10 -0
  44. package/dist/components/error/SilentErrorBoundary.js.map +1 -1
  45. package/dist/components/menu/MenuContent.d.ts +19 -545
  46. package/dist/components/menu/MenuContent.d.ts.map +1 -1
  47. package/dist/components/menu/MenuContent.js +33 -35
  48. package/dist/components/menu/MenuContent.js.map +1 -1
  49. package/dist/components/menu/MenuSections.d.ts +10 -0
  50. package/dist/components/menu/MenuSections.d.ts.map +1 -1
  51. package/dist/components/menu/MenuSections.js +70 -16
  52. package/dist/components/menu/MenuSections.js.map +1 -1
  53. package/dist/components/menu/PageSelector.d.ts +5 -0
  54. package/dist/components/menu/PageSelector.d.ts.map +1 -1
  55. package/dist/components/menu/PageSelector.js +8 -3
  56. package/dist/components/menu/PageSelector.js.map +1 -1
  57. package/dist/components/menu/types.d.ts +100 -7
  58. package/dist/components/menu/types.d.ts.map +1 -1
  59. package/dist/components/tour/PortalSwitcherStep.d.ts +6 -1
  60. package/dist/components/tour/PortalSwitcherStep.d.ts.map +1 -1
  61. package/dist/components/tour/PortalSwitcherStep.js +6 -1
  62. package/dist/components/tour/PortalSwitcherStep.js.map +1 -1
  63. package/dist/components/types.d.ts +0 -13
  64. package/dist/components/types.d.ts.map +1 -1
  65. package/dist/dictionary.d.ts +3 -0
  66. package/dist/dictionary.d.ts.map +1 -1
  67. package/dist/dictionary.js +3 -0
  68. package/dist/dictionary.js.map +1 -1
  69. package/dist/elements.d.ts +6 -0
  70. package/dist/elements.d.ts.map +1 -1
  71. package/dist/elements.js +6 -0
  72. package/dist/elements.js.map +1 -1
  73. package/dist/index.d.ts +0 -3
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +0 -3
  76. package/dist/index.js.map +1 -1
  77. package/dist/layout.css +1 -5
  78. package/dist/toaster.d.ts +74 -9
  79. package/dist/toaster.d.ts.map +1 -1
  80. package/dist/toaster.js +32 -6
  81. package/dist/toaster.js.map +1 -1
  82. package/dist/utils.d.ts +6 -69
  83. package/dist/utils.d.ts.map +1 -1
  84. package/dist/utils.js +9 -130
  85. package/dist/utils.js.map +1 -1
  86. package/package.json +12 -11
  87. package/readme.md +146 -0
  88. package/src/Layout.tsx +78 -28
  89. package/src/LayoutOverlayManager.tsx +184 -9
  90. package/src/components/Dialog.tsx +49 -6
  91. package/src/components/Header.tsx +31 -3
  92. package/src/components/OverlayContent.tsx +22 -0
  93. package/src/components/PortalSwitcher.tsx +22 -8
  94. package/src/components/Toaster.tsx +10 -2
  95. package/src/components/UserMenu.tsx +14 -1
  96. package/src/components/error/ErrorBoundary.tsx +11 -2
  97. package/src/components/error/ErrorManager.ts +22 -6
  98. package/src/components/error/SilentErrorBoundary.tsx +12 -2
  99. package/src/components/menu/MenuContent.tsx +33 -52
  100. package/src/components/menu/MenuSections.tsx +99 -49
  101. package/src/components/menu/PageSelector.tsx +8 -3
  102. package/src/components/menu/types.ts +100 -8
  103. package/src/components/tour/PortalSwitcherStep.tsx +7 -4
  104. package/src/components/types.ts +0 -14
  105. package/src/dictionary.ts +3 -0
  106. package/src/elements.ts +6 -0
  107. package/src/index.ts +0 -3
  108. package/src/layout.css +1 -5
  109. package/src/toaster.tsx +125 -14
  110. package/src/utils.ts +9 -142
  111. package/dist/components/BottomNotification.d.ts +0 -1
  112. package/dist/components/BottomNotification.d.ts.map +0 -1
  113. package/dist/components/BottomNotification.js +0 -2
  114. package/dist/components/BottomNotification.js.map +0 -1
  115. package/dist/components/BottomPanel.d.ts +0 -1
  116. package/dist/components/BottomPanel.d.ts.map +0 -1
  117. package/dist/components/BottomPanel.js +0 -2
  118. package/dist/components/BottomPanel.js.map +0 -1
  119. package/dist/components/SelectionList.d.ts +0 -36
  120. package/dist/components/SelectionList.d.ts.map +0 -1
  121. package/dist/components/SelectionList.js +0 -140
  122. package/dist/components/SelectionList.js.map +0 -1
  123. package/dist/components/error/ErrorDescriptor.d.ts +0 -12
  124. package/dist/components/error/ErrorDescriptor.d.ts.map +0 -1
  125. package/dist/components/error/ErrorDescriptor.js +0 -17
  126. package/dist/components/error/ErrorDescriptor.js.map +0 -1
  127. package/dist/components/error/ErrorFeedback.d.ts +0 -3
  128. package/dist/components/error/ErrorFeedback.d.ts.map +0 -1
  129. package/dist/components/error/ErrorFeedback.js +0 -66
  130. package/dist/components/error/ErrorFeedback.js.map +0 -1
  131. package/dist/components/menu/use-check-text-overflow.d.ts +0 -6
  132. package/dist/components/menu/use-check-text-overflow.d.ts.map +0 -1
  133. package/dist/components/menu/use-check-text-overflow.js +0 -20
  134. package/dist/components/menu/use-check-text-overflow.js.map +0 -1
  135. package/dist/components/menu/use-keyboard-controls.d.ts +0 -23
  136. package/dist/components/menu/use-keyboard-controls.d.ts.map +0 -1
  137. package/dist/components/menu/use-keyboard-controls.js +0 -49
  138. package/dist/components/menu/use-keyboard-controls.js.map +0 -1
  139. package/dist/components/menu/useCheckTextOverflow.d.ts +0 -6
  140. package/dist/components/menu/useCheckTextOverflow.d.ts.map +0 -1
  141. package/dist/components/menu/useCheckTextOverflow.js +0 -20
  142. package/dist/components/menu/useCheckTextOverflow.js.map +0 -1
  143. package/dist/components/tour/Navigation.d.ts +0 -8
  144. package/dist/components/tour/Navigation.d.ts.map +0 -1
  145. package/dist/components/tour/Navigation.js +0 -15
  146. package/dist/components/tour/Navigation.js.map +0 -1
  147. package/dist/components/tour/config.d.ts +0 -3
  148. package/dist/components/tour/config.d.ts.map +0 -1
  149. package/dist/components/tour/config.js +0 -22
  150. package/dist/components/tour/config.js.map +0 -1
  151. package/dist/components/tour/steps/PortalSwitcherStep.d.ts +0 -3
  152. package/dist/components/tour/steps/PortalSwitcherStep.d.ts.map +0 -1
  153. package/dist/components/tour/steps/PortalSwitcherStep.js +0 -30
  154. package/dist/components/tour/steps/PortalSwitcherStep.js.map +0 -1
  155. package/dist/components/tour/utils.d.ts +0 -9
  156. package/dist/components/tour/utils.d.ts.map +0 -1
  157. package/dist/components/tour/utils.js +0 -48
  158. package/dist/components/tour/utils.js.map +0 -1
  159. package/dist/layout-context.d.ts +0 -10
  160. package/dist/layout-context.d.ts.map +0 -1
  161. package/dist/layout-context.js +0 -11
  162. package/dist/layout-context.js.map +0 -1
  163. package/dist/svg/AI.d.ts +0 -6
  164. package/dist/svg/AI.d.ts.map +0 -1
  165. package/dist/svg/AI.js +0 -9
  166. package/dist/svg/AI.js.map +0 -1
  167. package/dist/svg/EDP.d.ts +0 -6
  168. package/dist/svg/EDP.d.ts.map +0 -1
  169. package/dist/svg/EDP.js +0 -5
  170. package/dist/svg/EDP.js.map +0 -1
  171. package/dist/svg/Forbidden.d.ts +0 -6
  172. package/dist/svg/Forbidden.d.ts.map +0 -1
  173. package/dist/svg/Forbidden.js +0 -4
  174. package/dist/svg/Forbidden.js.map +0 -1
  175. package/dist/svg/HUB.d.ts +0 -6
  176. package/dist/svg/HUB.d.ts.map +0 -1
  177. package/dist/svg/HUB.js +0 -5
  178. package/dist/svg/HUB.js.map +0 -1
  179. package/dist/svg/Logo.d.ts +0 -2
  180. package/dist/svg/Logo.d.ts.map +0 -1
  181. package/dist/svg/Logo.js +0 -4
  182. package/dist/svg/Logo.js.map +0 -1
  183. package/dist/svg/NotFound.d.ts +0 -6
  184. package/dist/svg/NotFound.d.ts.map +0 -1
  185. package/dist/svg/NotFound.js +0 -4
  186. package/dist/svg/NotFound.js.map +0 -1
  187. package/dist/svg/ServerError.d.ts +0 -6
  188. package/dist/svg/ServerError.d.ts.map +0 -1
  189. package/dist/svg/ServerError.js +0 -4
  190. package/dist/svg/ServerError.js.map +0 -1
  191. package/dist/svg/Unauthenticated.d.ts +0 -6
  192. package/dist/svg/Unauthenticated.d.ts.map +0 -1
  193. package/dist/svg/Unauthenticated.js +0 -4
  194. package/dist/svg/Unauthenticated.js.map +0 -1
  195. package/src/components/BottomNotification.tsx +0 -0
  196. package/src/components/BottomPanel.tsx +0 -0
  197. package/src/components/SelectionList.tsx +0 -272
  198. package/src/components/error/ErrorFeedback.tsx +0 -114
  199. package/src/components/menu/use-check-text-overflow.tsx +0 -26
  200. package/src/components/menu/use-keyboard-controls.tsx +0 -70
  201. package/src/layout-context.tsx +0 -22
  202. package/src/svg/AI.tsx +0 -37
  203. package/src/svg/EDP.tsx +0 -35
  204. package/src/svg/Forbidden.tsx +0 -22
  205. package/src/svg/HUB.tsx +0 -35
  206. package/src/svg/Logo.tsx +0 -35
  207. package/src/svg/NotFound.tsx +0 -16
  208. package/src/svg/ServerError.tsx +0 -33
  209. package/src/svg/Unauthenticated.tsx +0 -16
@@ -1,16 +1,21 @@
1
1
  /* eslint-disable react-hooks/rules-of-hooks */
2
2
 
3
3
  import { Button } from '@citric/core'
4
+ import { focusAccessibleElement, focusFirstChild } from '@stack-spot/portal-components'
4
5
  import { ReactElement, useLayoutEffect, useState } from 'react'
5
6
  import { Dialog, DialogOptions } from './components/Dialog'
6
7
  import { CLOSE_OVERLAY_ID, OverlayContent, OverlayContentProps } from './components/OverlayContent'
7
8
  import { getDictionary } from './dictionary'
8
9
  import { LayoutElements, elementIds, getLayoutElements } from './elements'
9
10
  import { ElementNotFound, LayoutError } from './errors'
10
- import { showToaster as showReactToaster } from './toaster'
11
- import { focusAccessibleElement, focusFirstChild, valueOfLayoutVar } from './utils'
11
+ import { CustomToasterOptions, DefaultToasterOptions, closeReactToaster, showToaster as showReactToaster } from './toaster'
12
+ import { valueOfLayoutVar } from './utils'
12
13
 
13
14
  interface AlertOptions extends Omit<DialogOptions, 'cancel'> {
15
+ /**
16
+ * Whether or not to show an "ok" button. If false, the dialog can still be closed through the close button, by clicking outside it or by
17
+ * pressing ESC.
18
+ */
14
19
  showButton?: boolean,
15
20
  }
16
21
 
@@ -26,12 +31,24 @@ interface OverlayContentSetter {
26
31
  }
27
32
 
28
33
  interface CustomModalOptions {
34
+ /**
35
+ * The size of the modal.
36
+ */
29
37
  size?: ModalSize,
38
+ /**
39
+ * A function to call when the modal closes.
40
+ */
30
41
  onClose?: () => void,
31
42
  }
32
43
 
33
44
  interface CustomRightPanelOptions {
45
+ /**
46
+ * The size of the right panel.
47
+ */
34
48
  size?: OverlaySize,
49
+ /**
50
+ * A function to call when the right panel closes.
51
+ */
35
52
  onClose?: () => void,
36
53
  }
37
54
 
@@ -72,9 +89,12 @@ class LayoutOverlayManager {
72
89
  this.setInteractivity(this.elements?.rightPanel, false)
73
90
  this.setInteractivity(this.elements?.bottomDialog, false)
74
91
  }
75
-
76
- // this should actually be like Kotlin's "internal", i.e. private to any user of the lib, but public to the lib itself.
77
- // @ts-ignore
92
+
93
+ /**
94
+ * Setup the overlay layout elements.
95
+ * @returns the content for the modal, rightPanel and bottomDialog.
96
+ */
97
+ // @ts-ignore: this should actually be like Kotlin's "internal", i.e. private to any user of the lib, but public to the lib itself.
78
98
  private useOverlays() {
79
99
  useLayoutEffect(() => {
80
100
  if (!this.elements) this.setupElements()
@@ -88,6 +108,11 @@ class LayoutOverlayManager {
88
108
  return { modal, rightPanel, bottomDialog }
89
109
  }
90
110
 
111
+ /**
112
+ * Enables or disables the interactivity of an element.
113
+ * @param element the element to have its interactivity changed.
114
+ * @param interactive false to disable interactivity, true to enable.
115
+ */
91
116
  private setInteractivity(element: HTMLElement | null | undefined, interactive: boolean) {
92
117
  if (interactive) {
93
118
  element?.removeAttribute('inert')
@@ -122,18 +147,36 @@ class LayoutOverlayManager {
122
147
  this.setMainContentInteractivity(true)
123
148
  }
124
149
 
150
+ /**
151
+ * @returns true if the modal is currently opened. False otherwise.
152
+ */
125
153
  isModalOpen() {
126
154
  return this.elements?.modal?.classList.contains('visible') ?? false
127
155
  }
128
156
 
157
+ /**
158
+ * @returns true if the right panel is currently opened. False otherwise.
159
+ */
129
160
  isRightPanelOpen() {
130
161
  return this.elements?.rightPanel?.classList.contains('visible') ?? false
131
162
  }
132
163
 
164
+ /**
165
+ * @returns true if the bottom dialog is currently opened. False otherwise.
166
+ */
133
167
  isBottomDialogOpen() {
134
168
  return this.elements?.bottomDialog?.classList.contains('visible') ?? false
135
169
  }
136
170
 
171
+ /**
172
+ * Opens a modal with custom content.
173
+ *
174
+ * Attention: the modal state must be declared within the modal. If the state is declared outside the modal, its content won't be updated
175
+ * accordingly. To force an update of an outside state, you need to call `showCustomModal` again with the new state value.
176
+ *
177
+ * @param content a react element with the modal content.
178
+ * @param options the modal options {@link CustomModalOptions}.
179
+ */
137
180
  showCustomModal(content: React.ReactElement, { size = 'medium', onClose }: CustomModalOptions = {}) {
138
181
  if (!this.elements?.modal) throw new ElementNotFound('modal', elementIds.modal)
139
182
  if (!this.setContent.modal) throw new LayoutError('unable to show modal, because it has not been setup yet.')
@@ -142,6 +185,14 @@ class LayoutOverlayManager {
142
185
  this.showOverlay(this.elements.modal, [size])
143
186
  }
144
187
 
188
+ /**
189
+ * Opens a modal.
190
+ *
191
+ * Attention: the modal state must be declared within the modal. If the state is declared outside the modal, its content won't be updated
192
+ * accordingly. To force an update of an outside state, you need to call `showModal` again with the new state value.
193
+ *
194
+ * @param options the modal options: {@link OverlayContentProps} & { size: {@link ModalSize} }.
195
+ */
145
196
  showModal({ size, ...props }: OverlayContentProps & { size?: ModalSize }) {
146
197
  this.showCustomModal(<OverlayContent {...props} onClose={() => this.closeModal()} type="modal" />, { size, onClose: props.onClose })
147
198
  }
@@ -167,23 +218,47 @@ class LayoutOverlayManager {
167
218
  })
168
219
  }
169
220
 
221
+ /**
222
+ * Shows a confirmation dialog and returns a promise that resolves as soon as the dialog is closed. The result of the promise is true if
223
+ * the user confirms and false otherwise.
224
+ *
225
+ * If you need the user to type something to confirm the action, use the property `validate` in the options parameter.
226
+ * @param options the dialog options: {@link DialogOptions}.
227
+ * @returns a promise that resolves with the user's answer.
228
+ */
170
229
  confirm({ confirm, cancel, ...options }: DialogOptions): Promise<boolean> {
171
230
  const t = getDictionary()
172
231
  return this.showDialog({ ...options, confirm: confirm || t.confirm, cancel: cancel || t.cancel })
173
232
  }
174
233
 
234
+ /**
235
+ * Shows an alert dialog and returns a promise that resolves as soon as the dialog is closed.
236
+ *
237
+ * @param options the dialog options: {@link AlertOptions}.
238
+ * @returns a promise that resolves to undefined as soon as the dialog is closed.
239
+ */
175
240
  async alert({ confirm, showButton = true, ...options }: AlertOptions): Promise<void> {
176
241
  const t = getDictionary()
177
242
  await this.showDialog({ ...options, confirm: showButton ? (confirm || t.confirm) : undefined })
178
243
  }
179
244
 
180
- showBottomDialog({ message, cancel, confirm }: BottomDialogOptions): Promise<boolean> {
245
+ /**
246
+ * Shows a message at the bottom of the window and asks the user to confirm or decline it. The return value is a promise that resolves as
247
+ * soon as the user presses one of the buttons. The result of the promise is true if the user confirms and false otherwise.
248
+ *
249
+ * Differently than `confirm` and `alert`, this message can only be closed if the user clicks one of the buttons or `closeBottomDialog`
250
+ * is called.
251
+ *
252
+ * @param options the dialog options: {@link BottomDialogOptions}.
253
+ * @returns a promise that resolves with the user's answer.
254
+ */
255
+ showBottomDialog({ children, cancel, confirm }: BottomDialogOptions): Promise<boolean> {
181
256
  if (!this.elements?.bottomDialog) throw new ElementNotFound('bottom dialog', elementIds.bottomDialog)
182
257
  if (!this.setContent.bottomDialog) throw new LayoutError('unable to show bottom dialog, because it has not been setup yet.')
183
258
  return new Promise((resolve) => {
184
259
  this.setContent.bottomDialog?.(
185
260
  <>
186
- {message}
261
+ {children}
187
262
  <div className="btn-group">
188
263
  {cancel && <Button onClick={() => resolve(false)} colorScheme="light" appearance="outlined">{cancel}</Button>}
189
264
  {confirm && <Button onClick={() => resolve(true)} colorScheme="light">{confirm}</Button>}
@@ -194,6 +269,16 @@ class LayoutOverlayManager {
194
269
  })
195
270
  }
196
271
 
272
+ /**
273
+ * Opens a right panel with custom content.
274
+ *
275
+ * Attention: the right panel state must be declared within the right panel. If the state is declared outside the right panel, its content
276
+ * won't be updated accordingly. To force an update of an outside state, you need to call `showCustomRightPanel` again with the new state
277
+ * value.
278
+ *
279
+ * @param content a react element with the modal content.
280
+ * @param options the modal options {@link CustomModalOptions}.
281
+ */
197
282
  showCustomRightPanel(content: ReactElement, { size = 'medium', onClose }: CustomRightPanelOptions = {}) {
198
283
  if (!this.elements?.rightPanel) throw new ElementNotFound('right panel overlay', elementIds.rightPanel)
199
284
  if (!this.setContent.rightPanel) throw new LayoutError('unable to show right panel overlay, because it has not been setup yet.')
@@ -205,6 +290,14 @@ class LayoutOverlayManager {
205
290
  })
206
291
  }
207
292
 
293
+ /**
294
+ * Opens a right panel.
295
+ *
296
+ * Attention: the right panel state must be declared within the right panel. If the state is declared outside the right panel, its content
297
+ * won't be updated accordingly. To force an update of an outside state, you need to call `showRightPanel` again with the new state value.
298
+ *
299
+ * @param options the modal options: {@link OverlayContentProps} & { size: {@link ModalSize} }.
300
+ */
208
301
  showRightPanel({ size, ...props }: OverlayContentProps & { size?: OverlaySize }) {
209
302
  this.showCustomRightPanel(
210
303
  <OverlayContent {...props} onClose={() => this.closeRightPanel()} type="panel" />,
@@ -212,7 +305,7 @@ class LayoutOverlayManager {
212
305
  )
213
306
  }
214
307
 
215
- /**
308
+ /*
216
309
  * Focus the element that had focus before the last overlay was opened. If the element is not visible anymore, another one that makes
217
310
  * sense (accessibility-wise) is focused.
218
311
  */
@@ -221,6 +314,10 @@ class LayoutOverlayManager {
221
314
  this.lastActiveElement = null
222
315
  }
223
316
 
317
+ /**
318
+ * Closes the modal if it's open.
319
+ * @param runCloseListener whether or not to run the function `onClose` passed to `showModal` or `showCustomModal`. Defaults to true.
320
+ */
224
321
  closeModal(runCloseListener = true) {
225
322
  this.elements?.modal?.classList.remove('visible')
226
323
  this.elements?.backdrop?.setAttribute('class', '')
@@ -246,6 +343,11 @@ class LayoutOverlayManager {
246
343
  )
247
344
  }
248
345
 
346
+ /**
347
+ * Closes the right panel if it's open.
348
+ * @param runCloseListener whether or not to run the function `onClose` passed to `showRightPanel` or `showCustomRightPanel`. Defaults to
349
+ * true.
350
+ */
249
351
  closeRightPanel(runCloseListener = true) {
250
352
  this.elements?.rightPanel?.classList.remove('visible')
251
353
  this.elements?.backdrop?.setAttribute('class', '')
@@ -271,19 +373,92 @@ class LayoutOverlayManager {
271
373
  )
272
374
  }
273
375
 
376
+ /**
377
+ * Closes the bottom dialog if it's open.
378
+ */
274
379
  closeBottomDialog() {
275
380
  this.hideOverlay(this.elements?.bottomDialog)
276
381
  }
277
382
 
383
+ /**
384
+ * Verifies if the HTML element passed as parameter is inside the modal.
385
+ * @param element the HTML element to check.
386
+ * @returns true if `element` is inside the modal; false otherwise.
387
+ */
278
388
  isInsideModal(element: HTMLElement) {
279
389
  return !!this.elements?.modal?.contains(element)
280
390
  }
281
391
 
392
+ /**
393
+ * Verifies if the HTML element passed as parameter is inside the right panel.
394
+ * @param element the HTML element to check.
395
+ * @returns true if `element` is inside the right panel; false otherwise.
396
+ */
282
397
  isInsideRightPanel(element: HTMLElement) {
283
398
  return !!this.elements?.rightPanel?.contains(element)
284
399
  }
285
400
 
286
- showToaster = showReactToaster
401
+ /**
402
+ * Shows a new toaster on the top right corner of the layout.
403
+ * @example
404
+ * ```
405
+ * overlay.showToaster({ title: 'Welcome', message: 'Hello World' })
406
+ * overlay.showToaster({
407
+ * title: 'Welcome',
408
+ * message: 'Hello World',
409
+ * actions: [
410
+ * { label: 'Got it!' },
411
+ * {
412
+ * label: 'Tell me more',
413
+ * closeOnClick: false,
414
+ * onClick: (event) => {
415
+ * // do something...
416
+ * },
417
+ * },
418
+ * ]
419
+ * })
420
+ * ```
421
+ * @param options the options for the toaster: {@link DefaultToasterOptions}.
422
+ * @returns the toaster's id.
423
+ */
424
+ showToaster(defaultToasterConfig: DefaultToasterOptions): number | string
425
+ /**
426
+ * Shows a fully customized toaster on the top right corner of the layout.
427
+ * @example
428
+ * ```
429
+ * overlay.showToaster({
430
+ * custom: true,
431
+ * message: <MyCustomToasterContent />,
432
+ * closeButton: <MyCustomCloseButton />,
433
+ * })
434
+ * ```
435
+ * @param options the options for the toaster: {@link CustomToasterOptions}.
436
+ * @returns the toaster's id.
437
+ */
438
+ showToaster(customToasterConfig: CustomToasterOptions): number | string
439
+ /**
440
+ * Shows the message passed as parameter in a new toaster on the top right corner of the layout.
441
+ * @example
442
+ * ```
443
+ * overlay.showToaster('Hello World!')
444
+ * overlay.showToaster(<p>Hello World</p>)
445
+ * ```
446
+ * @param message the message to show, can be either a string or React Element.
447
+ * @returns the toaster's id.
448
+ */
449
+ showToaster(message: React.ReactNode): number | string
450
+ showToaster(options: any): number | string {
451
+ return showReactToaster(options)
452
+ }
453
+
454
+ /**
455
+ * Closes the toaster with the specified id.
456
+ * @param id the id of the toaster to close.
457
+ */
458
+ closeToaster = closeReactToaster
287
459
  }
288
460
 
461
+ /**
462
+ * Manages overlay components of the layout like: modal, rightPanel, bottomDialog and toaster.
463
+ */
289
464
  export const overlay = new LayoutOverlayManager()
@@ -6,35 +6,73 @@ import { useDictionary } from '../dictionary'
6
6
  import { OverlayContent } from './OverlayContent'
7
7
 
8
8
  interface Validation {
9
+ /**
10
+ * The expected value of the field.
11
+ */
9
12
  value: string,
13
+ /**
14
+ * A custom label for the input field.
15
+ */
10
16
  label?: string | ReactNode,
17
+ /**
18
+ * A placeholder for the input field.
19
+ */
11
20
  placeholder?: string,
12
21
  }
13
22
 
14
23
  export interface DialogOptions {
24
+ /**
25
+ * The dialog's title.
26
+ */
15
27
  title: string,
28
+ /**
29
+ * The dialog's subtitle.
30
+ */
16
31
  subtitle?: string,
17
- message: ReactNode,
32
+ /**
33
+ * The dialog's content.
34
+ */
35
+ children: ReactNode,
36
+ /**
37
+ * A text for the confirm button. If left blank, the confirm button won't render.
38
+ */
18
39
  confirm?: string,
40
+ /**
41
+ * A text for the cancel button. If left blank, the cancel button won't render.
42
+ */
19
43
  cancel?: string,
44
+ /**
45
+ * A validation input field. If the validation is incorrect, the confirm button is disabled.
46
+ * If this is a string, the validation input's value must be equal to this string in order to enable the confirm button.
47
+ * If this is an object, a value, label and placeholder may be specified.
48
+ * Use false or undefined to not include a validation field.
49
+ */
20
50
  validation?: false | string | Validation,
21
51
  /**
52
+ * If this is a modal or a rightPanel.
22
53
  * @default modal
23
54
  */
24
55
  type?: 'modal' | 'panel',
25
56
  /**
26
- * @default right if type is "panel", "right" otherwise.
57
+ * The placement for the confirm/cancel buttons.
58
+ * @default "left" if type is "panel", "right" otherwise.
27
59
  */
28
60
  buttonPlacement?: 'left' | 'center' | 'right',
29
61
  /**
30
62
  * The color of the primary button.
31
- * @default primary
63
+ * @default "primary"
32
64
  */
33
65
  buttonColor?: ColorSchemeName,
34
66
  }
35
67
 
36
- interface Props extends DialogOptions {
68
+ interface Props extends Omit<DialogOptions, 'message'> {
69
+ /**
70
+ * Function to run when the confirm button is clicked.
71
+ */
37
72
  onConfirm: () => void,
73
+ /**
74
+ * Function to run when the cancel button is clicked.
75
+ */
38
76
  onCancel: () => void,
39
77
  }
40
78
 
@@ -44,8 +82,13 @@ const justifyButtons: Record<Required<DialogOptions>['buttonPlacement'], React.C
44
82
  right: 'end',
45
83
  }
46
84
 
85
+ /**
86
+ * A dialog, i.e. A UI with title, subtitle, close button, content and a set of buttons: cancel and confirm. May be placed under a Modal or
87
+ * RightPanel.
88
+ * @param props the React props of the component {@link Props}.
89
+ */
47
90
  export const Dialog = ({
48
- message,
91
+ children,
49
92
  title,
50
93
  subtitle,
51
94
  cancel,
@@ -83,7 +126,7 @@ export const Dialog = ({
83
126
  return (
84
127
  <OverlayContent title={title} subtitle={subtitle} onClose={onCancel} type={type}>
85
128
  <Flex flexDirection="column" flex={1}>
86
- {message}
129
+ {children}
87
130
  {renderValidation()}
88
131
  </Flex>
89
132
  {(cancel || confirm) && <Flex gap justifyContent={justifyButtons[buttonPlacement]} alignItems="center" sx={{ mt: 6 }}>
@@ -1,22 +1,50 @@
1
1
  import { Flex } from '@citric/core'
2
+ import { SelectionListProps } from '@stack-spot/portal-components/SelectionList'
3
+ import { useAnchorTag } from '@stack-spot/portal-components/anchor'
4
+ import { Logo } from '@stack-spot/portal-components/svg'
2
5
  import { ReactNode } from 'react'
3
- import { useAnchorTag } from '../layout-context'
4
- import { Logo } from '../svg/Logo'
5
6
  import { PortalSwitcher, PortalSwitcherProps } from './PortalSwitcher'
6
- import { SelectionListProps } from './SelectionList'
7
7
  import { UserMenu } from './UserMenu'
8
8
 
9
9
  export interface HeaderProps {
10
+ /**
11
+ * The logo to show in the header, if the portal switch feature is disabled.
12
+ */
10
13
  logo?: ReactNode,
14
+ /**
15
+ * The url the logo links to, if the portal switch feature is disabled.
16
+ */
11
17
  logoHref?: string,
18
+ /**
19
+ * The username to show in the user menu.
20
+ */
12
21
  userName?: string,
22
+ /**
23
+ * The email to show in the user menu.
24
+ */
13
25
  email?: string,
26
+ /**
27
+ * A portal switch config. If not specified, disables the portal switch feature.
28
+ */
14
29
  portalSwitch?: PortalSwitcherProps['portals'],
30
+ /**
31
+ * The options to show in the user menu.
32
+ */
15
33
  options?: SelectionListProps['items'],
34
+ /**
35
+ * A custom React Node to show at the center of the header. This is a good place for a search field, for instance.
36
+ */
16
37
  center?: ReactNode,
38
+ /**
39
+ * A custom React Node to show at the right (end) of the header.
40
+ */
17
41
  right?: ReactNode,
18
42
  }
19
43
 
44
+ /**
45
+ * The page header.
46
+ * @param props the React props for the header {@link HeaderProps}.
47
+ */
20
48
  export const Header = ({ logo, logoHref, center, right, userName, email, options, portalSwitch }: HeaderProps) => {
21
49
  const Link = useAnchorTag()
22
50
 
@@ -9,14 +9,32 @@ import { useDictionary } from '../dictionary'
9
9
  export const CLOSE_OVERLAY_ID = 'close-overlay'
10
10
 
11
11
  export interface OverlayContentProps extends WithStyle {
12
+ /**
13
+ * The title for the modal or right panel.
14
+ */
12
15
  title: string,
16
+ /**
17
+ * The subtitle for the modal or right panel.
18
+ */
13
19
  subtitle?: string,
20
+ /**
21
+ * The content for the modal or right panel.
22
+ */
14
23
  children: ReactNode,
24
+ /**
25
+ * A function to run when the close button is pressed.
26
+ */
15
27
  onClose?: () => void,
16
28
  }
17
29
 
18
30
  interface Props extends OverlayContentProps {
31
+ /**
32
+ * A function to run when the close button is pressed.
33
+ */
19
34
  onClose: () => void,
35
+ /**
36
+ * Whether this is a modal or a right panel.
37
+ */
20
38
  type: 'modal' | 'panel',
21
39
  }
22
40
 
@@ -41,6 +59,10 @@ const ContentBox = styled.section`
41
59
  }
42
60
  `
43
61
 
62
+ /**
63
+ * Renders a modal or right panel with title, subtitle, close button and content.
64
+ * @param props the React props for the component {@link Props}.
65
+ */
44
66
  export const OverlayContent = ({ children, title, subtitle, className, style, onClose, type }: Props) => {
45
67
  const t = useDictionary()
46
68
  return (
@@ -1,26 +1,35 @@
1
1
  import { Button, Flex, IconBox, Text } from '@citric/core'
2
2
  import { ArrowRight, CheckCircleFill, Select } from '@citric/icons'
3
+ import { SelectionList } from '@stack-spot/portal-components/SelectionList'
4
+ import { AI, EDP, HUB, Logo } from '@stack-spot/portal-components/svg'
3
5
  import { theme } from '@stack-spot/portal-theme'
4
6
  import { useTranslate } from '@stack-spot/portal-translate'
5
7
  import { ReactNode, useState } from 'react'
6
8
  import styled from 'styled-components'
7
- import { SelectionList, announce } from '..'
8
- import { AI } from '../svg/AI'
9
- import { EDP } from '../svg/EDP'
10
- import { HUB } from '../svg/HUB'
11
- import { Logo } from '../svg/Logo'
9
+ import { announce } from '../utils'
12
10
  import { PortalAcronym } from './types'
13
11
 
14
-
15
12
  const Logos: Record<PortalAcronym, ReactNode> = {
16
13
  'AI': <AI />,
17
14
  'EDP': <EDP />,
18
15
  'HUB': <HUB />,
19
16
  }
20
17
 
21
- export interface Portal { acronym: PortalAcronym, url: string }
18
+ export interface Portal {
19
+ /**
20
+ * A Stackspot Portal.
21
+ */
22
+ acronym: PortalAcronym,
23
+ /**
24
+ * The URL to the Stackspot Portal.
25
+ */
26
+ url: string,
27
+ }
22
28
 
23
29
  export interface PortalSwitcherProps {
30
+ /**
31
+ * The Stackspot portals to show in the selector.
32
+ */
24
33
  portals?: Portal[],
25
34
  }
26
35
 
@@ -80,6 +89,11 @@ const PortalSwitcherBox = styled(Flex)`
80
89
  `
81
90
  const PORTAL_SWITCHER_ID = 'PortalSwitcher'
82
91
 
92
+ /**
93
+ * A selector with different Stackspot portals.
94
+ * Each item contains a logo with a link to the portal.
95
+ * @param props the component Props {@link PortalSwitcherProps}.
96
+ */
83
97
  export const PortalSwitcher = ({ portals = [] }: PortalSwitcherProps) => {
84
98
  const [visible, setVisible] = useState<boolean>(false)
85
99
  const t = useTranslate(translations)
@@ -140,7 +154,7 @@ const translations = {
140
154
  pt: {
141
155
  EDP: 'Soluções eficientes e seguras do código até a implantação em produção.',
142
156
  AI: 'Acelere o desenvolvimento com sugestões eficientes e resultados de alta qualidade.',
143
- HUB: 'Descubra AI Stacks, knownledge sources e quick commands, tudo em um hub simplificado.',
157
+ HUB: 'Descubra AI Stacks, knowledge sources e quick commands, tudo em um hub simplificado.',
144
158
  portalSwitcher: 'Seletor de portais',
145
159
  selected: 'selecionado',
146
160
  },
@@ -1,16 +1,24 @@
1
1
  import { TimesMini } from '@citric/icons'
2
2
  import { IconButton } from '@citric/ui'
3
- import { CloseButtonProps, ToastContainer } from 'react-toastify'
3
+ import type { CloseButton as DefaultCloseButton } from 'react-toastify'
4
+ import { ToastContainer } from 'react-toastify'
4
5
  import 'react-toastify/dist/ReactToastify.css'
5
6
  import { useDictionary } from '../dictionary'
6
7
 
8
+ type CloseButtonProps = Parameters<typeof DefaultCloseButton>[0]
9
+
10
+ export const TOASTER_CLOSE_BTN_CLASS = 'btn-close'
11
+
7
12
  const CloseButton = ({ closeToast }: CloseButtonProps) => {
8
13
  const t = useDictionary()
9
14
  return (
10
- <IconButton onClick={() => closeToast(null as any)} title={t.dismiss}>
15
+ <IconButton className={TOASTER_CLOSE_BTN_CLASS} onClick={closeToast} title={t.dismiss}>
11
16
  <TimesMini />
12
17
  </IconButton>
13
18
  )
14
19
  }
15
20
 
21
+ /**
22
+ * Uses react-toastify to render a Toaster based on the Citric DS.
23
+ */
16
24
  export const Toaster = () => <ToastContainer closeButton={CloseButton} />
@@ -1,15 +1,24 @@
1
1
  import { Flex, IconBox, LinkBox, Text } from '@citric/core'
2
2
  import { ChevronDown } from '@citric/icons'
3
3
  import { Avatar } from '@citric/ui'
4
+ import { SelectionList, SelectionListProps } from '@stack-spot/portal-components/SelectionList'
4
5
  import { theme } from '@stack-spot/portal-theme'
5
6
  import { Dictionary, interpolate, useTranslate } from '@stack-spot/portal-translate'
6
7
  import { useState } from 'react'
7
8
  import { styled } from 'styled-components'
8
- import { SelectionList, SelectionListProps } from './SelectionList'
9
9
 
10
10
  interface Props {
11
+ /**
12
+ * The user name.
13
+ */
11
14
  userName: string,
15
+ /**
16
+ * The user email.
17
+ */
12
18
  email?: string,
19
+ /**
20
+ * The menu options.
21
+ */
13
22
  options?: SelectionListProps['items'],
14
23
  }
15
24
 
@@ -65,6 +74,10 @@ const UserMenuHeader = ({ userName, email }: Omit<Props, 'options'>) => (
65
74
  </div>
66
75
  )
67
76
 
77
+ /**
78
+ * Renders the user menu.
79
+ * @param props the component's props {@link Props}.
80
+ */
68
81
  export const UserMenu = ({ userName, email, options }: Props) => {
69
82
  const t = useTranslate(dictionary)
70
83
  const [visible, setVisible] = useState(false)
@@ -1,6 +1,6 @@
1
+ import { ErrorDescription, ErrorFeedback } from '@stack-spot/portal-components/ErrorFeedback'
1
2
  import { Component } from 'react'
2
- import { ErrorFeedback } from './ErrorFeedback'
3
- import { ErrorDescription, ErrorManager } from './ErrorManager'
3
+ import { ErrorManager } from './ErrorManager'
4
4
 
5
5
  interface State extends ErrorDescription {
6
6
  hasError: boolean,
@@ -10,6 +10,15 @@ interface Props {
10
10
  children: React.ReactNode,
11
11
  }
12
12
 
13
+ /**
14
+ * An Error Boundary that renders an ErrorFeedback instead of its content if any of its children throws.
15
+ *
16
+ * To customize what properties are passed to the ErrorFeedback component, setup an error descriptor for the ErrorManager class. If you're
17
+ * using the component `Layout` or `RawLayout`, you can use the property `errorDescriptor`.
18
+ *
19
+ * To run an error handler every time an error is catch by this boundary, setup an error handler for the ErrorManager class. If you're
20
+ * using the component `Layout` or `RawLayout`, you can use the property `onError`.
21
+ */
13
22
  export class ErrorBoundary extends Component<Props, State> {
14
23
  constructor(props: Props) {
15
24
  super(props)