@variocube/app-ui 1.15.0 → 1.16.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 (319) hide show
  1. package/esm/AppShell/AppShell.js +12 -12
  2. package/esm/AppShell/AppShell.js.map +1 -1
  3. package/esm/AppShell/index.js.map +1 -1
  4. package/esm/Input/ActionsMenu.js +1 -1
  5. package/esm/Input/ActionsMenu.js.map +1 -1
  6. package/esm/Input/EmailSenderField.js +3 -3
  7. package/esm/Input/EmailSenderField.js.map +1 -1
  8. package/esm/Input/Selector.js +2 -2
  9. package/esm/Input/Selector.js.map +1 -1
  10. package/esm/Paging/Paging.d.ts +1 -1
  11. package/esm/Paging/Paging.js +5 -5
  12. package/esm/Paging/Paging.js.map +1 -1
  13. package/esm/Paging/index.d.ts +2 -1
  14. package/esm/Paging/index.js.map +1 -1
  15. package/esm/VCThemeProvider/JetbrainsMonoFont.js +8 -8
  16. package/esm/VCThemeProvider/JetbrainsMonoFont.js.map +1 -1
  17. package/esm/VCThemeProvider/RobotoFont.js +8 -8
  18. package/esm/VCThemeProvider/RobotoFont.js.map +1 -1
  19. package/esm/VCThemeProvider/ThemeModeSwitcher.js +5 -5
  20. package/esm/VCThemeProvider/ThemeModeSwitcher.js.map +1 -1
  21. package/esm/VCThemeProvider/VCThemeProvider.d.ts +1 -1
  22. package/esm/VCThemeProvider/VCThemeProvider.js +21 -21
  23. package/esm/VCThemeProvider/VCThemeProvider.js.map +1 -1
  24. package/esm/audit/AuditChanges.js +9 -5
  25. package/esm/audit/AuditChanges.js.map +1 -1
  26. package/esm/audit/AuditTimeline.js +1 -1
  27. package/esm/audit/AuditTimeline.js.map +1 -1
  28. package/esm/audit/AuditTimelineItem.js +3 -3
  29. package/esm/audit/AuditTimelineItem.js.map +1 -1
  30. package/esm/audit/index.d.ts +1 -1
  31. package/esm/audit/index.js +1 -1
  32. package/esm/audit/index.js.map +1 -1
  33. package/esm/breadcrumbs.d.ts +2 -2
  34. package/esm/breadcrumbs.js +3 -3
  35. package/esm/breadcrumbs.js.map +1 -1
  36. package/esm/code/CodeBlock.d.ts +1 -1
  37. package/esm/code/CodeBlock.js +1 -1
  38. package/esm/code/CodeBlock.js.map +1 -1
  39. package/esm/confirm/ConfirmButton.js.map +1 -1
  40. package/esm/confirm/ConfirmDialog.js +1 -1
  41. package/esm/confirm/ConfirmDialog.js.map +1 -1
  42. package/esm/confirm/ConfirmMenuItem.js +1 -1
  43. package/esm/confirm/ConfirmMenuItem.js.map +1 -1
  44. package/esm/container/ContainerLayout.d.ts +1 -1
  45. package/esm/container/ContainerLayout.js +5 -5
  46. package/esm/container/ContainerLayout.js.map +1 -1
  47. package/esm/container/ContainerSettingsContext.d.ts +2 -2
  48. package/esm/container/ContainerSettingsContext.js +6 -6
  49. package/esm/container/ContainerSettingsContext.js.map +1 -1
  50. package/esm/container/ContainerWidthControl.js +2 -2
  51. package/esm/container/ContainerWidthControl.js.map +1 -1
  52. package/esm/container/index.d.ts +1 -1
  53. package/esm/container/index.js +1 -1
  54. package/esm/container/index.js.map +1 -1
  55. package/esm/content-table/ContentTable.d.ts +1 -1
  56. package/esm/content-table/ContentTable.js +26 -19
  57. package/esm/content-table/ContentTable.js.map +1 -1
  58. package/esm/content-table/UndrawEmpty.js.map +1 -1
  59. package/esm/content-table/index.js.map +1 -1
  60. package/esm/country/country-select.js.map +1 -1
  61. package/esm/country/index.d.ts +3 -3
  62. package/esm/country/index.js +3 -3
  63. package/esm/country/index.js.map +1 -1
  64. package/esm/country/locale-select.js +1 -1
  65. package/esm/country/locale-select.js.map +1 -1
  66. package/esm/country/locales.js +1 -1
  67. package/esm/country/locales.js.map +1 -1
  68. package/esm/country/phone-prefix-select.js +1 -1
  69. package/esm/country/phone-prefix-select.js.map +1 -1
  70. package/esm/cube/accessibility.js +2 -1
  71. package/esm/cube/accessibility.js.map +1 -1
  72. package/esm/data-table/DataTableColumnSettings.d.ts +1 -1
  73. package/esm/data-table/DataTableColumnSettings.js +2 -2
  74. package/esm/data-table/DataTableColumnSettings.js.map +1 -1
  75. package/esm/data-table/DataTableHeader.js +1 -1
  76. package/esm/data-table/DataTableHeader.js.map +1 -1
  77. package/esm/data-table/DataTableToolbar.js +1 -1
  78. package/esm/data-table/DataTableToolbar.js.map +1 -1
  79. package/esm/data-table/index.d.ts +4 -4
  80. package/esm/data-table/index.js +4 -4
  81. package/esm/data-table/index.js.map +1 -1
  82. package/esm/data-table/useDataTableColumnStorage.d.ts +9 -1
  83. package/esm/data-table/useDataTableColumnStorage.js +12 -5
  84. package/esm/data-table/useDataTableColumnStorage.js.map +1 -1
  85. package/esm/data-table/useDataTableStorage.d.ts +31 -6
  86. package/esm/data-table/useDataTableStorage.js +25 -5
  87. package/esm/data-table/useDataTableStorage.js.map +1 -1
  88. package/esm/date-pickers/PlainAdapterCommon.d.ts +1 -1
  89. package/esm/date-pickers/PlainAdapterCommon.js.map +1 -1
  90. package/esm/date-pickers/PlainDateAdapter.js.map +1 -1
  91. package/esm/date-pickers/PlainDatePicker.js +1 -1
  92. package/esm/date-pickers/PlainDatePicker.js.map +1 -1
  93. package/esm/date-pickers/PlainDatePicker.spec.js +2 -2
  94. package/esm/date-pickers/PlainDatePicker.spec.js.map +1 -1
  95. package/esm/date-pickers/PlainDateTimeAdapter.js.map +1 -1
  96. package/esm/date-pickers/PlainDateTimePicker.js +2 -2
  97. package/esm/date-pickers/PlainDateTimePicker.js.map +1 -1
  98. package/esm/date-pickers/PlainDateTimePicker.spec.js +2 -2
  99. package/esm/date-pickers/PlainDateTimePicker.spec.js.map +1 -1
  100. package/esm/date-pickers/PlainTimeAdapter.js.map +1 -1
  101. package/esm/date-pickers/PlainTimePicker.js +2 -2
  102. package/esm/date-pickers/PlainTimePicker.js.map +1 -1
  103. package/esm/date-pickers/PlainTimePicker.spec.js +2 -2
  104. package/esm/date-pickers/PlainTimePicker.spec.js.map +1 -1
  105. package/esm/date-pickers/TemporalAdapter.d.ts +1 -1
  106. package/esm/date-pickers/TemporalAdapter.js +1 -1
  107. package/esm/date-pickers/TemporalAdapter.js.map +1 -1
  108. package/esm/date-pickers/TimezoneSelect.js +348 -348
  109. package/esm/date-pickers/TimezoneSelect.js.map +1 -1
  110. package/esm/date-pickers/getFormatString.js.map +1 -1
  111. package/esm/date-pickers/index.d.ts +2 -1
  112. package/esm/date-pickers/index.js +1 -1
  113. package/esm/date-pickers/index.js.map +1 -1
  114. package/esm/date-pickers/parse.js.map +1 -1
  115. package/esm/date-pickers/timeframe-picker.js +7 -7
  116. package/esm/date-pickers/timeframe-picker.js.map +1 -1
  117. package/esm/date-pickers/useLocale.js.map +1 -1
  118. package/esm/date-pickers/useRenderInput.js +5 -3
  119. package/esm/date-pickers/useRenderInput.js.map +1 -1
  120. package/esm/formats/CompactFormat.js +1 -1
  121. package/esm/formats/CompactFormat.js.map +1 -1
  122. package/esm/formats/CompactFormat.spec.js +1 -1
  123. package/esm/formats/CompactFormat.spec.js.map +1 -1
  124. package/esm/formats/CurrencyFormat.js +1 -1
  125. package/esm/formats/CurrencyFormat.js.map +1 -1
  126. package/esm/formats/CurrencyFormat.spec.js +1 -1
  127. package/esm/formats/CurrencyFormat.spec.js.map +1 -1
  128. package/esm/formats/DecimalFormat.js +1 -1
  129. package/esm/formats/DecimalFormat.js.map +1 -1
  130. package/esm/formats/DecimalFormat.spec.js +1 -1
  131. package/esm/formats/DecimalFormat.spec.js.map +1 -1
  132. package/esm/formats/DurationFormat.js +2 -2
  133. package/esm/formats/DurationFormat.js.map +1 -1
  134. package/esm/formats/DurationFormat.spec.js +2 -2
  135. package/esm/formats/DurationFormat.spec.js.map +1 -1
  136. package/esm/formats/TemporalFormat.spec.js +2 -2
  137. package/esm/formats/TemporalFormat.spec.js.map +1 -1
  138. package/esm/formats/TemporalRangeFormat.js +1 -1
  139. package/esm/formats/TemporalRangeFormat.js.map +1 -1
  140. package/esm/formats/TemporalRangeFormat.spec.js +2 -2
  141. package/esm/formats/TemporalRangeFormat.spec.js.map +1 -1
  142. package/esm/formats/useDateTimeFormat.js +5 -2
  143. package/esm/formats/useDateTimeFormat.js.map +1 -1
  144. package/esm/formats/useNumberFormat.js +1 -1
  145. package/esm/formats/useNumberFormat.js.map +1 -1
  146. package/esm/formats/useRelativeTimeFormat.js +4 -2
  147. package/esm/formats/useRelativeTimeFormat.js.map +1 -1
  148. package/esm/forms/SearchForm.js +1 -1
  149. package/esm/forms/SearchForm.js.map +1 -1
  150. package/esm/getNavigatorLanguages.js.map +1 -1
  151. package/esm/getSupportedFormatLocale.js.map +1 -1
  152. package/esm/help/HelpDrawer.js +12 -8
  153. package/esm/help/HelpDrawer.js.map +1 -1
  154. package/esm/help/index.d.ts +1 -1
  155. package/esm/help/index.js +1 -1
  156. package/esm/help/index.js.map +1 -1
  157. package/esm/icons.d.ts +13 -13
  158. package/esm/icons.js +13 -13
  159. package/esm/icons.js.map +1 -1
  160. package/esm/layout/ErrorBoundary.js +36 -36
  161. package/esm/layout/ErrorBoundary.js.map +1 -1
  162. package/esm/layout/NotFound.js +6 -6
  163. package/esm/layout/NotFound.js.map +1 -1
  164. package/esm/layout/NotFoundSvg.js.map +1 -1
  165. package/esm/layout/UserNav.js +18 -18
  166. package/esm/layout/UserNav.js.map +1 -1
  167. package/esm/layout/index.d.ts +2 -2
  168. package/esm/layout/index.js +2 -2
  169. package/esm/layout/index.js.map +1 -1
  170. package/esm/logo/Logo.js +2 -2
  171. package/esm/logo/Logo.js.map +1 -1
  172. package/esm/logo/VCAppLogo.js +1 -1
  173. package/esm/logo/VCAppLogo.js.map +1 -1
  174. package/esm/logo/index.d.ts +3 -3
  175. package/esm/logo/index.js +3 -3
  176. package/esm/logo/index.js.map +1 -1
  177. package/esm/splash/index.js +2 -2
  178. package/esm/splash/index.js.map +1 -1
  179. package/esm/storage/MemoryStorage.d.ts +6 -4
  180. package/esm/storage/MemoryStorage.js +13 -4
  181. package/esm/storage/MemoryStorage.js.map +1 -1
  182. package/esm/storage/index.d.ts +2 -1
  183. package/esm/storage/index.js +1 -1
  184. package/esm/storage/index.js.map +1 -1
  185. package/esm/storage/storage.d.ts +6 -4
  186. package/esm/storage/storage.js +35 -15
  187. package/esm/storage/storage.js.map +1 -1
  188. package/esm/storage/types.d.ts +6 -0
  189. package/esm/storage/types.js +2 -0
  190. package/esm/storage/types.js.map +1 -0
  191. package/esm/storage/useStorage.d.ts +10 -1
  192. package/esm/storage/useStorage.js +18 -6
  193. package/esm/storage/useStorage.js.map +1 -1
  194. package/esm/tabs/Tabs.d.ts +2 -2
  195. package/esm/tabs/Tabs.js +15 -15
  196. package/esm/tabs/Tabs.js.map +1 -1
  197. package/esm/tabs/index.js.map +1 -1
  198. package/esm/temporal/index.d.ts +1 -1
  199. package/esm/temporal/index.js +1 -1
  200. package/esm/temporal/index.js.map +1 -1
  201. package/esm/temporal/parse.js.map +1 -1
  202. package/esm/temporal/polyfill.d.ts +1 -1
  203. package/esm/temporal/polyfill.js +3 -3
  204. package/esm/temporal/polyfill.js.map +1 -1
  205. package/esm/utils/defined.js.map +1 -1
  206. package/esm/utils/index.d.ts +1 -1
  207. package/esm/utils/index.js +1 -1
  208. package/esm/utils/index.js.map +1 -1
  209. package/esm/utils/useFlag.js.map +1 -1
  210. package/esm/utils/useIsMounted.js.map +1 -1
  211. package/package.json +8 -9
  212. package/src/AppShell/AppShell.tsx +147 -140
  213. package/src/AppShell/index.tsx +1 -1
  214. package/src/Input/ActionsMenu.tsx +70 -73
  215. package/src/Input/EmailSenderField.tsx +59 -52
  216. package/src/Input/Selector.tsx +15 -15
  217. package/src/LanguageSwitcher/index.ts +1 -1
  218. package/src/Paging/Paging.ts +56 -57
  219. package/src/Paging/index.ts +2 -1
  220. package/src/VCThemeProvider/JetbrainsMonoFont.tsx +52 -42
  221. package/src/VCThemeProvider/RobotoFont.tsx +47 -39
  222. package/src/VCThemeProvider/ThemeModeSwitcher.tsx +17 -17
  223. package/src/VCThemeProvider/VCThemeProvider.tsx +149 -145
  224. package/src/audit/AuditChanges.tsx +18 -12
  225. package/src/audit/AuditTimeline.tsx +16 -17
  226. package/src/audit/AuditTimelineItem.tsx +58 -61
  227. package/src/audit/index.ts +2 -2
  228. package/src/audit/types.ts +27 -28
  229. package/src/breadcrumbs.tsx +11 -15
  230. package/src/code/CodeBlock.tsx +10 -10
  231. package/src/confirm/ConfirmButton.tsx +56 -56
  232. package/src/confirm/ConfirmDialog.tsx +54 -54
  233. package/src/confirm/ConfirmMenuItem.tsx +51 -53
  234. package/src/container/ContainerLayout.tsx +7 -8
  235. package/src/container/ContainerSettingsContext.tsx +49 -46
  236. package/src/container/ContainerWidthControl.tsx +11 -9
  237. package/src/container/index.ts +8 -3
  238. package/src/content-table/ContentTable.tsx +235 -197
  239. package/src/content-table/UndrawEmpty.tsx +281 -281
  240. package/src/content-table/index.ts +1 -1
  241. package/src/country/country-select.tsx +0 -1
  242. package/src/country/index.ts +3 -3
  243. package/src/country/locale-select.tsx +36 -36
  244. package/src/country/locales.ts +185 -187
  245. package/src/country/phone-prefix-select.tsx +4 -5
  246. package/src/cube/accessibility.tsx +2 -1
  247. package/src/data-table/DataTableColumnSettings.tsx +181 -181
  248. package/src/data-table/DataTableHeader.tsx +6 -6
  249. package/src/data-table/DataTableToolbar.tsx +6 -6
  250. package/src/data-table/index.tsx +4 -4
  251. package/src/data-table/useDataTableColumnStorage.ts +30 -19
  252. package/src/data-table/useDataTableStorage.spec.ts +108 -0
  253. package/src/data-table/useDataTableStorage.ts +91 -36
  254. package/src/date-pickers/PlainAdapterCommon.ts +118 -120
  255. package/src/date-pickers/PlainDateAdapter.spec.ts +44 -46
  256. package/src/date-pickers/PlainDateAdapter.ts +130 -132
  257. package/src/date-pickers/PlainDatePicker.spec.tsx +17 -18
  258. package/src/date-pickers/PlainDatePicker.tsx +33 -36
  259. package/src/date-pickers/PlainDateTimeAdapter.spec.ts +44 -46
  260. package/src/date-pickers/PlainDateTimeAdapter.ts +141 -134
  261. package/src/date-pickers/PlainDateTimePicker.spec.tsx +17 -18
  262. package/src/date-pickers/PlainDateTimePicker.tsx +35 -36
  263. package/src/date-pickers/PlainTimeAdapter.ts +219 -221
  264. package/src/date-pickers/PlainTimePicker.spec.tsx +17 -18
  265. package/src/date-pickers/PlainTimePicker.tsx +35 -34
  266. package/src/date-pickers/TemporalAdapter.ts +110 -111
  267. package/src/date-pickers/TimezoneSelect.tsx +375 -375
  268. package/src/date-pickers/getFormatString.ts +29 -29
  269. package/src/date-pickers/index.ts +6 -5
  270. package/src/date-pickers/parse.spec.ts +37 -40
  271. package/src/date-pickers/parse.ts +44 -47
  272. package/src/date-pickers/timeframe-picker.tsx +16 -17
  273. package/src/date-pickers/useLocale.ts +1 -1
  274. package/src/date-pickers/useRenderInput.tsx +24 -15
  275. package/src/formats/CompactFormat.spec.tsx +18 -20
  276. package/src/formats/CompactFormat.tsx +30 -30
  277. package/src/formats/CurrencyFormat.spec.tsx +18 -20
  278. package/src/formats/CurrencyFormat.tsx +52 -52
  279. package/src/formats/DecimalFormat.spec.tsx +18 -20
  280. package/src/formats/DecimalFormat.tsx +47 -47
  281. package/src/formats/DurationFormat.spec.tsx +48 -49
  282. package/src/formats/DurationFormat.tsx +51 -51
  283. package/src/formats/TemporalFormat.spec.tsx +93 -95
  284. package/src/formats/TemporalRangeFormat.spec.tsx +143 -144
  285. package/src/formats/TemporalRangeFormat.tsx +54 -54
  286. package/src/formats/types.ts +1 -3
  287. package/src/formats/useDateTimeFormat.ts +5 -2
  288. package/src/formats/useNumberFormat.ts +3 -3
  289. package/src/formats/useRelativeTimeFormat.ts +5 -3
  290. package/src/forms/SearchForm.tsx +37 -38
  291. package/src/getNavigatorLanguages.ts +4 -5
  292. package/src/getSupportedFormatLocale.ts +10 -10
  293. package/src/help/HelpDrawer.tsx +76 -55
  294. package/src/help/index.ts +3 -3
  295. package/src/icons.ts +21 -21
  296. package/src/layout/ErrorBoundary.tsx +226 -215
  297. package/src/layout/NotFound.tsx +36 -39
  298. package/src/layout/NotFoundSvg.tsx +193 -169
  299. package/src/layout/UserNav.tsx +98 -98
  300. package/src/layout/index.ts +3 -3
  301. package/src/logo/Logo.tsx +6 -7
  302. package/src/logo/VCAppLogo.tsx +22 -20
  303. package/src/logo/index.tsx +3 -3
  304. package/src/splash/index.tsx +25 -25
  305. package/src/storage/MemoryStorage.ts +22 -15
  306. package/src/storage/index.ts +2 -1
  307. package/src/storage/storage.spec.ts +184 -0
  308. package/src/storage/storage.ts +37 -15
  309. package/src/storage/types.ts +6 -0
  310. package/src/storage/useStorage.ts +21 -6
  311. package/src/tabs/Tabs.tsx +172 -170
  312. package/src/tabs/index.ts +1 -1
  313. package/src/temporal/index.ts +1 -1
  314. package/src/temporal/parse.ts +1 -2
  315. package/src/temporal/polyfill.ts +11 -11
  316. package/src/utils/defined.ts +2 -3
  317. package/src/utils/index.ts +1 -1
  318. package/src/utils/useFlag.ts +5 -5
  319. package/src/utils/useIsMounted.ts +9 -9
