@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,104 +1,104 @@
1
- import { pull } from 'lodash'
2
- import { LazyNotificationList } from './LazyNotificationList'
3
- import { LoadNotificationsFilters, NotificationConfig, UnreadNotificationListener } from './types'
4
-
5
- const DEFAULT_POLLING_MS = 120000
6
-
7
- export class NotificationController {
8
- private lazyLists = new Map<number, LazyNotificationList>()
9
- readonly config: NotificationConfig
10
- private readStatusMap = new Map<string, boolean>()
11
- private nextId = 1
12
- private unreadNotification = false
13
- private lastUnreadVerification: Date | undefined
14
- private pollingTimeoutId: number | undefined
15
- private unreadNotificationListeners: UnreadNotificationListener[] = []
16
-
17
- constructor(config: NotificationConfig) {
18
- this.config = config
19
- config.pollingMS ??= DEFAULT_POLLING_MS
20
- config.notificationsPath ??= '/notifications'
21
- }
22
-
23
- private hasUnreadNotificationInMemory() {
24
- for (const [, read] of this.readStatusMap) {
25
- if (!read.valueOf()) return true
26
- }
27
- return false
28
- }
29
-
30
- private setUnreadNotification(value: boolean) {
31
- this.unreadNotification = value
32
- if (!value) {
33
- this.pollingTimeoutId = window.setTimeout(() => this.checkUnread(), this.config.pollingMS)
34
- }
35
- this.unreadNotificationListeners.forEach(l => l(value))
36
- }
37
-
38
- createLazyNotificationList(filters?: LoadNotificationsFilters): LazyNotificationList {
39
- const list = new LazyNotificationList({
40
- id: this.nextId++,
41
- filters,
42
- load: async (options) => {
43
- const result = await this.config.load(options)
44
- result.items.forEach((i) => {
45
- this.readStatusMap.set(i.id, i.committed)
46
- // updating the unread indicator, since it might be stale
47
- if (!i.committed) this.setUnreadNotification(true)
48
- })
49
- this.lazyLists.forEach(l => l !== list && list.update(this.readStatusMap))
50
- return result
51
- },
52
- })
53
- this.lazyLists.set(list.id, list)
54
- return list
55
- }
56
-
57
- destroyLazyNotificationList(id: number) {
58
- this.lazyLists.get(id)?.mute()
59
- this.lazyLists.delete(id)
60
- }
61
-
62
- async checkUnread() {
63
- window.clearInterval(this.pollingTimeoutId)
64
- this.setUnreadNotification(await this.config.checkForUnreadNotificationsSince(this.lastUnreadVerification))
65
- this.lastUnreadVerification = new Date()
66
- }
67
-
68
- async markAsCommitted(notificationId: string) {
69
- if (this.readStatusMap.get(notificationId)) return
70
- this.readStatusMap.set(notificationId, true)
71
- this.lazyLists.forEach(l => l.update(this.readStatusMap))
72
- try {
73
- await this.config.markAsCommitted(notificationId)
74
- // update the notification indicator: this is an inconsistency, we should actually ask the API, but since it's an expensive
75
- // operation in the backend, we won't, instead, we'll only check the notifications we have loaded in memory.
76
- if (!this.hasUnreadNotificationInMemory()) this.setUnreadNotification(false)
77
- } catch {
78
- this.readStatusMap.set(notificationId, false)
79
- this.lazyLists.forEach(l => l.update(this.readStatusMap))
80
- }
81
- }
82
-
83
- onUnreadNotificationChange(listener: UnreadNotificationListener) {
84
- this.unreadNotificationListeners.push(listener)
85
- return () => {
86
- pull(this.unreadNotificationListeners, listener)
87
- }
88
- }
89
-
90
- hasUnreadNotification() {
91
- return this.unreadNotification
92
- }
93
-
94
- reset() {
95
- this.lazyLists.clear()
96
- this.readStatusMap.clear()
97
- this.lastUnreadVerification = undefined
98
- this.unreadNotification = false
99
- }
100
-
101
- mute() {
102
- this.unreadNotificationListeners = []
103
- }
104
- }
1
+ import { pull } from 'lodash'
2
+ import { LazyNotificationList } from './LazyNotificationList'
3
+ import { LoadNotificationsFilters, NotificationConfig, UnreadNotificationListener } from './types'
4
+
5
+ const DEFAULT_POLLING_MS = 120000
6
+
7
+ export class NotificationController {
8
+ private lazyLists = new Map<number, LazyNotificationList>()
9
+ readonly config: NotificationConfig
10
+ private readStatusMap = new Map<string, boolean>()
11
+ private nextId = 1
12
+ private unreadNotification = false
13
+ private lastUnreadVerification: Date | undefined
14
+ private pollingTimeoutId: number | undefined
15
+ private unreadNotificationListeners: UnreadNotificationListener[] = []
16
+
17
+ constructor(config: NotificationConfig) {
18
+ this.config = config
19
+ config.pollingMS ??= DEFAULT_POLLING_MS
20
+ config.notificationsPath ??= '/notifications'
21
+ }
22
+
23
+ private hasUnreadNotificationInMemory() {
24
+ for (const [, read] of this.readStatusMap) {
25
+ if (!read.valueOf()) return true
26
+ }
27
+ return false
28
+ }
29
+
30
+ private setUnreadNotification(value: boolean) {
31
+ this.unreadNotification = value
32
+ if (!value) {
33
+ this.pollingTimeoutId = window.setTimeout(() => this.checkUnread(), this.config.pollingMS)
34
+ }
35
+ this.unreadNotificationListeners.forEach(l => l(value))
36
+ }
37
+
38
+ createLazyNotificationList(filters?: LoadNotificationsFilters): LazyNotificationList {
39
+ const list = new LazyNotificationList({
40
+ id: this.nextId++,
41
+ filters,
42
+ load: async (options) => {
43
+ const result = await this.config.load(options)
44
+ result.items.forEach((i) => {
45
+ this.readStatusMap.set(i.id, i.committed)
46
+ // updating the unread indicator, since it might be stale
47
+ if (!i.committed) this.setUnreadNotification(true)
48
+ })
49
+ this.lazyLists.forEach(l => l !== list && list.update(this.readStatusMap))
50
+ return result
51
+ },
52
+ })
53
+ this.lazyLists.set(list.id, list)
54
+ return list
55
+ }
56
+
57
+ destroyLazyNotificationList(id: number) {
58
+ this.lazyLists.get(id)?.mute()
59
+ this.lazyLists.delete(id)
60
+ }
61
+
62
+ async checkUnread() {
63
+ window.clearInterval(this.pollingTimeoutId)
64
+ this.setUnreadNotification(await this.config.checkForUnreadNotificationsSince(this.lastUnreadVerification))
65
+ this.lastUnreadVerification = new Date()
66
+ }
67
+
68
+ async markAsCommitted(notificationId: string) {
69
+ if (this.readStatusMap.get(notificationId)) return
70
+ this.readStatusMap.set(notificationId, true)
71
+ this.lazyLists.forEach(l => l.update(this.readStatusMap))
72
+ try {
73
+ await this.config.markAsCommitted(notificationId)
74
+ // update the notification indicator: this is an inconsistency, we should actually ask the API, but since it's an expensive
75
+ // operation in the backend, we won't, instead, we'll only check the notifications we have loaded in memory.
76
+ if (!this.hasUnreadNotificationInMemory()) this.setUnreadNotification(false)
77
+ } catch {
78
+ this.readStatusMap.set(notificationId, false)
79
+ this.lazyLists.forEach(l => l.update(this.readStatusMap))
80
+ }
81
+ }
82
+
83
+ onUnreadNotificationChange(listener: UnreadNotificationListener) {
84
+ this.unreadNotificationListeners.push(listener)
85
+ return () => {
86
+ pull(this.unreadNotificationListeners, listener)
87
+ }
88
+ }
89
+
90
+ hasUnreadNotification() {
91
+ return this.unreadNotification
92
+ }
93
+
94
+ reset() {
95
+ this.lazyLists.clear()
96
+ this.readStatusMap.clear()
97
+ this.lastUnreadVerification = undefined
98
+ this.unreadNotification = false
99
+ }
100
+
101
+ mute() {
102
+ this.unreadNotificationListeners = []
103
+ }
104
+ }
@@ -1,23 +1,23 @@
1
- import { createContext, useContext } from 'react'
2
- import { NotificationController } from './NotificationController'
3
-
4
- export interface Props {
5
- children: React.ReactNode,
6
- controller: NotificationController,
7
- }
8
-
9
- const context = createContext<NotificationController | undefined>(undefined)
10
-
11
- export const NotificationProvider = ({ children, controller }: Props) => (
12
- <context.Provider value={controller}>{children}</context.Provider>
13
- )
14
-
15
- export function useNotificationController() {
16
- const controller = useContext(context)
17
- if (!controller) {
18
- throw new Error(
19
- 'A NotificationController was requested, but no NotificationController is available in the React Context. Please, be sure to wrap your component in a NotificationProvider.',
20
- )
21
- }
22
- return controller
23
- }
1
+ import { createContext, useContext } from 'react'
2
+ import { NotificationController } from './NotificationController'
3
+
4
+ export interface Props {
5
+ children: React.ReactNode,
6
+ controller: NotificationController,
7
+ }
8
+
9
+ const context = createContext<NotificationController | undefined>(undefined)
10
+
11
+ export const NotificationProvider = ({ children, controller }: Props) => (
12
+ <context.Provider value={controller}>{children}</context.Provider>
13
+ )
14
+
15
+ export function useNotificationController() {
16
+ const controller = useContext(context)
17
+ if (!controller) {
18
+ throw new Error(
19
+ 'A NotificationController was requested, but no NotificationController is available in the React Context. Please, be sure to wrap your component in a NotificationProvider.',
20
+ )
21
+ }
22
+ return controller
23
+ }
@@ -1,98 +1,98 @@
1
- import { useLanguage } from '@stack-spot/portal-translate'
2
- import { useCallback, useRef, useState } from 'react'
3
- import { useManualRender } from '../../hooks/manual-render'
4
- import { useEffectOnce } from '../../hooks/use-effect-once'
5
- import { GetTenantNotificationsResponse } from '../../notifications'
6
- import { useNotificationController } from './context'
7
- import { LazyNotificationList } from './LazyNotificationList'
8
- import { LoadNotificationsFilters } from './types'
9
-
10
- export function useNotificationList(initialFilters: LoadNotificationsFilters = {}) {
11
- const { repaint } = useManualRender()
12
- const [status, setStatus] = useState<'startup' | 'idle' | 'error' | 'loading'>('startup')
13
- const error = useRef<any>()
14
- const controller = useNotificationController()
15
- const [filters, setFilters] = useState(initialFilters)
16
- const list = useRef<LazyNotificationList | undefined>()
17
-
18
- useEffectOnce(() => {
19
- async function start() {
20
- list.current = controller.createLazyNotificationList(initialFilters)
21
- list.current.subscribe(repaint)
22
- try {
23
- await list.current?.loadMore()
24
- setStatus('idle')
25
- } catch (e) {
26
- setStatus('error')
27
- error.current = e
28
- }
29
- }
30
- start()
31
- return () => {
32
- if (list.current) controller.destroyLazyNotificationList(list.current.id)
33
- }
34
- })
35
-
36
- const applyFilters = useCallback(async (newFilters: LoadNotificationsFilters) => {
37
- if (!list.current) return
38
- setStatus('loading')
39
- // since we're inside a useCallback, we don't have direct access to the current value of "filters". The only way we can access this
40
- // value is from the function "setFilters". But "setFilters" run async. For this reason, we wait the function to run and retrieve the
41
- // current value before continuing, hence the use of the await and promise below.
42
- const { next, prev } = await new Promise<{ next: LoadNotificationsFilters, prev: LoadNotificationsFilters }>((resolve) => {
43
- setFilters((filters) => {
44
- const prev = filters
45
- const next = { ...filters, ...newFilters }
46
- resolve({ prev, next })
47
- return next
48
- })
49
- })
50
- try {
51
- await list.current.applyFilters(next)
52
- setStatus('idle')
53
- error.current = undefined
54
- } catch (e) {
55
- setFilters(prev)
56
- if (error.current) {
57
- error.current = e
58
- setStatus('error')
59
- } else {
60
- setStatus('idle')
61
- }
62
- }
63
- }, [])
64
-
65
- return {
66
- status,
67
- items: list.current?.items ?? [],
68
- hasMore: list.current?.hasMore() ?? false,
69
- loadMore: () => list.current?.loadMore(),
70
- refresh: () => applyFilters(filters),
71
- applyFilters,
72
- filters,
73
- error: error.current,
74
- }
75
- }
76
-
77
- export function useGetNotificationTitleAndDescription({ content }: GetTenantNotificationsResponse) {
78
- const language = useLanguage()
79
- const { title, description } = content?.[language] || content?.en || {}
80
-
81
- return {
82
- title,
83
- description,
84
- }
85
- }
86
-
87
- export function useUnreadNotifications() {
88
- const [hasUnreadNotifications, setUnreadNotifications] = useState(false)
89
- const controller = useNotificationController()
90
-
91
- useEffectOnce(() => {
92
- const unsubscribe = controller.onUnreadNotificationChange(setUnreadNotifications)
93
- controller.checkUnread()
94
- return unsubscribe
95
- })
96
-
97
- return hasUnreadNotifications
98
- }
1
+ import { useLanguage } from '@stack-spot/portal-translate'
2
+ import { useCallback, useRef, useState } from 'react'
3
+ import { useManualRender } from '../../hooks/manual-render'
4
+ import { useEffectOnce } from '../../hooks/use-effect-once'
5
+ import { GetTenantNotificationsResponse } from '../../notifications'
6
+ import { useNotificationController } from './context'
7
+ import { LazyNotificationList } from './LazyNotificationList'
8
+ import { LoadNotificationsFilters } from './types'
9
+
10
+ export function useNotificationList(initialFilters: LoadNotificationsFilters = {}) {
11
+ const { repaint } = useManualRender()
12
+ const [status, setStatus] = useState<'startup' | 'idle' | 'error' | 'loading'>('startup')
13
+ const error = useRef<any>()
14
+ const controller = useNotificationController()
15
+ const [filters, setFilters] = useState(initialFilters)
16
+ const list = useRef<LazyNotificationList | undefined>()
17
+
18
+ useEffectOnce(() => {
19
+ async function start() {
20
+ list.current = controller.createLazyNotificationList(initialFilters)
21
+ list.current.subscribe(repaint)
22
+ try {
23
+ await list.current?.loadMore()
24
+ setStatus('idle')
25
+ } catch (e) {
26
+ setStatus('error')
27
+ error.current = e
28
+ }
29
+ }
30
+ start()
31
+ return () => {
32
+ if (list.current) controller.destroyLazyNotificationList(list.current.id)
33
+ }
34
+ })
35
+
36
+ const applyFilters = useCallback(async (newFilters: LoadNotificationsFilters) => {
37
+ if (!list.current) return
38
+ setStatus('loading')
39
+ // since we're inside a useCallback, we don't have direct access to the current value of "filters". The only way we can access this
40
+ // value is from the function "setFilters". But "setFilters" run async. For this reason, we wait the function to run and retrieve the
41
+ // current value before continuing, hence the use of the await and promise below.
42
+ const { next, prev } = await new Promise<{ next: LoadNotificationsFilters, prev: LoadNotificationsFilters }>((resolve) => {
43
+ setFilters((filters) => {
44
+ const prev = filters
45
+ const next = { ...filters, ...newFilters }
46
+ resolve({ prev, next })
47
+ return next
48
+ })
49
+ })
50
+ try {
51
+ await list.current.applyFilters(next)
52
+ setStatus('idle')
53
+ error.current = undefined
54
+ } catch (e) {
55
+ setFilters(prev)
56
+ if (error.current) {
57
+ error.current = e
58
+ setStatus('error')
59
+ } else {
60
+ setStatus('idle')
61
+ }
62
+ }
63
+ }, [])
64
+
65
+ return {
66
+ status,
67
+ items: list.current?.items ?? [],
68
+ hasMore: list.current?.hasMore() ?? false,
69
+ loadMore: () => list.current?.loadMore(),
70
+ refresh: () => applyFilters(filters),
71
+ applyFilters,
72
+ filters,
73
+ error: error.current,
74
+ }
75
+ }
76
+
77
+ export function useGetNotificationTitleAndDescription({ content }: GetTenantNotificationsResponse) {
78
+ const language = useLanguage()
79
+ const { title, description } = content?.[language] || content?.en || {}
80
+
81
+ return {
82
+ title,
83
+ description,
84
+ }
85
+ }
86
+
87
+ export function useUnreadNotifications() {
88
+ const [hasUnreadNotifications, setUnreadNotifications] = useState(false)
89
+ const controller = useNotificationController()
90
+
91
+ useEffectOnce(() => {
92
+ const unsubscribe = controller.onUnreadNotificationChange(setUnreadNotifications)
93
+ controller.checkUnread()
94
+ return unsubscribe
95
+ })
96
+
97
+ return hasUnreadNotifications
98
+ }
@@ -1,65 +1,65 @@
1
- import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
2
-
3
- export type NotificationPriority = 'HIGH' | 'MEDIUM' | 'LOW'
4
-
5
- export type NotificationContext = 'ACCOUNT' | 'STUDIO' | 'WORKSPACE'
6
-
7
- export interface LoadNotificationsFilters {
8
- committed?: boolean,
9
- criticality?: NotificationPriority,
10
- search?: string,
11
- context?: NotificationContext,
12
- size?: number,
13
- isBanner?: boolean,
14
- }
15
-
16
- export interface LoadNotificationsOptions extends LoadNotificationsFilters {
17
- page: number,
18
- isBanner?: boolean,
19
- }
20
-
21
- export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
22
-
23
- export interface NotificationConfig {
24
- /**
25
- * Fetches the notifications according to the options passed as parameters.
26
- * @param options page, size and filters.
27
- * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
28
- */
29
- load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
30
- /**
31
- * Check for unread notifications since the date passed as parameter.
32
- * @param date the date to start the search from
33
- * @returns a promise that resolves to true if there are unread notifications or false otherwise.
34
- */
35
- checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
36
- /**
37
- * Marks the notification with the id passed as parameter as read (committed).
38
- * @param id the id of the notification.
39
- */
40
- markAsCommitted(id: string): Promise<void>,
41
- /**
42
- * How long (ms) we should wait before checking for unread notifications again.
43
- * @default 120000
44
- */
45
- pollingMS?: number,
46
- /**
47
- * Path to the notifications page.
48
- * @default '/notifications'
49
- */
50
- notificationsPath?: string,
51
- /**
52
- * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
53
- *
54
- * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
55
- *
56
- * @param id the id of the notification.
57
- */
58
- onClickAction?: (id: string) => void,
59
- /**
60
- * Function that is called whenever the "read more" button is clicked.
61
- */
62
- onClickViewNotification?: () => void,
63
- }
64
-
65
- export type UnreadNotificationListener = (hasUnreadNotification: boolean) => void
1
+ import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
2
+
3
+ export type NotificationPriority = 'HIGH' | 'MEDIUM' | 'LOW'
4
+
5
+ export type NotificationContext = 'ACCOUNT' | 'STUDIO' | 'WORKSPACE'
6
+
7
+ export interface LoadNotificationsFilters {
8
+ committed?: boolean,
9
+ criticality?: NotificationPriority,
10
+ search?: string,
11
+ context?: NotificationContext,
12
+ size?: number,
13
+ isBanner?: boolean,
14
+ }
15
+
16
+ export interface LoadNotificationsOptions extends LoadNotificationsFilters {
17
+ page: number,
18
+ isBanner?: boolean,
19
+ }
20
+
21
+ export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
22
+
23
+ export interface NotificationConfig {
24
+ /**
25
+ * Fetches the notifications according to the options passed as parameters.
26
+ * @param options page, size and filters.
27
+ * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
28
+ */
29
+ load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
30
+ /**
31
+ * Check for unread notifications since the date passed as parameter.
32
+ * @param date the date to start the search from
33
+ * @returns a promise that resolves to true if there are unread notifications or false otherwise.
34
+ */
35
+ checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
36
+ /**
37
+ * Marks the notification with the id passed as parameter as read (committed).
38
+ * @param id the id of the notification.
39
+ */
40
+ markAsCommitted(id: string): Promise<void>,
41
+ /**
42
+ * How long (ms) we should wait before checking for unread notifications again.
43
+ * @default 120000
44
+ */
45
+ pollingMS?: number,
46
+ /**
47
+ * Path to the notifications page.
48
+ * @default '/notifications'
49
+ */
50
+ notificationsPath?: string,
51
+ /**
52
+ * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
53
+ *
54
+ * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
55
+ *
56
+ * @param id the id of the notification.
57
+ */
58
+ onClickAction?: (id: string) => void,
59
+ /**
60
+ * Function that is called whenever the "read more" button is clicked.
61
+ */
62
+ onClickViewNotification?: () => void,
63
+ }
64
+
65
+ export type UnreadNotificationListener = (hasUnreadNotification: boolean) => void
package/src/hooks/date.ts CHANGED
@@ -1,31 +1,31 @@
1
- import { Language, useLanguage } from '@stack-spot/portal-translate'
2
- import { format } from 'date-fns'
3
- import { ptBR } from 'date-fns/locale'
4
-
5
- function formatDate(date: string, locale: Language, includeTime: boolean) {
6
- const time = includeTime ? ' HH:mm' : ''
7
- return locale === 'pt'
8
- ? format(new Date(date), `dd/MM/yyyy${time}`, { locale: ptBR })
9
- : format(new Date(date), `MMMM dd yyyy${time}`)
10
- }
11
-
12
- function formatSimpleDate(date: string, locale: Language) {
13
-
14
- return locale === 'pt'
15
- ? format(new Date(date), 'dd/MM/yyyy', { locale: ptBR })
16
- : format(new Date(date), 'MM/dd/yyyy')
17
- }
18
-
19
- /**
20
- * A utility for formatting dates.
21
- * @param date
22
- * @returns a date formatter object.
23
- */
24
- export function useDateFormatter() {
25
- const locale = useLanguage()
26
- return {
27
- formatDate: (date?: string) => date ? formatDate(date, locale, false) : undefined,
28
- formatDateWithTime: (date?: string) => date ? formatDate(date, locale, true) : undefined,
29
- formatSimpleDate: (date?: string) => date ? formatSimpleDate(date, locale) : undefined,
30
- }
31
- }
1
+ import { Language, useLanguage } from '@stack-spot/portal-translate'
2
+ import { format } from 'date-fns'
3
+ import { ptBR } from 'date-fns/locale'
4
+
5
+ function formatDate(date: string, locale: Language, includeTime: boolean) {
6
+ const time = includeTime ? ' HH:mm' : ''
7
+ return locale === 'pt'
8
+ ? format(new Date(date), `dd/MM/yyyy${time}`, { locale: ptBR })
9
+ : format(new Date(date), `MMMM dd yyyy${time}`)
10
+ }
11
+
12
+ function formatSimpleDate(date: string, locale: Language) {
13
+
14
+ return locale === 'pt'
15
+ ? format(new Date(date), 'dd/MM/yyyy', { locale: ptBR })
16
+ : format(new Date(date), 'MM/dd/yyyy')
17
+ }
18
+
19
+ /**
20
+ * A utility for formatting dates.
21
+ * @param date
22
+ * @returns a date formatter object.
23
+ */
24
+ export function useDateFormatter() {
25
+ const locale = useLanguage()
26
+ return {
27
+ formatDate: (date?: string) => date ? formatDate(date, locale, false) : undefined,
28
+ formatDateWithTime: (date?: string) => date ? formatDate(date, locale, true) : undefined,
29
+ formatSimpleDate: (date?: string) => date ? formatSimpleDate(date, locale) : undefined,
30
+ }
31
+ }