@stack-spot/portal-components 2.26.0 → 2.27.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 (237) hide show
  1. package/CHANGELOG.md +621 -614
  2. package/dist/components/AnimatedHeight.d.ts +1 -1
  3. package/dist/components/AnimatedHeight.js +26 -26
  4. package/dist/components/AsyncContent.d.ts +1 -1
  5. package/dist/components/AsyncContent.js +1 -1
  6. package/dist/components/BannerWarning.d.ts +1 -1
  7. package/dist/components/BannerWarning.js +1 -1
  8. package/dist/components/Breadcrumb/index.d.ts +2 -2
  9. package/dist/components/Breadcrumb/index.js +1 -1
  10. package/dist/components/Breadcrumb/styled.js +31 -31
  11. package/dist/components/ButtonLoading.d.ts +1 -1
  12. package/dist/components/ButtonLoading.js +1 -1
  13. package/dist/components/ChatBot.d.ts +1 -1
  14. package/dist/components/ChatBot.js +1 -1
  15. package/dist/components/ContentValidateFilter.d.ts +1 -1
  16. package/dist/components/ContentValidateFilter.js +1 -1
  17. package/dist/components/FadingOverflow.d.ts +1 -1
  18. package/dist/components/FadingOverflow.js +69 -69
  19. package/dist/components/FileTreeView/More.d.ts +1 -1
  20. package/dist/components/FileTreeView/More.js +1 -1
  21. package/dist/components/FileTreeView/index.d.ts +1 -1
  22. package/dist/components/FileTreeView/index.js +1 -1
  23. package/dist/components/InfiniteScroll.d.ts +1 -1
  24. package/dist/components/InfiniteScroll.js +1 -1
  25. package/dist/components/InfoMaintenanceBanner.d.ts +1 -1
  26. package/dist/components/InfoMaintenanceBanner.js +2 -2
  27. package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +1 -1
  28. package/dist/components/LazyMarkdown/BlockquoteMd.js +1 -1
  29. package/dist/components/LazyMarkdown/CodeViewer.d.ts +1 -1
  30. package/dist/components/LazyMarkdown/CodeViewer.js +76 -76
  31. package/dist/components/LazyMarkdown/Markdown.d.ts +1 -1
  32. package/dist/components/LazyMarkdown/Markdown.js +1 -1
  33. package/dist/components/LazyMarkdown/MarkdownButton.d.ts +1 -1
  34. package/dist/components/LazyMarkdown/MarkdownButton.js +1 -1
  35. package/dist/components/LazyMarkdown/Video.d.ts +1 -1
  36. package/dist/components/LazyMarkdown/Video.js +1 -1
  37. package/dist/components/LazyMarkdown/index.d.ts +1 -1
  38. package/dist/components/LazyMarkdown/index.js +1 -1
  39. package/dist/components/Placeholder.d.ts +3 -3
  40. package/dist/components/Placeholder.js +1 -1
  41. package/dist/components/ScrollView.js +16 -16
  42. package/dist/components/Select/BadgeItem.d.ts +1 -1
  43. package/dist/components/Select/BadgeItem.js +1 -1
  44. package/dist/components/Select/ClearInput.d.ts +1 -1
  45. package/dist/components/Select/ClearInput.js +1 -1
  46. package/dist/components/Select/CloseItem.d.ts +1 -1
  47. package/dist/components/Select/CloseItem.js +1 -1
  48. package/dist/components/Select/CreatableSelect.js +1 -1
  49. package/dist/components/Select/CustomMenu.d.ts +1 -1
  50. package/dist/components/Select/CustomMenu.js +1 -1
  51. package/dist/components/Select/LabelItem.d.ts +1 -1
  52. package/dist/components/Select/LabelItem.js +1 -1
  53. package/dist/components/Select/MultiValue.d.ts +1 -1
  54. package/dist/components/Select/MultiValue.js +1 -1
  55. package/dist/components/Select/SelectInfiniteScroll.d.ts +1 -1
  56. package/dist/components/Select/SelectInfiniteScroll.js +1 -1
  57. package/dist/components/Select/SelectSearch.d.ts +1 -1
  58. package/dist/components/Select/SelectSearch.js +1 -1
  59. package/dist/components/SelectionList.d.ts +1 -1
  60. package/dist/components/SelectionList.js +61 -61
  61. package/dist/components/StatusCircle.d.ts +1 -1
  62. package/dist/components/StatusCircle.js +6 -6
  63. package/dist/components/Stepper/Navigation.js +4 -4
  64. package/dist/components/Stepper/Step.js +3 -3
  65. package/dist/components/Stepper/Stepper.js +6 -6
  66. package/dist/components/Stepper/headers.js +22 -22
  67. package/dist/components/Table/HeaderItem.js +1 -1
  68. package/dist/components/Table/SettingsVerticalMenu.d.ts +1 -1
  69. package/dist/components/Table/SettingsVerticalMenu.js +1 -1
  70. package/dist/components/Table/StyledLinkTable.d.ts +1 -1
  71. package/dist/components/Table/StyledLinkTable.js +5 -5
  72. package/dist/components/Table/TableData.d.ts +1 -1
  73. package/dist/components/Table/TableData.js +25 -25
  74. package/dist/components/TimelineSection.d.ts +1 -1
  75. package/dist/components/TimelineSection.js +14 -14
  76. package/dist/components/error/ErrorFeedback.d.ts +1 -1
  77. package/dist/components/error/ErrorFeedback.js +35 -35
  78. package/dist/components/error/NotFound.d.ts +1 -1
  79. package/dist/components/error/NotFound.js +1 -1
  80. package/dist/components/error/UnderMaintenance.d.ts +1 -1
  81. package/dist/components/error/UnderMaintenance.js +1 -1
  82. package/dist/components/form/Form/Form.d.ts +1 -1
  83. package/dist/components/form/Form/Form.js +1 -1
  84. package/dist/components/form/Form/FormGroup.d.ts +2 -2
  85. package/dist/components/form/Form/FormGroup.js +1 -1
  86. package/dist/components/form/SearchInput.d.ts +1 -1
  87. package/dist/components/form/SearchInput.js +1 -1
  88. package/dist/components/form/Select/CustomSelect.d.ts +1 -1
  89. package/dist/components/form/Select/CustomSelect.js +1 -1
  90. package/dist/components/form/Select/DetailedSelect.d.ts +1 -1
  91. package/dist/components/form/Select/DetailedSelect.js +1 -1
  92. package/dist/components/form/Select/Select.d.ts +1 -1
  93. package/dist/components/form/Select/Select.js +1 -1
  94. package/dist/components/form/Select/styled.js +161 -161
  95. package/dist/components/form/Select/utils.js +1 -1
  96. package/dist/components/notification/NotificationComponent.d.ts +1 -1
  97. package/dist/components/notification/NotificationComponent.js +54 -54
  98. package/dist/components/notification/NotificationItem.d.ts +1 -1
  99. package/dist/components/notification/NotificationItem.js +1 -1
  100. package/dist/components/notification/NotificationList.d.ts +1 -1
  101. package/dist/components/notification/NotificationList.js +43 -43
  102. package/dist/components/notification/NotificationPlaceholder.d.ts +1 -1
  103. package/dist/components/notification/NotificationPlaceholder.js +9 -9
  104. package/dist/components/notification/NotificationPlaceholder.js.map +1 -1
  105. package/dist/containers/NotificationsPage.d.ts +1 -1
  106. package/dist/containers/NotificationsPage.js +10 -10
  107. package/dist/context/anchor.d.ts +1 -1
  108. package/dist/context/anchor.js +1 -1
  109. package/dist/context/loading.d.ts +1 -1
  110. package/dist/context/loading.js +1 -1
  111. package/dist/context/notification/context.d.ts +1 -1
  112. package/dist/context/notification/context.js +1 -1
  113. package/dist/hooks/date.js +1 -1
  114. package/dist/hooks/service-now.js +28 -28
  115. package/dist/svg/AI.d.ts +1 -1
  116. package/dist/svg/AI.js +1 -1
  117. package/dist/svg/CS.d.ts +1 -1
  118. package/dist/svg/CS.js +1 -1
  119. package/dist/svg/EDP.d.ts +1 -1
  120. package/dist/svg/EDP.js +1 -1
  121. package/dist/svg/Forbidden.d.ts +1 -1
  122. package/dist/svg/Forbidden.js +1 -1
  123. package/dist/svg/GenericPlaceholder.d.ts +1 -1
  124. package/dist/svg/GenericPlaceholder.js +1 -1
  125. package/dist/svg/HUB.d.ts +1 -1
  126. package/dist/svg/HUB.js +1 -1
  127. package/dist/svg/Logo.d.ts +1 -1
  128. package/dist/svg/Logo.js +1 -1
  129. package/dist/svg/MiniLogo.d.ts +1 -1
  130. package/dist/svg/MiniLogo.js +1 -1
  131. package/dist/svg/NotFound.d.ts +1 -1
  132. package/dist/svg/NotFound.js +1 -1
  133. package/dist/svg/ServerError.d.ts +1 -1
  134. package/dist/svg/ServerError.js +1 -1
  135. package/dist/svg/Unauthenticated.d.ts +1 -1
  136. package/dist/svg/Unauthenticated.js +1 -1
  137. package/package.json +6 -6
  138. package/readme.md +66 -66
  139. package/src/components/AnimatedHeight.tsx +174 -174
  140. package/src/components/AsyncContent.tsx +78 -78
  141. package/src/components/BannerWarning.tsx +91 -91
  142. package/src/components/Breadcrumb/index.tsx +76 -76
  143. package/src/components/Breadcrumb/styled.ts +37 -37
  144. package/src/components/ButtonLoading.tsx +29 -29
  145. package/src/components/ChatBot.tsx +82 -82
  146. package/src/components/ContentValidateFilter.tsx +15 -15
  147. package/src/components/FadingOverflow.tsx +265 -265
  148. package/src/components/FileTreeView/More.tsx +114 -114
  149. package/src/components/FileTreeView/index.tsx +186 -186
  150. package/src/components/InfiniteScroll.tsx +24 -24
  151. package/src/components/InfoMaintenanceBanner.tsx +29 -29
  152. package/src/components/LazyMarkdown/BlockquoteMd.tsx +107 -107
  153. package/src/components/LazyMarkdown/CodeViewer.tsx +161 -161
  154. package/src/components/LazyMarkdown/Markdown.tsx +122 -122
  155. package/src/components/LazyMarkdown/MarkdownButton.tsx +24 -24
  156. package/src/components/LazyMarkdown/Video.tsx +13 -13
  157. package/src/components/LazyMarkdown/index.tsx +21 -21
  158. package/src/components/Placeholder.tsx +118 -118
  159. package/src/components/ScrollView.tsx +57 -57
  160. package/src/components/Select/BadgeItem.tsx +58 -58
  161. package/src/components/Select/ClearInput.tsx +24 -24
  162. package/src/components/Select/CloseItem.tsx +38 -38
  163. package/src/components/Select/CreatableSelect.tsx +155 -155
  164. package/src/components/Select/CustomMenu.tsx +16 -16
  165. package/src/components/Select/LabelItem.tsx +8 -8
  166. package/src/components/Select/MultiValue.tsx +49 -49
  167. package/src/components/Select/SelectInfiniteScroll.tsx +82 -82
  168. package/src/components/Select/SelectSearch.tsx +195 -195
  169. package/src/components/Select/index.tsx +7 -7
  170. package/src/components/Select/types.ts +8 -8
  171. package/src/components/SelectionList.tsx +427 -427
  172. package/src/components/StatusCircle.tsx +67 -67
  173. package/src/components/Stepper/Navigation.tsx +97 -97
  174. package/src/components/Stepper/Step.tsx +30 -30
  175. package/src/components/Stepper/Stepper.tsx +113 -113
  176. package/src/components/Stepper/headers.tsx +64 -64
  177. package/src/components/Stepper/index.ts +3 -3
  178. package/src/components/Table/HeaderItem.tsx +52 -52
  179. package/src/components/Table/SettingsVerticalMenu.tsx +50 -50
  180. package/src/components/Table/StyledLinkTable.tsx +22 -22
  181. package/src/components/Table/TableData.tsx +251 -251
  182. package/src/components/Table/index.tsx +2 -2
  183. package/src/components/TimelineSection.tsx +66 -66
  184. package/src/components/error/ErrorFeedback.tsx +217 -217
  185. package/src/components/error/NotFound.tsx +24 -24
  186. package/src/components/error/UnderMaintenance.tsx +30 -30
  187. package/src/components/error/index.ts +4 -4
  188. package/src/components/form/Form/Form.tsx +101 -101
  189. package/src/components/form/Form/FormGroup.tsx +221 -221
  190. package/src/components/form/Form/index.ts +2 -2
  191. package/src/components/form/SearchInput.tsx +69 -69
  192. package/src/components/form/Select/CustomSelect.tsx +232 -232
  193. package/src/components/form/Select/DetailedSelect.tsx +85 -85
  194. package/src/components/form/Select/Select.tsx +67 -67
  195. package/src/components/form/Select/index.ts +4 -4
  196. package/src/components/form/Select/styled.ts +165 -165
  197. package/src/components/form/Select/types.ts +112 -112
  198. package/src/components/form/Select/utils.tsx +28 -28
  199. package/src/components/notification/NotificationComponent.tsx +340 -340
  200. package/src/components/notification/NotificationItem.tsx +336 -336
  201. package/src/components/notification/NotificationList.tsx +178 -178
  202. package/src/components/notification/NotificationPlaceholder.tsx +43 -43
  203. package/src/components/notification/types.ts +72 -72
  204. package/src/containers/NotificationsPage.tsx +98 -98
  205. package/src/context/anchor.tsx +37 -37
  206. package/src/context/loading.tsx +36 -36
  207. package/src/context/notification/LazyNotificationList.ts +103 -103
  208. package/src/context/notification/NotificationController.ts +104 -104
  209. package/src/context/notification/context.tsx +23 -23
  210. package/src/context/notification/hooks.ts +98 -98
  211. package/src/context/notification/types.ts +65 -65
  212. package/src/hooks/date.ts +31 -31
  213. package/src/hooks/keyboard.tsx +128 -128
  214. package/src/hooks/manual-render.tsx +10 -10
  215. package/src/hooks/service-now.tsx +233 -233
  216. package/src/hooks/text.tsx +30 -30
  217. package/src/hooks/title.tsx +28 -28
  218. package/src/hooks/use-effect-once.tsx +43 -43
  219. package/src/index.ts +19 -19
  220. package/src/notifications.ts +11 -11
  221. package/src/svg/AI.tsx +41 -41
  222. package/src/svg/CS.tsx +48 -48
  223. package/src/svg/EDP.tsx +31 -31
  224. package/src/svg/Forbidden.tsx +22 -22
  225. package/src/svg/GenericPlaceholder.tsx +20 -20
  226. package/src/svg/HUB.tsx +48 -48
  227. package/src/svg/Logo.tsx +16 -16
  228. package/src/svg/MiniLogo.tsx +12 -12
  229. package/src/svg/NotFound.tsx +16 -16
  230. package/src/svg/ServerError.tsx +33 -33
  231. package/src/svg/Unauthenticated.tsx +16 -16
  232. package/src/svg/index.ts +11 -11
  233. package/src/utils/accessibility.ts +135 -135
  234. package/src/utils/cookie.ts +73 -73
  235. package/src/utils/promise.ts +5 -5
  236. package/src/utils/read-file.ts +16 -16
  237. package/tsconfig.json +10 -10