@@ -1,28 +1,30 @@
1
1
  import {Box, BoxProps} from "@mui/material";
2
- import {VCLogoIcon} from "./VCLogoIcon";
3
2
  import React from "react";
3
+ import {VCLogoIcon} from "./VCLogoIcon";
4
4
 
5
5
  interface VCAppLogoProps extends BoxProps {
6
- appName?: string;
6
+ appName?: string;
7
7
  }
8
8
 
9
9
  export function VCAppLogo({appName, ...props}: VCAppLogoProps) {
10
- return (
11
- <Box {...props}>
12
- <Box sx={{display: "flex", flexFlow: "row nowrap", alignItems: "center"}}>
13
- <VCLogoIcon width="auto" height="32" display="block"/>
14
- {appName && (
15
- <Box sx={{
16
- lineHeight: 1,
17
- fontSize: "20px",
18
- fontWeight: 900,
19
- textTransform: "uppercase",
20
- marginLeft: "8px",
21
- }}>
22
- {appName}
23
- </Box>
24
- )}
25
- </Box>
26
- </Box>
27
- )
10
+ return (
11
+ <Box {...props}>
12
+ <Box sx={{display: "flex", flexFlow: "row nowrap", alignItems: "center"}}>
13
+ <VCLogoIcon width="auto" height="32" display="block" />
14
+ {appName && (
15
+ <Box
16
+ sx={{
17
+ lineHeight: 1,
18
+ fontSize: "20px",
19
+ fontWeight: 900,
20
+ textTransform: "uppercase",
21
+ marginLeft: "8px",
22
+ }}
23
+ >
24
+ {appName}
25
+ </Box>
26
+ )}
27
+ </Box>
28
+ </Box>
29
+ );
28
30
  }
