@stack-spot/portal-components 2.27.1 → 2.27.3

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 (247) hide show
  1. package/CHANGELOG.md +642 -628
  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 +7 -3
  40. package/dist/components/Placeholder.d.ts.map +1 -1
  41. package/dist/components/Placeholder.js +3 -3
  42. package/dist/components/Placeholder.js.map +1 -1
  43. package/dist/components/ScrollView.js +16 -16
  44. package/dist/components/Select/BadgeItem.d.ts +1 -1
  45. package/dist/components/Select/BadgeItem.js +1 -1
  46. package/dist/components/Select/ClearInput.d.ts +1 -1
  47. package/dist/components/Select/ClearInput.js +1 -1
  48. package/dist/components/Select/CloseItem.d.ts +1 -1
  49. package/dist/components/Select/CloseItem.js +1 -1
  50. package/dist/components/Select/CreatableSelect.js +1 -1
  51. package/dist/components/Select/CustomMenu.d.ts +1 -1
  52. package/dist/components/Select/CustomMenu.js +1 -1
  53. package/dist/components/Select/LabelItem.d.ts +1 -1
  54. package/dist/components/Select/LabelItem.js +1 -1
  55. package/dist/components/Select/MultiValue.d.ts +1 -1
  56. package/dist/components/Select/MultiValue.js +1 -1
  57. package/dist/components/Select/SelectInfiniteScroll.d.ts +1 -1
  58. package/dist/components/Select/SelectInfiniteScroll.js +1 -1
  59. package/dist/components/Select/SelectSearch.d.ts +1 -1
  60. package/dist/components/Select/SelectSearch.js +1 -1
  61. package/dist/components/SelectionList.d.ts +1 -1
  62. package/dist/components/SelectionList.js +62 -62
  63. package/dist/components/SelectionList.js.map +1 -1
  64. package/dist/components/StatusCircle.d.ts +1 -1
  65. package/dist/components/StatusCircle.js +6 -6
  66. package/dist/components/Stepper/Navigation.js +4 -4
  67. package/dist/components/Stepper/Step.js +3 -3
  68. package/dist/components/Stepper/Stepper.js +6 -6
  69. package/dist/components/Stepper/headers.js +22 -22
  70. package/dist/components/Table/HeaderItem.js +1 -1
  71. package/dist/components/Table/SettingsVerticalMenu.d.ts +1 -1
  72. package/dist/components/Table/SettingsVerticalMenu.js +1 -1
  73. package/dist/components/Table/StyledLinkTable.d.ts +1 -1
  74. package/dist/components/Table/StyledLinkTable.js +5 -5
  75. package/dist/components/Table/TableData.d.ts +1 -1
  76. package/dist/components/Table/TableData.js +25 -25
  77. package/dist/components/TimelineSection.d.ts +1 -1
  78. package/dist/components/TimelineSection.js +14 -14
  79. package/dist/components/error/ErrorFeedback.d.ts +1 -1
  80. package/dist/components/error/ErrorFeedback.js +35 -35
  81. package/dist/components/error/NotFound.d.ts +1 -1
  82. package/dist/components/error/NotFound.js +1 -1
  83. package/dist/components/error/UnderMaintenance.d.ts +1 -1
  84. package/dist/components/error/UnderMaintenance.js +1 -1
  85. package/dist/components/form/Form/Form.d.ts +1 -1
  86. package/dist/components/form/Form/Form.js +1 -1
  87. package/dist/components/form/Form/FormGroup.d.ts +2 -2
  88. package/dist/components/form/Form/FormGroup.js +1 -1
  89. package/dist/components/form/SearchInput.d.ts +1 -1
  90. package/dist/components/form/SearchInput.js +1 -1
  91. package/dist/components/form/Select/CustomSelect.d.ts +1 -1
  92. package/dist/components/form/Select/CustomSelect.js +1 -1
  93. package/dist/components/form/Select/DetailedSelect.d.ts +1 -1
  94. package/dist/components/form/Select/DetailedSelect.js +1 -1
  95. package/dist/components/form/Select/Select.d.ts +1 -1
  96. package/dist/components/form/Select/Select.js +1 -1
  97. package/dist/components/form/Select/styled.js +161 -161
  98. package/dist/components/form/Select/utils.js +1 -1
  99. package/dist/components/notification/NotificationComponent.d.ts +1 -1
  100. package/dist/components/notification/NotificationComponent.js +54 -54
  101. package/dist/components/notification/NotificationItem.d.ts +1 -1
  102. package/dist/components/notification/NotificationItem.d.ts.map +1 -1
  103. package/dist/components/notification/NotificationItem.js +8 -2
  104. package/dist/components/notification/NotificationItem.js.map +1 -1
  105. package/dist/components/notification/NotificationList.d.ts +1 -1
  106. package/dist/components/notification/NotificationList.d.ts.map +1 -1
  107. package/dist/components/notification/NotificationList.js +44 -44
  108. package/dist/components/notification/NotificationList.js.map +1 -1
  109. package/dist/components/notification/NotificationPlaceholder.d.ts +1 -1
  110. package/dist/components/notification/NotificationPlaceholder.d.ts.map +1 -1
  111. package/dist/components/notification/NotificationPlaceholder.js +2 -2
  112. package/dist/components/notification/NotificationPlaceholder.js.map +1 -1
  113. package/dist/containers/NotificationsPage.d.ts +1 -1
  114. package/dist/containers/NotificationsPage.js +10 -10
  115. package/dist/context/anchor.d.ts +1 -1
  116. package/dist/context/anchor.js +1 -1
  117. package/dist/context/loading.d.ts +1 -1
  118. package/dist/context/loading.js +1 -1
  119. package/dist/context/notification/context.d.ts +1 -1
  120. package/dist/context/notification/context.js +1 -1
  121. package/dist/hooks/date.js +1 -1
  122. package/dist/hooks/service-now.js +28 -28
  123. package/dist/svg/AI.d.ts +1 -1
  124. package/dist/svg/AI.js +1 -1
  125. package/dist/svg/CS.d.ts +1 -1
  126. package/dist/svg/CS.js +1 -1
  127. package/dist/svg/EDP.d.ts +1 -1
  128. package/dist/svg/EDP.js +1 -1
  129. package/dist/svg/Forbidden.d.ts +1 -1
  130. package/dist/svg/Forbidden.js +1 -1
  131. package/dist/svg/GenericPlaceholder.d.ts +4 -2
  132. package/dist/svg/GenericPlaceholder.d.ts.map +1 -1
  133. package/dist/svg/GenericPlaceholder.js +2 -2
  134. package/dist/svg/GenericPlaceholder.js.map +1 -1
  135. package/dist/svg/HUB.d.ts +1 -1
  136. package/dist/svg/HUB.js +1 -1
  137. package/dist/svg/Logo.d.ts +1 -1
  138. package/dist/svg/Logo.js +1 -1
  139. package/dist/svg/MiniLogo.d.ts +1 -1
  140. package/dist/svg/MiniLogo.js +1 -1
  141. package/dist/svg/NotFound.d.ts +1 -1
  142. package/dist/svg/NotFound.js +1 -1
  143. package/dist/svg/ServerError.d.ts +1 -1
  144. package/dist/svg/ServerError.js +1 -1
  145. package/dist/svg/Unauthenticated.d.ts +1 -1
  146. package/dist/svg/Unauthenticated.js +1 -1
  147. package/package.json +80 -80
  148. package/readme.md +66 -66
  149. package/src/components/AnimatedHeight.tsx +174 -174
  150. package/src/components/AsyncContent.tsx +78 -78
  151. package/src/components/BannerWarning.tsx +91 -91
  152. package/src/components/Breadcrumb/index.tsx +76 -76
  153. package/src/components/Breadcrumb/styled.ts +37 -37
  154. package/src/components/ButtonLoading.tsx +29 -29
  155. package/src/components/ChatBot.tsx +82 -82
  156. package/src/components/ContentValidateFilter.tsx +15 -15
  157. package/src/components/FadingOverflow.tsx +265 -265
  158. package/src/components/FileTreeView/More.tsx +114 -114
  159. package/src/components/FileTreeView/index.tsx +186 -186
  160. package/src/components/InfiniteScroll.tsx +24 -24
  161. package/src/components/InfoMaintenanceBanner.tsx +29 -29
  162. package/src/components/LazyMarkdown/BlockquoteMd.tsx +107 -107
  163. package/src/components/LazyMarkdown/CodeViewer.tsx +161 -161
  164. package/src/components/LazyMarkdown/Markdown.tsx +122 -122
  165. package/src/components/LazyMarkdown/MarkdownButton.tsx +24 -24
  166. package/src/components/LazyMarkdown/Video.tsx +13 -13
  167. package/src/components/LazyMarkdown/index.tsx +21 -21
  168. package/src/components/Placeholder.tsx +123 -118
  169. package/src/components/ScrollView.tsx +57 -57
  170. package/src/components/Select/BadgeItem.tsx +58 -58
  171. package/src/components/Select/ClearInput.tsx +24 -24
  172. package/src/components/Select/CloseItem.tsx +38 -38
  173. package/src/components/Select/CreatableSelect.tsx +155 -155
  174. package/src/components/Select/CustomMenu.tsx +16 -16
  175. package/src/components/Select/LabelItem.tsx +8 -8
  176. package/src/components/Select/MultiValue.tsx +49 -49
  177. package/src/components/Select/SelectInfiniteScroll.tsx +82 -82
  178. package/src/components/Select/SelectSearch.tsx +195 -195
  179. package/src/components/Select/index.tsx +7 -7
  180. package/src/components/Select/types.ts +8 -8
  181. package/src/components/SelectionList.tsx +427 -427
  182. package/src/components/StatusCircle.tsx +67 -67
  183. package/src/components/Stepper/Navigation.tsx +97 -97
  184. package/src/components/Stepper/Step.tsx +30 -30
  185. package/src/components/Stepper/Stepper.tsx +113 -113
  186. package/src/components/Stepper/headers.tsx +64 -64
  187. package/src/components/Stepper/index.ts +3 -3
  188. package/src/components/Table/HeaderItem.tsx +52 -52
  189. package/src/components/Table/SettingsVerticalMenu.tsx +50 -50
  190. package/src/components/Table/StyledLinkTable.tsx +22 -22
  191. package/src/components/Table/TableData.tsx +251 -251
  192. package/src/components/Table/index.tsx +2 -2
  193. package/src/components/TimelineSection.tsx +66 -66
  194. package/src/components/error/ErrorFeedback.tsx +217 -217
  195. package/src/components/error/NotFound.tsx +24 -24
  196. package/src/components/error/UnderMaintenance.tsx +30 -30
  197. package/src/components/error/index.ts +4 -4
  198. package/src/components/form/Form/Form.tsx +101 -101
  199. package/src/components/form/Form/FormGroup.tsx +221 -221
  200. package/src/components/form/Form/index.ts +2 -2
  201. package/src/components/form/SearchInput.tsx +69 -69
  202. package/src/components/form/Select/CustomSelect.tsx +232 -232
  203. package/src/components/form/Select/DetailedSelect.tsx +85 -85
  204. package/src/components/form/Select/Select.tsx +67 -67
  205. package/src/components/form/Select/index.ts +4 -4
  206. package/src/components/form/Select/styled.ts +165 -165
  207. package/src/components/form/Select/types.ts +112 -112
  208. package/src/components/form/Select/utils.tsx +28 -28
  209. package/src/components/notification/NotificationComponent.tsx +340 -340
  210. package/src/components/notification/NotificationItem.tsx +345 -337
  211. package/src/components/notification/NotificationList.tsx +179 -178
  212. package/src/components/notification/NotificationPlaceholder.tsx +44 -43
  213. package/src/components/notification/types.ts +72 -72
  214. package/src/containers/NotificationsPage.tsx +119 -119
  215. package/src/context/anchor.tsx +37 -37
  216. package/src/context/loading.tsx +36 -36
  217. package/src/context/notification/LazyNotificationList.ts +103 -103
  218. package/src/context/notification/NotificationController.ts +104 -104
  219. package/src/context/notification/context.tsx +23 -23
  220. package/src/context/notification/hooks.ts +98 -98
  221. package/src/context/notification/types.ts +66 -66
  222. package/src/hooks/date.ts +31 -31
  223. package/src/hooks/keyboard.tsx +128 -128
  224. package/src/hooks/manual-render.tsx +10 -10
  225. package/src/hooks/service-now.tsx +233 -233
  226. package/src/hooks/text.tsx +30 -30
  227. package/src/hooks/title.tsx +28 -28
  228. package/src/hooks/use-effect-once.tsx +43 -43
  229. package/src/index.ts +19 -19
  230. package/src/notifications.ts +11 -11
  231. package/src/svg/AI.tsx +41 -41
  232. package/src/svg/CS.tsx +48 -48
  233. package/src/svg/EDP.tsx +31 -31
  234. package/src/svg/Forbidden.tsx +22 -22
  235. package/src/svg/GenericPlaceholder.tsx +20 -20
  236. package/src/svg/HUB.tsx +48 -48
  237. package/src/svg/Logo.tsx +16 -16
  238. package/src/svg/MiniLogo.tsx +12 -12
  239. package/src/svg/NotFound.tsx +16 -16
  240. package/src/svg/ServerError.tsx +33 -33
  241. package/src/svg/Unauthenticated.tsx +16 -16
  242. package/src/svg/index.ts +11 -11
  243. package/src/utils/accessibility.ts +135 -135
  244. package/src/utils/cookie.ts +73 -73
  245. package/src/utils/promise.ts +5 -5
  246. package/src/utils/read-file.ts +16 -16
  247. 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,66 +1,66 @@
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
- createdSince?: number,
15
- }
16
-
17
- export interface LoadNotificationsOptions extends LoadNotificationsFilters {
18
- page: number,
19
- isBanner?: boolean,
20
- }
21
-
22
- export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
23
-
24
- export interface NotificationConfig {
25
- /**
26
- * Fetches the notifications according to the options passed as parameters.
27
- * @param options page, size and filters.
28
- * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
29
- */
30
- load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
31
- /**
32
- * Check for unread notifications since the date passed as parameter.
33
- * @param date the date to start the search from
34
- * @returns a promise that resolves to true if there are unread notifications or false otherwise.
35
- */
36
- checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
37
- /**
38
- * Marks the notification with the id passed as parameter as read (committed).
39
- * @param id the id of the notification.
40
- */
41
- markAsCommitted(id: string): Promise<void>,
42
- /**
43
- * How long (ms) we should wait before checking for unread notifications again.
44
- * @default 120000
45
- */
46
- pollingMS?: number,
47
- /**
48
- * Path to the notifications page.
49
- * @default '/notifications'
50
- */
51
- notificationsPath?: string,
52
- /**
53
- * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
54
- *
55
- * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
56
- *
57
- * @param id the id of the notification.
58
- */
59
- onClickAction?: (id: string) => void,
60
- /**
61
- * Function that is called whenever the "read more" button is clicked.
62
- */
63
- onClickViewNotification?: () => void,
64
- }
65
-
66
- 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
+ createdSince?: number,
15
+ }
16
+
17
+ export interface LoadNotificationsOptions extends LoadNotificationsFilters {
18
+ page: number,
19
+ isBanner?: boolean,
20
+ }
21
+
22
+ export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
23
+
24
+ export interface NotificationConfig {
25
+ /**
26
+ * Fetches the notifications according to the options passed as parameters.
27
+ * @param options page, size and filters.
28
+ * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
29
+ */
30
+ load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
31
+ /**
32
+ * Check for unread notifications since the date passed as parameter.
33
+ * @param date the date to start the search from
34
+ * @returns a promise that resolves to true if there are unread notifications or false otherwise.
35
+ */
36
+ checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
37
+ /**
38
+ * Marks the notification with the id passed as parameter as read (committed).
39
+ * @param id the id of the notification.
40
+ */
41
+ markAsCommitted(id: string): Promise<void>,
42
+ /**
43
+ * How long (ms) we should wait before checking for unread notifications again.
44
+ * @default 120000
45
+ */
46
+ pollingMS?: number,
47
+ /**
48
+ * Path to the notifications page.
49
+ * @default '/notifications'
50
+ */
51
+ notificationsPath?: string,
52
+ /**
53
+ * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
54
+ *
55
+ * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
56
+ *
57
+ * @param id the id of the notification.
58
+ */
59
+ onClickAction?: (id: string) => void,
60
+ /**
61
+ * Function that is called whenever the "read more" button is clicked.
62
+ */
63
+ onClickViewNotification?: () => void,
64
+ }
65
+
66
+ 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
+ }