@@ -1,98 +1,98 @@
1
- import { Text } from '@citric/core'
2
- import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
3
- import { styled } from 'styled-components'
4
- import { AsyncContent } from '../components/AsyncContent'
5
- import { ErrorFeedback } from '../components/error'
6
- import { SearchInput } from '../components/form/SearchInput'
7
- import { Select } from '../components/form/Select'
8
- import { NotificationList } from '../components/notification/NotificationList'
9
- import { useNotificationList } from '../context/notification/hooks'
10
- import { NotificationContext, NotificationPriority } from '../context/notification/types'
11
- import { useNotificationController } from '../notifications'
12
-
13
- const FilterBox = styled.div`
14
- margin: 24px 0;
15
- display: flex;
16
- flex-direction: row;
17
- gap: 8px;
18
-
19
- > * {
20
- flex: 1;
21
- }
22
- `
23
-
24
- const criticalities: NotificationPriority[] = ['HIGH', 'MEDIUM', 'LOW']
25
- const contexts: NotificationContext[] = ['ACCOUNT', 'STUDIO', 'WORKSPACE']
26
-
27
- export const NotificationsPage = () => {
28
- const t = useTranslate(dictionary)
29
- const controller = useNotificationController()
30
- const { hasMore, items, loadMore, applyFilters, filters, status, error } = useNotificationList()
31
-
32
- return (<>
33
- <header><Text appearance="h2" as="h1">{t.title}</Text></header>
34
- <Text appearance="body2" colorScheme="light.700">
35
- {t.description}
36
- </Text>
37
- <FilterBox>
38
- <SearchInput searchText={t.filter} defaultValue={filters.search} onChange={search => applyFilters({ search })} />
39
- <Select
40
- value={filters.criticality}
41
- options={criticalities}
42
- onChange={criticality => applyFilters({ criticality })}
43
- emptyOption={t.allCriticalities}
44
- renderLabel={o => t[`criticality.${o}`]}
45
- />
46
- <Select
47
- value={filters.context}
48
- options={contexts}
49
- onChange={context => applyFilters({ context })}
50
- emptyOption={t.allContexts}
51
- renderLabel={o => t[`context.${o}`]}
52
- />
53
- </FilterBox>
54
- <AsyncContent
55
- error={error}
56
- loading={status === 'startup'}
57
- errorDetails={{ errorComponent: () => <ErrorFeedback code={error.code} />, reportError: () => { } }}
58
- >
59
- <NotificationList
60
- items={items}
61
- loading={status === 'loading'}
62
- onCommit={id => controller.markAsCommitted(id)}
63
- infiniteScroll={{ hasMore, loadMore }}
64
- onClickAction={controller.config.onClickAction}
65
- showEmptySearch
66
- />
67
- </AsyncContent>
68
- </>)
69
- }
70
-
71
- const dictionary = {
72
- en: {
73
- title: 'Notifications',
74
- description: 'Here you can view all your received notifications.',
75
- filter: 'Filter',
76
- allCriticalities: 'All severities',
77
- 'criticality.HIGH': 'High',
78
- 'criticality.MEDIUM': 'Medium',
79
- 'criticality.LOW': 'Low',
80
- allContexts: 'All contexts',
81
- 'context.ACCOUNT': 'Account',
82
- 'context.STUDIO': 'Studio',
83
- 'context.WORKSPACE': 'Workspace',
84
- },
85
- pt: {
86
- title: 'Notificações',
87
- description: 'Aqui você encontra todas as notificações recebidas.',
88
- filter: 'Filtrar',
89
- allCriticalities: 'Todas as criticidades',
90
- 'criticality.HIGH': 'Alto',
91
- 'criticality.MEDIUM': 'Médio',
92
- 'criticality.LOW': 'Baixo',
93
- allContexts: 'Todos os contextos',
94
- 'context.ACCOUNT': 'Conta',
95
- 'context.STUDIO': 'Estúdio',
96
- 'context.WORKSPACE': 'Workspace',
97
- },
98
- } satisfies Dictionary
1
+ import { Text } from '@citric/core'
2
+ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
3
+ import { styled } from 'styled-components'
4
+ import { AsyncContent } from '../components/AsyncContent'
5
+ import { ErrorFeedback } from '../components/error'
6
+ import { SearchInput } from '../components/form/SearchInput'
7
+ import { Select } from '../components/form/Select'
8
+ import { NotificationList } from '../components/notification/NotificationList'
9
+ import { useNotificationList } from '../context/notification/hooks'
10
+ import { NotificationContext, NotificationPriority } from '../context/notification/types'
11
+ import { useNotificationController } from '../notifications'
12
+
13
+ const FilterBox = styled.div`
14
+ margin: 24px 0;
15
+ display: flex;
16
+ flex-direction: row;
17
+ gap: 8px;
18
+
19
+ > * {
20
+ flex: 1;
21
+ }
22
+ `
23
+
24
+ const criticalities: NotificationPriority[] = ['HIGH', 'MEDIUM', 'LOW']
25
+ const contexts: NotificationContext[] = ['ACCOUNT', 'STUDIO', 'WORKSPACE']
26
+
27
+ export const NotificationsPage = () => {
28
+ const t = useTranslate(dictionary)
29
+ const controller = useNotificationController()
30
+ const { hasMore, items, loadMore, applyFilters, filters, status, error } = useNotificationList()
31
+
32
+ return (<>
33
+ <header><Text appearance="h2" as="h1">{t.title}</Text></header>
34
+ <Text appearance="body2" colorScheme="light.700">
35
+ {t.description}
36
+ </Text>
37
+ <FilterBox>
38
+ <SearchInput searchText={t.filter} defaultValue={filters.search} onChange={search => applyFilters({ search })} />
39
+ <Select
40
+ value={filters.criticality}
41
+ options={criticalities}
42
+ onChange={criticality => applyFilters({ criticality })}
43
+ emptyOption={t.allCriticalities}
44
+ renderLabel={o => t[`criticality.${o}`]}
45
+ />
46
+ <Select
47
+ value={filters.context}
48
+ options={contexts}
49
+ onChange={context => applyFilters({ context })}
50
+ emptyOption={t.allContexts}
51
+ renderLabel={o => t[`context.${o}`]}
52
+ />
53
+ </FilterBox>
54
+ <AsyncContent
55
+ error={error}
56
+ loading={status === 'startup'}
57
+ errorDetails={{ errorComponent: () => <ErrorFeedback code={error.code} />, reportError: () => { } }}
58
+ >
59
+ <NotificationList
60
+ items={items}
61
+ loading={status === 'loading'}
62
+ onCommit={id => controller.markAsCommitted(id)}
63
+ infiniteScroll={{ hasMore, loadMore }}
64
+ onClickAction={controller.config.onClickAction}
65
+ showEmptySearch
66
+ />
67
+ </AsyncContent>
68
+ </>)
69
+ }
70
+
71
+ const dictionary = {
72
+ en: {
73
+ title: 'Notifications',
74
+ description: 'Here you can view all your received notifications.',
75
+ filter: 'Filter',
76
+ allCriticalities: 'All severities',
77
+ 'criticality.HIGH': 'High',
78
+ 'criticality.MEDIUM': 'Medium',
79
+ 'criticality.LOW': 'Low',
80
+ allContexts: 'All contexts',
81
+ 'context.ACCOUNT': 'Account',
82
+ 'context.STUDIO': 'Studio',
83
+ 'context.WORKSPACE': 'Workspace',
84
+ },
85
+ pt: {
86
+ title: 'Notificações',
87
+ description: 'Aqui você encontra todas as notificações recebidas.',
88
+ filter: 'Filtrar',
89
+ allCriticalities: 'Todas as criticidades',
90
+ 'criticality.HIGH': 'Alto',
91
+ 'criticality.MEDIUM': 'Médio',
92
+ 'criticality.LOW': 'Baixo',
93
+ allContexts: 'Todos os contextos',
94
+ 'context.ACCOUNT': 'Conta',
95
+ 'context.STUDIO': 'Estúdio',
96
+ 'context.WORKSPACE': 'Workspace',
97
+ },
98
+ } satisfies Dictionary
@@ -1,37 +1,37 @@
1
- import { createContext, useContext } from 'react'
2
-
3
- export type AnchorComponent = (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => React.ReactElement
4
-
5
- interface AnchorContext {
6
- /**
7
- * The component to render by a layout component a link is needed.
8
- * @default <a>
9
- */
10
- anchorTag?: AnchorComponent,
11
- }
12
-
13
- const context = createContext<AnchorContext>({})
14
-
15
- const Anchor: AnchorComponent = props => <a {...props} />
16
-
17
- /**
18
- * Some components need to create HTML anchors (links) to other pages in the website. These links, sometimes, must to be managed by a
19
- * navigator for React. Since we don't want to couple this library with any specific navigator, you can provide your own component for
20
- * creating links, it must follow the same interface of the HTML's `a` tag.
21
- *
22
- * If this is not used, the tag `<a>` is used by default.
23
- * @param props the anchor component (anchorTag) and the content to render (children).
24
- */
25
- export const AnchorProvider = ({ children, ...props }: Required<AnchorContext> & { children: React.ReactNode }) => (
26
- <context.Provider value={props}>{children}</context.Provider>
27
- )
28
-
29
- /**
30
- * A React hook for retrieving the Link (anchor) component.
31
- * @returns the link component declared at {@link AnchorProvider} or a component that renders the tag <a> from HTML if no link component was
32
- * provided.
33
- */
34
- export function useAnchorTag(): AnchorComponent {
35
- const { anchorTag } = useContext(context)
36
- return anchorTag ?? Anchor
37
- }
1
+ import { createContext, useContext } from 'react'
2
+
3
+ export type AnchorComponent = (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => React.ReactElement
4
+
5
+ interface AnchorContext {
6
+ /**
7
+ * The component to render by a layout component a link is needed.
8
+ * @default <a>
9
+ */
10
+ anchorTag?: AnchorComponent,
11
+ }
12
+
13
+ const context = createContext<AnchorContext>({})
14
+
15
+ const Anchor: AnchorComponent = props => <a {...props} />
16
+
17
+ /**
18
+ * Some components need to create HTML anchors (links) to other pages in the website. These links, sometimes, must to be managed by a
19
+ * navigator for React. Since we don't want to couple this library with any specific navigator, you can provide your own component for
20
+ * creating links, it must follow the same interface of the HTML's `a` tag.
21
+ *
22
+ * If this is not used, the tag `<a>` is used by default.
23
+ * @param props the anchor component (anchorTag) and the content to render (children).
24
+ */
25
+ export const AnchorProvider = ({ children, ...props }: Required<AnchorContext> & { children: React.ReactNode }) => (
26
+ <context.Provider value={props}>{children}</context.Provider>
27
+ )
28
+
29
+ /**
30
+ * A React hook for retrieving the Link (anchor) component.
31
+ * @returns the link component declared at {@link AnchorProvider} or a component that renders the tag <a> from HTML if no link component was
32
+ * provided.
33
+ */
34
+ export function useAnchorTag(): AnchorComponent {
35
+ const { anchorTag } = useContext(context)
36
+ return anchorTag ?? Anchor
37
+ }
@@ -1,36 +1,36 @@
1
- import { LoadingCircular } from '@citric/ui'
2
- import { createContext, ReactElement, useContext } from 'react'
3
-
4
- interface LoadingContext {
5
- /**
6
- * The component to render by a layout component.
7
- * @default <LoadingCircular>
8
- */
9
- loading?: ReactElement,
10
- }
11
-
12
- const context = createContext<LoadingContext>({})
13
-
14
- const Loading: ReactElement = <LoadingCircular size="sm" sx={{ position: 'absolute' }} />
15
-
16
- /**
17
- * Some portals use a different loading style than what we have in citric.
18
- * You can provide your own loading component to use wherever you want within the application.
19
- *
20
- * @param position The position parameter is mandatory to be passed as absolute in the loading component that is being passed.
21
- *
22
- * If this is not used, the component `LoadingCircular` from citric is used by default.
23
- * @param props the loading component (useLoadingComponent) and the content to render (children).
24
- */
25
- export const LoadingProvider = ({ children, ...props }: Required<LoadingContext> & { children: React.ReactNode }) => (
26
- <context.Provider value={props}>{children}</context.Provider>
27
- )
28
-
29
- /**
30
- * A React hook for retrieving the Loading component.
31
- * @returns the loading component declared at `LoadingProvider`.
32
- */
33
- export function useLoadingComponent(): ReactElement {
34
- const { loading } = useContext(context)
35
- return loading ?? Loading
36
- }
1
+ import { LoadingCircular } from '@citric/ui'
2
+ import { createContext, ReactElement, useContext } from 'react'
3
+
4
+ interface LoadingContext {
5
+ /**
6
+ * The component to render by a layout component.
7
+ * @default <LoadingCircular>
8
+ */
9
+ loading?: ReactElement,
10
+ }
11
+
12
+ const context = createContext<LoadingContext>({})
13
+
14
+ const Loading: ReactElement = <LoadingCircular size="sm" sx={{ position: 'absolute' }} />
15
+
16
+ /**
17
+ * Some portals use a different loading style than what we have in citric.
18
+ * You can provide your own loading component to use wherever you want within the application.
19
+ *
20
+ * @param position The position parameter is mandatory to be passed as absolute in the loading component that is being passed.
21
+ *
22
+ * If this is not used, the component `LoadingCircular` from citric is used by default.
23
+ * @param props the loading component (useLoadingComponent) and the content to render (children).
24
+ */
25
+ export const LoadingProvider = ({ children, ...props }: Required<LoadingContext> & { children: React.ReactNode }) => (
26
+ <context.Provider value={props}>{children}</context.Provider>
27
+ )
28
+
29
+ /**
30
+ * A React hook for retrieving the Loading component.
31
+ * @returns the loading component declared at `LoadingProvider`.
32
+ */
33
+ export function useLoadingComponent(): ReactElement {
34
+ const { loading } = useContext(context)
35
+ return loading ?? Loading
36
+ }
@@ -1,103 +1,103 @@
1
- import { pull, uniqBy } from 'lodash'
2
- import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
3
- import { LazyNotificationListener, LoadNotificationsFilters, LoadNotificationsOptions } from './types'
4
-
5
- interface ConstructorParams {
6
- id: number,
7
- load: (options: LoadNotificationsOptions) => Promise<ResponseModelGetTenantNotificationsResponse>,
8
- filters?: LoadNotificationsFilters,
9
- }
10
-
11
- export class LazyNotificationList {
12
- readonly id: number
13
- items: GetTenantNotificationsResponse[] = []
14
- private page = -1
15
- private total = 0
16
- private filters: LoadNotificationsFilters
17
- private readonly load: (options: LoadNotificationsOptions) => Promise<ResponseModelGetTenantNotificationsResponse>
18
- private listeners: LazyNotificationListener[] = []
19
- private currentFetch: Promise<void> | undefined
20
-
21
- constructor({ id, load, filters = {} }: ConstructorParams) {
22
- this.id = id
23
- this.load = load
24
- this.filters = filters
25
- }
26
-
27
- private notify() {
28
- const hasMore = this.hasMore()
29
- this.listeners.forEach(l => l(this.items, hasMore))
30
- }
31
-
32
- async #loadMore(reset = false): Promise<void> {
33
- try {
34
- const result = await this.load({ ...this.filters, page: this.page + 1 })
35
- if (reset) {
36
- this.items = []
37
- this.total = 0
38
- }
39
- // we can't have items with the same id: this can happen if new notifications have been created after the first page was loaded.
40
- this.items = uniqBy([...this.items, ...result.items], 'id')
41
- // we can't keep loading more if we already loaded every item or if the API returned an empty list.
42
- this.total = result.items.length ? (this.total || result.total) : this.items.length
43
- this.page++
44
- this.notify()
45
- } finally {
46
- this.currentFetch = undefined
47
- }
48
- }
49
-
50
- async applyFilters(filters: LoadNotificationsFilters) {
51
- try {
52
- await this.currentFetch
53
- } catch {
54
- /* empty: this error will be treated by the function who actually triggered `currentFetch`. We just need to make sure we wait until
55
- it ends before making new requests. */
56
- }
57
- const prevPage = this.page
58
- const prevFilters = this.filters
59
- this.filters = filters
60
- this.page = -1
61
- try {
62
-
63
- this.currentFetch = this.#loadMore(true)
64
- await this.currentFetch
65
- } catch (error) {
66
- this.filters = prevFilters
67
- this.page = prevPage
68
- throw error
69
- }
70
- }
71
-
72
- loadMore() {
73
- this.currentFetch ??= this.#loadMore()
74
- return this.currentFetch
75
- }
76
-
77
- hasMore() {
78
- return this.items.length < this.total
79
- }
80
-
81
- subscribe(listener: LazyNotificationListener) {
82
- this.listeners.push(listener)
83
- return () => {
84
- pull(this.listeners, listener)
85
- }
86
- }
87
-
88
- update(readStatusMap: Map<string, boolean>) {
89
- this.items.forEach(i => i.committed = readStatusMap.get(i.id) ?? i.committed)
90
- if (this.filters.committed !== undefined) {
91
- this.items = this.items.filter(i => i.committed === this.filters.committed)
92
- // if the filtered list has one item or less, we update the list with the backend so it can show more items.
93
- // we can't use loadMore in this case because the page 2 would now skip some items. We'd need the backend to implement a cursor
94
- // interface.
95
- if (this.items.length <= 1) this.applyFilters(this.filters)
96
- }
97
- this.notify()
98
- }
99
-
100
- mute() {
101
- this.listeners = []
102
- }
103
- }
1
+ import { pull, uniqBy } from 'lodash'
2
+ import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
3
+ import { LazyNotificationListener, LoadNotificationsFilters, LoadNotificationsOptions } from './types'
4
+
5
+ interface ConstructorParams {
6
+ id: number,
7
+ load: (options: LoadNotificationsOptions) => Promise<ResponseModelGetTenantNotificationsResponse>,
8
+ filters?: LoadNotificationsFilters,
9
+ }
10
+
11
+ export class LazyNotificationList {
12
+ readonly id: number
13
+ items: GetTenantNotificationsResponse[] = []
14
+ private page = -1
15
+ private total = 0
16
+ private filters: LoadNotificationsFilters
17
+ private readonly load: (options: LoadNotificationsOptions) => Promise<ResponseModelGetTenantNotificationsResponse>
18
+ private listeners: LazyNotificationListener[] = []
19
+ private currentFetch: Promise<void> | undefined
20
+
21
+ constructor({ id, load, filters = {} }: ConstructorParams) {
22
+ this.id = id
23
+ this.load = load
24
+ this.filters = filters
25
+ }
26
+
27
+ private notify() {
28
+ const hasMore = this.hasMore()
29
+ this.listeners.forEach(l => l(this.items, hasMore))
30
+ }
31
+
32
+ async #loadMore(reset = false): Promise<void> {
33
+ try {
34
+ const result = await this.load({ ...this.filters, page: this.page + 1 })
35
+ if (reset) {
36
+ this.items = []
37
+ this.total = 0
38
+ }
39
+ // we can't have items with the same id: this can happen if new notifications have been created after the first page was loaded.
40
+ this.items = uniqBy([...this.items, ...result.items], 'id')
41
+ // we can't keep loading more if we already loaded every item or if the API returned an empty list.
42
+ this.total = result.items.length ? (this.total || result.total) : this.items.length
43
+ this.page++
44
+ this.notify()
45
+ } finally {
46
+ this.currentFetch = undefined
47
+ }
48
+ }
49
+
50
+ async applyFilters(filters: LoadNotificationsFilters) {
51
+ try {
52
+ await this.currentFetch
53
+ } catch {
54
+ /* empty: this error will be treated by the function who actually triggered `currentFetch`. We just need to make sure we wait until
55
+ it ends before making new requests. */
56
+ }
57
+ const prevPage = this.page
58
+ const prevFilters = this.filters
59
+ this.filters = filters
60
+ this.page = -1
61
+ try {
62
+
63
+ this.currentFetch = this.#loadMore(true)
64
+ await this.currentFetch
65
+ } catch (error) {
66
+ this.filters = prevFilters
67
+ this.page = prevPage
68
+ throw error
69
+ }
70
+ }
71
+
72
+ loadMore() {
73
+ this.currentFetch ??= this.#loadMore()
74
+ return this.currentFetch
75
+ }
76
+
77
+ hasMore() {
78
+ return this.items.length < this.total
79
+ }
80
+
81
+ subscribe(listener: LazyNotificationListener) {
82
+ this.listeners.push(listener)
83
+ return () => {
84
+ pull(this.listeners, listener)
85
+ }
86
+ }
87
+
88
+ update(readStatusMap: Map<string, boolean>) {
89
+ this.items.forEach(i => i.committed = readStatusMap.get(i.id) ?? i.committed)
90
+ if (this.filters.committed !== undefined) {
91
+ this.items = this.items.filter(i => i.committed === this.filters.committed)
92
+ // if the filtered list has one item or less, we update the list with the backend so it can show more items.
93
+ // we can't use loadMore in this case because the page 2 would now skip some items. We'd need the backend to implement a cursor
94
+ // interface.
95
+ if (this.items.length <= 1) this.applyFilters(this.filters)
96
+ }
97
+ this.notify()
98
+ }
99
+
100
+ mute() {
101
+ this.listeners = []
102
+ }
103
+ }