@@ -1,4 +1,4 @@
1
- export * from "./VCLogoIcon";
2
- export * from "./VCLogo";
3
- export * from "./VCAppLogo";
4
1
  export * from "./Logo";
2
+ export * from "./VCAppLogo";
3
+ export * from "./VCLogo";
4
+ export * from "./VCLogoIcon";
@@ -2,33 +2,33 @@ import React, {FunctionComponentElement} from "react";
2
2
  import ReactDOM from "react-dom";
3
3
 
4
4
  export function render(element: FunctionComponentElement<any> | Array<FunctionComponentElement<any>>) {
5
- try {
6
- ReactDOM.render(element, document.querySelector("#react-root"), removeSplash);
7
- }
8
- catch (error) {
9
- // show the error using the class defined in the template
10
- // this can obviously be improved
11
- ReactDOM.render((
12
- <div className="splash-error">
13
- <h1>Failed to load application</h1>
14
- <p>
15
- An error occurred while loading the application.
16
- Please make sure you are using a current browser version.
17
- </p>
18
- </div>
19
- ), document.body);
20
- }
5
+ try {
6
+ ReactDOM.render(element, document.querySelector("#react-root"), removeSplash);
7
+ } catch (error) {
8
+ // show the error using the class defined in the template
9
+ // this can obviously be improved
10
+ ReactDOM.render(
11
+ <div className="splash-error">
12
+ <h1>Failed to load application</h1>
13
+ <p>
14
+ An error occurred while loading the application. Please make sure you are using a current browser
15
+ version.
16
+ </p>
17
+ </div>,
18
+ document.body,
19
+ );
20
+ }
21
21
  }
22
22
 
23
23
  export async function removeSplash() {
24
- // wait for fonts to become ready
25
- if (document.fonts) {
26
- await document.fonts.ready;
27
- }
24
+ // wait for fonts to become ready
25
+ if (document.fonts) {
26
+ await document.fonts.ready;
27
+ }
28
28
 
29
- const preloader = document.querySelector("#splash");
30
- if (preloader) {
31
- preloader.classList.add("fadeout");
32
- setTimeout(() => preloader.parentNode?.removeChild(preloader), 500);
33
- }
29
+ const preloader = document.querySelector("#splash");
30
+ if (preloader) {
31
+ preloader.classList.add("fadeout");
32
+ setTimeout(() => preloader.parentNode?.removeChild(preloader), 500);
33
+ }
34
34
  }
@@ -1,21 +1,28 @@
1
+ export class MemoryStorage implements Storage {
2
+ private readonly data = new Map<string, string>();
1
3
 
2
- export class MemoryStorage {
4
+ get length(): number {
5
+ return this.data.size;
6
+ }
3
7
 
4
- private readonly data = new Map<string, string>();
8
+ key(index: number): string | null {
9
+ const keys = Array.from(this.data.keys());
10
+ return keys[index] ?? null;
11
+ }
5
12
 
6
- getItem(key: string) {
7
- return this.data.get(key);
8
- }
13
+ getItem(key: string): string | null {
14
+ return this.data.get(key) ?? null;
15
+ }
9
16
 
10
- setItem(key: string, value: string) {
11
- return this.data.set(key, value);
12
- }
17
+ setItem(key: string, value: string): void {
18
+ this.data.set(key, value);
19
+ }
13
20
 
14
- removeItem(key: string) {
15
- return this.data.delete(key);
16
- }
21
+ removeItem(key: string): void {
22
+ this.data.delete(key);
23
+ }
17
24
 
18
- clear() {
19
- return this.data.clear();
20
- }
21
- }
25
+ clear(): void {
26
+ this.data.clear();
27
+ }
28
+ }
@@ -1,2 +1,3 @@
1
- export * from "./useStorage";
2
1
  export * from "./storage";
2
+ export type { StorageType } from "./types";
3
+ export * from "./useStorage";
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import {storage} from "./storage";
6
+
7
+ describe("StorageWrapper", () => {
8
+ beforeEach(() => {
9
+ localStorage.clear();
10
+ sessionStorage.clear();
11
+ });
12
+
13
+ describe("read/write with storage type", () => {
14
+ test("writes to localStorage by default", () => {
15
+ storage.write("test-key", "value");
16
+ expect(localStorage.getItem("test-key")).toBe("value");
17
+ expect(sessionStorage.getItem("test-key")).toBeNull();
18
+ });
19
+
20
+ test("writes to localStorage when explicitly specified", () => {
21
+ storage.write("test-key", "value", "local");
22
+ expect(localStorage.getItem("test-key")).toBe("value");
23
+ expect(sessionStorage.getItem("test-key")).toBeNull();
24
+ });
25
+
26
+ test("writes to sessionStorage when specified", () => {
27
+ storage.write("test-key", "value", "session");
28
+ expect(sessionStorage.getItem("test-key")).toBe("value");
29
+ expect(localStorage.getItem("test-key")).toBeNull();
30
+ });
31
+
32
+ test("reads from correct storage area by default (local)", () => {
33
+ localStorage.setItem("key1", "local-value");
34
+ sessionStorage.setItem("key1", "session-value");
35
+
36
+ expect(storage.read("key1")).toBe("local-value");
37
+ });
38
+
39
+ test("reads from localStorage when explicitly specified", () => {
40
+ localStorage.setItem("key1", "local-value");
41
+ sessionStorage.setItem("key1", "session-value");
42
+
43
+ expect(storage.read("key1", "local")).toBe("local-value");
44
+ });
45
+
46
+ test("reads from sessionStorage when specified", () => {
47
+ localStorage.setItem("key1", "local-value");
48
+ sessionStorage.setItem("key1", "session-value");
49
+
50
+ expect(storage.read("key1", "session")).toBe("session-value");
51
+ });
52
+
53
+ test("returns null for non-existent keys", () => {
54
+ expect(storage.read("non-existent")).toBeNull();
55
+ expect(storage.read("non-existent", "local")).toBeNull();
56
+ expect(storage.read("non-existent", "session")).toBeNull();
57
+ });
58
+ });
59
+
60
+ describe("delete with storage type", () => {
61
+ test("deletes from localStorage by default", () => {
62
+ localStorage.setItem("key", "local");
63
+ sessionStorage.setItem("key", "session");
64
+
65
+ storage.delete("key");
66
+ expect(localStorage.getItem("key")).toBeNull();
67
+ expect(sessionStorage.getItem("key")).toBe("session");
68
+ });
69
+
70
+ test("deletes from localStorage when explicitly specified", () => {
71
+ localStorage.setItem("key", "local");
72
+ sessionStorage.setItem("key", "session");
73
+
74
+ storage.delete("key", "local");
75
+ expect(localStorage.getItem("key")).toBeNull();
76
+ expect(sessionStorage.getItem("key")).toBe("session");
77
+ });
78
+
79
+ test("deletes from sessionStorage when specified", () => {
80
+ localStorage.setItem("key", "local");
81
+ sessionStorage.setItem("key", "session");
82
+
83
+ storage.delete("key", "session");
84
+ expect(localStorage.getItem("key")).toBe("local");
85
+ expect(sessionStorage.getItem("key")).toBeNull();
86
+ });
87
+ });
88
+
89
+ describe("storage isolation", () => {
90
+ test("local and session storage are isolated", () => {
91
+ storage.write("shared-key", "local-value", "local");
92
+ storage.write("shared-key", "session-value", "session");
93
+
94
+ expect(storage.read("shared-key", "local")).toBe("local-value");
95
+ expect(storage.read("shared-key", "session")).toBe("session-value");
96
+ });
97
+
98
+ test("deleting from one storage does not affect the other", () => {
99
+ storage.write("shared-key", "local-value", "local");
100
+ storage.write("shared-key", "session-value", "session");
101
+
102
+ storage.delete("shared-key", "local");
103
+
104
+ expect(storage.read("shared-key", "local")).toBeNull();
105
+ expect(storage.read("shared-key", "session")).toBe("session-value");
106
+ });
107
+ });
108
+
109
+ describe("write with undefined value removes key", () => {
110
+ test("removes key when writing undefined to localStorage", () => {
111
+ storage.write("key", "value", "local");
112
+ expect(storage.read("key", "local")).toBe("value");
113
+
114
+ storage.write("key", undefined, "local");
115
+ expect(storage.read("key", "local")).toBeNull();
116
+ });
117
+
118
+ test("removes key when writing undefined to sessionStorage", () => {
119
+ storage.write("key", "value", "session");
120
+ expect(storage.read("key", "session")).toBe("value");
121
+
122
+ storage.write("key", undefined, "session");
123
+ expect(storage.read("key", "session")).toBeNull();
124
+ });
125
+ });
126
+
127
+ describe("change listeners", () => {
128
+ test("notifies listeners on write", () => {
129
+ const listener = jest.fn();
130
+ storage.addChangeListener("key", listener);
131
+
132
+ storage.write("key", "value");
133
+ expect(listener).toHaveBeenCalledWith(null, "value");
134
+
135
+ storage.removeChangeListener("key", listener);
136
+ });
137
+
138
+ test("notifies listeners on delete", () => {
139
+ storage.write("key", "value");
140
+
141
+ const listener = jest.fn();
142
+ storage.addChangeListener("key", listener);
143
+
144
+ storage.delete("key");
145
+ expect(listener).toHaveBeenCalledWith("value", undefined);
146
+
147
+ storage.removeChangeListener("key", listener);
148
+ });
149
+
150
+ test("notifies listeners on update", () => {
151
+ storage.write("key", "old-value");
152
+
153
+ const listener = jest.fn();
154
+ storage.addChangeListener("key", listener);
155
+
156
+ storage.write("key", "new-value");
157
+ expect(listener).toHaveBeenCalledWith("old-value", "new-value");
158
+
159
+ storage.removeChangeListener("key", listener);
160
+ });
161
+
162
+ test("does not notify after listener removal", () => {
163
+ const listener = jest.fn();
164
+ storage.addChangeListener("key", listener);
165
+ storage.removeChangeListener("key", listener);
166
+
167
+ storage.write("key", "value");
168
+ expect(listener).not.toHaveBeenCalled();
169
+ });
170
+
171
+ test("notifies listeners for different storage types", () => {
172
+ const listener = jest.fn();
173
+ storage.addChangeListener("key", listener);
174
+
175
+ storage.write("key", "local-value", "local");
176
+ expect(listener).toHaveBeenCalledWith(null, "local-value");
177
+
178
+ storage.write("key", "session-value", "session");
179
+ expect(listener).toHaveBeenCalledWith(null, "session-value");
180
+
181
+ storage.removeChangeListener("key", listener);
182
+ });
183
+ });
184
+ });
@@ -1,4 +1,5 @@
1
1
  import {MemoryStorage} from "./MemoryStorage";
2
+ import {StorageType} from "./types";
2
3
 
3
4
  const TEST_KEY = "__check_storage_supported";
4
5
 
@@ -15,26 +16,44 @@ function isSupported(storage?: Storage) {
15
16
  }
16
17
  }
17
18
 
18
- function findSupportedStorage() {
19
- return [window.localStorage, window.sessionStorage].find(isSupported)
20
- || new MemoryStorage();
19
+ function getStorageArea(type: StorageType): Storage {
20
+ const preferred = type === "local" ? window.localStorage : window.sessionStorage;
21
+ const fallback = type === "local" ? window.sessionStorage : window.localStorage;
22
+
23
+ if (isSupported(preferred)) return preferred;
24
+ if (isSupported(fallback)) return fallback;
25
+ return new MemoryStorage();
21
26
  }
22
27
 
23
28
  type StorageChangeListener = (oldValue?: string, newValue?: string) => void;
24
29
 
25
30
  class StorageWrapper {
26
31
  private readonly listeners = new Map<string, Set<StorageChangeListener>>();
27
- private readonly storageArea = findSupportedStorage();
32
+ private readonly storageAreas = new Map<StorageType, Storage>();
28
33
 
29
34
  constructor() {
35
+ // Initialize both storage areas
36
+ this.storageAreas.set("local", getStorageArea("local"));
37
+ this.storageAreas.set("session", getStorageArea("session"));
38
+
30
39
  window.addEventListener("storage", event => {
31
40
  const {storageArea, key, oldValue, newValue} = event;
32
- if (storageArea == this.storageArea && key && oldValue != newValue) {
33
- this.notifyChangeListener(key, oldValue || undefined, newValue || undefined);
41
+ // Check against both storage areas
42
+ if (key && oldValue != newValue) {
43
+ for (const area of this.storageAreas.values()) {
44
+ if (storageArea === area) {
45
+ this.notifyChangeListener(key, oldValue || undefined, newValue || undefined);
46
+ break;
47
+ }
48
+ }
34
49
  }
35
50
  });
36
51
  }
37
52
 
53
+ private getArea(type?: StorageType): Storage {
54
+ return this.storageAreas.get(type ?? "local")!;
55
+ }
56
+
38
57
  addChangeListener(key: string, listener: StorageChangeListener) {
39
58
  this.getOrCreateListeners(key).add(listener);
40
59
  }
@@ -43,13 +62,14 @@ class StorageWrapper {
43
62
  this.getOrCreateListeners(key).delete(listener);
44
63
  }
45
64
 
46
- write(key: string, value?: string) {
47
- const oldValue = this.read(key);
65
+ write(key: string, value?: string, type?: StorageType) {
66
+ const oldValue = this.read(key, type);
67
+ const area = this.getArea(type);
48
68
  try {
49
69
  if (value !== undefined) {
50
- this.storageArea.setItem(key, value);
70
+ area.setItem(key, value);
51
71
  } else {
52
- this.storageArea.removeItem(key);
72
+ area.removeItem(key);
53
73
  }
54
74
  this.notifyChangeListener(key, oldValue, value);
55
75
  } catch (error) {
@@ -57,19 +77,21 @@ class StorageWrapper {
57
77
  }
58
78
  }
59
79
 
60
- delete(key: string) {
61
- const oldValue = this.read(key);
80
+ delete(key: string, type?: StorageType) {
81
+ const oldValue = this.read(key, type);
82
+ const area = this.getArea(type);
62
83
  try {
63
- this.storageArea.removeItem(key);
84
+ area.removeItem(key);
64
85
  this.notifyChangeListener(key, oldValue, undefined);
65
86
  } catch (error) {
66
87
  // ignore error
67
88
  }
68
89
  }
69
90
 
70
- read(key: string) {
91
+ read(key: string, type?: StorageType) {
92
+ const area = this.getArea(type);
71
93
  try {
72
- return this.storageArea.getItem(key);
94
+ return area.getItem(key);
73
95
  } catch (error) {
74
96
  // ignore error
75
97
  return null;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The type of browser storage to use.
3
+ * - 'local': Uses localStorage (persists across browser sessions)
4
+ * - 'session': Uses sessionStorage (cleared when tab closes)
5
+ */
6
+ export type StorageType = "local" | "session";
@@ -1,13 +1,14 @@
1
1
  import {useCallback, useLayoutEffect, useMemo, useState} from "react";
2
2
  import {storage} from "./storage";
3
+ import {StorageType} from "./types";
3
4
 
4
- export function useStorage<T>(key: string, defaultValue: T): [T, (newValue: T) => void] {
5
+ export function useStorage<T>(key: string, defaultValue: T, storageType?: StorageType): [T, (newValue: T) => void] {
5
6
  const defaultValueSerialized = useMemo(() => JSON.stringify(defaultValue), [defaultValue]);
6
7
 
7
8
  const readStateFromStorage = useCallback(() => {
8
- const storageValue = storage.read(key);
9
+ const storageValue = storage.read(key, storageType);
9
10
  return storageValue ?? defaultValueSerialized;
10
- }, [key, defaultValueSerialized]);
11
+ }, [key, defaultValueSerialized, storageType]);
11
12
 
12
13
  const [value, setValue] = useState(readStateFromStorage);
13
14
 
@@ -25,11 +26,25 @@ export function useStorage<T>(key: string, defaultValue: T): [T, (newValue: T) =
25
26
  const setTypedValue = useCallback((newValue: T) => {
26
27
  const value = JSON.stringify(newValue);
27
28
  if (value != defaultValueSerialized) {
28
- storage.write(key, value);
29
+ storage.write(key, value, storageType);
29
30
  } else {
30
- storage.delete(key);
31
+ storage.delete(key, storageType);
31
32
  }
32
- }, [key, defaultValueSerialized]);
33
+ }, [key, defaultValueSerialized, storageType]);
33
34
 
34
35
  return [typedValue, setTypedValue];
35
36
  }
37
+
38
+ /**
39
+ * Convenience hook for localStorage. Equivalent to `useStorage(key, defaultValue, "local")`.
40
+ */
41
+ export function useLocalStorage<T>(key: string, defaultValue: T): [T, (newValue: T) => void] {
42
+ return useStorage(key, defaultValue, "local");
43
+ }
44
+
45
+ /**
46
+ * Convenience hook for sessionStorage. Equivalent to `useStorage(key, defaultValue, "session")`.
47
+ */
48
+ export function useSessionStorage<T>(key: string, defaultValue: T): [T, (newValue: T) => void] {
49
+ return useStorage(key, defaultValue, "session");
50
+ }