@dbcdk/react-components 0.0.161 → 0.0.163
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.
- package/dist/components/menu/Menu.cjs +15 -1
- package/dist/components/menu/Menu.d.ts +4 -0
- package/dist/components/menu/Menu.js +15 -1
- package/dist/components/menu/Menu.module.css +16 -0
- package/dist/components/nav-bar/NavBar.module.css +2 -2
- package/dist/components/page-layout/PageLayout.module.css +4 -0
- package/dist/hooks/usePersistentState.cjs +29 -9
- package/dist/hooks/usePersistentState.d.ts +21 -1
- package/dist/hooks/usePersistentState.js +30 -10
- package/dist/utils/cookie.utils.cjs +44 -0
- package/dist/utils/cookie.utils.d.ts +19 -0
- package/dist/utils/cookie.utils.js +40 -0
- package/package.json +1 -1
|
@@ -428,6 +428,19 @@ const MenuHeader = React__namespace.forwardRef(
|
|
|
428
428
|
)
|
|
429
429
|
);
|
|
430
430
|
MenuHeader.displayName = "Menu.Header";
|
|
431
|
+
const MenuDescription = React__namespace.forwardRef(
|
|
432
|
+
({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
433
|
+
"li",
|
|
434
|
+
{
|
|
435
|
+
ref,
|
|
436
|
+
role: "presentation",
|
|
437
|
+
className: [styles__default.default.description, className].filter(Boolean).join(" "),
|
|
438
|
+
...props,
|
|
439
|
+
children
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
);
|
|
443
|
+
MenuDescription.displayName = "Menu.Description";
|
|
431
444
|
const Menu = Object.assign(MenuBase, {
|
|
432
445
|
Item: MenuItem,
|
|
433
446
|
CheckItem: MenuCheckItem,
|
|
@@ -435,7 +448,8 @@ const Menu = Object.assign(MenuBase, {
|
|
|
435
448
|
SwitchItem: MenuSwitchItem,
|
|
436
449
|
Label: MenuLabel,
|
|
437
450
|
Separator: MenuSeparator,
|
|
438
|
-
Header: MenuHeader
|
|
451
|
+
Header: MenuHeader,
|
|
452
|
+
Description: MenuDescription
|
|
439
453
|
});
|
|
440
454
|
|
|
441
455
|
exports.Menu = Menu;
|
|
@@ -15,6 +15,9 @@ export type MenuSeparatorProps = React.LiHTMLAttributes<HTMLLIElement>;
|
|
|
15
15
|
export interface MenuHeaderProps extends React.LiHTMLAttributes<HTMLLIElement> {
|
|
16
16
|
children: React.ReactNode;
|
|
17
17
|
}
|
|
18
|
+
export interface MenuDescriptionProps extends React.LiHTMLAttributes<HTMLLIElement> {
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
}
|
|
18
21
|
export interface MenuItemProps extends React.LiHTMLAttributes<HTMLLIElement> {
|
|
19
22
|
children: React.ReactNode;
|
|
20
23
|
active?: boolean;
|
|
@@ -94,5 +97,6 @@ export declare const Menu: React.ForwardRefExoticComponent<MenuProps & React.Ref
|
|
|
94
97
|
SwitchItem: React.ForwardRefExoticComponent<MenuSwitchItemProps & React.RefAttributes<HTMLLIElement>>;
|
|
95
98
|
Separator: React.ForwardRefExoticComponent<MenuSeparatorProps & React.RefAttributes<HTMLLIElement>>;
|
|
96
99
|
Header: React.ForwardRefExoticComponent<MenuHeaderProps & React.RefAttributes<HTMLLIElement>>;
|
|
100
|
+
Description: React.ForwardRefExoticComponent<MenuDescriptionProps & React.RefAttributes<HTMLLIElement>>;
|
|
97
101
|
Label: React.ForwardRefExoticComponent<MenuLabelProps & React.RefAttributes<HTMLSpanElement>>;
|
|
98
102
|
};
|
|
@@ -403,6 +403,19 @@ const MenuHeader = React.forwardRef(
|
|
|
403
403
|
)
|
|
404
404
|
);
|
|
405
405
|
MenuHeader.displayName = "Menu.Header";
|
|
406
|
+
const MenuDescription = React.forwardRef(
|
|
407
|
+
({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
408
|
+
"li",
|
|
409
|
+
{
|
|
410
|
+
ref,
|
|
411
|
+
role: "presentation",
|
|
412
|
+
className: [styles.description, className].filter(Boolean).join(" "),
|
|
413
|
+
...props,
|
|
414
|
+
children
|
|
415
|
+
}
|
|
416
|
+
)
|
|
417
|
+
);
|
|
418
|
+
MenuDescription.displayName = "Menu.Description";
|
|
406
419
|
const Menu = Object.assign(MenuBase, {
|
|
407
420
|
Item: MenuItem,
|
|
408
421
|
CheckItem: MenuCheckItem,
|
|
@@ -410,7 +423,8 @@ const Menu = Object.assign(MenuBase, {
|
|
|
410
423
|
SwitchItem: MenuSwitchItem,
|
|
411
424
|
Label: MenuLabel,
|
|
412
425
|
Separator: MenuSeparator,
|
|
413
|
-
Header: MenuHeader
|
|
426
|
+
Header: MenuHeader,
|
|
427
|
+
Description: MenuDescription
|
|
414
428
|
});
|
|
415
429
|
|
|
416
430
|
export { Menu };
|
|
@@ -251,6 +251,22 @@
|
|
|
251
251
|
color: inherit;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
.description {
|
|
255
|
+
padding-block: var(--spacing-xxs);
|
|
256
|
+
padding-inline: var(--spacing-md);
|
|
257
|
+
color: var(--color-fg-muted);
|
|
258
|
+
font-size: var(--font-size-xs);
|
|
259
|
+
font-weight: 400;
|
|
260
|
+
line-height: var(--line-height-normal);
|
|
261
|
+
text-transform: none;
|
|
262
|
+
letter-spacing: normal;
|
|
263
|
+
cursor: default;
|
|
264
|
+
user-select: text;
|
|
265
|
+
white-space: normal;
|
|
266
|
+
overflow-wrap: anywhere;
|
|
267
|
+
word-break: break-word;
|
|
268
|
+
}
|
|
269
|
+
|
|
254
270
|
/* Gap between items */
|
|
255
271
|
.gap {
|
|
256
272
|
gap: var(--spacing-xs);
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
.navItems {
|
|
138
138
|
display: flex;
|
|
139
139
|
align-items: center;
|
|
140
|
-
gap: var(--spacing-
|
|
140
|
+
gap: var(--spacing-sm);
|
|
141
141
|
min-inline-size: 0;
|
|
142
142
|
list-style: none;
|
|
143
143
|
margin: 0;
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
gap: var(--spacing-xs);
|
|
173
173
|
color: var(--color-fg-muted);
|
|
174
174
|
font-family: var(--font-family);
|
|
175
|
-
font-size: var(--font-size-
|
|
175
|
+
font-size: var(--font-size-md);
|
|
176
176
|
text-decoration: none;
|
|
177
177
|
border-radius: var(--border-radius-default);
|
|
178
178
|
padding-block: var(--spacing-xs);
|
|
@@ -2,35 +2,55 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var react = require('react');
|
|
5
|
+
var cookie_utils = require('../utils/cookie.utils');
|
|
5
6
|
var localStorage_utils = require('../utils/localStorage.utils');
|
|
6
7
|
|
|
8
|
+
function readStorage(storage, key) {
|
|
9
|
+
return storage === "cookie" ? cookie_utils.readCookie(key) : localStorage_utils.readLocalStorage(key);
|
|
10
|
+
}
|
|
11
|
+
function writeStorage(storage, key, value, maxAgeSeconds) {
|
|
12
|
+
if (storage === "cookie") {
|
|
13
|
+
cookie_utils.writeCookie(key, value, maxAgeSeconds);
|
|
14
|
+
} else {
|
|
15
|
+
localStorage_utils.writeLocalStorage(key, value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
7
18
|
function usePersistentState({
|
|
8
19
|
initialValue,
|
|
9
20
|
storageKey,
|
|
10
21
|
isControlled = false,
|
|
11
|
-
validate
|
|
22
|
+
validate,
|
|
23
|
+
storage = "localStorage",
|
|
24
|
+
hydration = "effect",
|
|
25
|
+
maxAgeSeconds
|
|
12
26
|
}) {
|
|
13
27
|
const shouldHydrate = !isControlled && Boolean(storageKey);
|
|
14
|
-
const
|
|
15
|
-
const [value, setValue] = react.useState(initialValue);
|
|
16
|
-
const hydrated = !shouldHydrate || didHydrate;
|
|
28
|
+
const sync = hydration === "sync";
|
|
17
29
|
const validateRef = react.useRef(validate);
|
|
30
|
+
const [didHydrate, setDidHydrate] = react.useState(() => !shouldHydrate || sync);
|
|
31
|
+
const [value, setValue] = react.useState(() => {
|
|
32
|
+
if (!shouldHydrate || !sync) return initialValue;
|
|
33
|
+
const stored = readStorage(storage, storageKey);
|
|
34
|
+
const currentValidate = validateRef.current;
|
|
35
|
+
return currentValidate == null || currentValidate(stored) ? stored : initialValue;
|
|
36
|
+
});
|
|
37
|
+
const hydrated = !shouldHydrate || didHydrate;
|
|
18
38
|
react.useEffect(() => {
|
|
19
39
|
validateRef.current = validate;
|
|
20
40
|
}, [validate]);
|
|
21
41
|
react.useEffect(() => {
|
|
22
|
-
if (!storageKey || !shouldHydrate) return;
|
|
23
|
-
const stored =
|
|
42
|
+
if (!storageKey || !shouldHydrate || sync) return;
|
|
43
|
+
const stored = readStorage(storage, storageKey);
|
|
24
44
|
const currentValidate = validateRef.current;
|
|
25
45
|
const nextValue = currentValidate == null || currentValidate(stored) ? stored : initialValue;
|
|
26
46
|
setValue(nextValue);
|
|
27
47
|
setDidHydrate(true);
|
|
28
|
-
}, [initialValue, shouldHydrate, storageKey]);
|
|
48
|
+
}, [initialValue, shouldHydrate, storageKey, storage, sync]);
|
|
29
49
|
react.useEffect(() => {
|
|
30
50
|
if (isControlled || !storageKey) return;
|
|
31
51
|
if (!hydrated) return;
|
|
32
|
-
|
|
33
|
-
}, [hydrated, isControlled, storageKey, value]);
|
|
52
|
+
writeStorage(storage, storageKey, value, maxAgeSeconds);
|
|
53
|
+
}, [hydrated, isControlled, storageKey, storage, maxAgeSeconds, value]);
|
|
34
54
|
return {
|
|
35
55
|
value,
|
|
36
56
|
setValue,
|
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
export type PersistentStateStorage = 'localStorage' | 'cookie';
|
|
3
|
+
/**
|
|
4
|
+
* 'effect' (default): the stored value is read in a useEffect after mount,
|
|
5
|
+
* so `value` starts as `initialValue` on every render — including the
|
|
6
|
+
* first client render of a component that was server-rendered — and only
|
|
7
|
+
* updates once the effect runs. This is what makes it hydration-safe.
|
|
8
|
+
*
|
|
9
|
+
* 'sync': the stored value is read synchronously during the initial
|
|
10
|
+
* render, so there's no post-mount update and no flash of `initialValue`.
|
|
11
|
+
* Only use this for components that are never part of an SSR/hydration
|
|
12
|
+
* pass (e.g. mounted purely client-side, behind a data-loading gate) —
|
|
13
|
+
* otherwise the server and the first client render will disagree and
|
|
14
|
+
* React will emit a hydration mismatch and force a visible patch-up,
|
|
15
|
+
* which is the exact flash this mode is meant to avoid.
|
|
16
|
+
*/
|
|
17
|
+
export type PersistentStateHydration = 'effect' | 'sync';
|
|
2
18
|
export interface UsePersistentStateOptions<T> {
|
|
3
19
|
initialValue: T;
|
|
4
20
|
storageKey?: string;
|
|
5
21
|
isControlled?: boolean;
|
|
6
22
|
validate?: (value: unknown) => value is T;
|
|
23
|
+
storage?: PersistentStateStorage;
|
|
24
|
+
hydration?: PersistentStateHydration;
|
|
25
|
+
/** Only used when storage is 'cookie'. Defaults to one year. */
|
|
26
|
+
maxAgeSeconds?: number;
|
|
7
27
|
}
|
|
8
28
|
export interface UsePersistentStateResult<T> {
|
|
9
29
|
value: T;
|
|
10
30
|
setValue: Dispatch<SetStateAction<T>>;
|
|
11
31
|
hydrated: boolean;
|
|
12
32
|
}
|
|
13
|
-
export declare function usePersistentState<T>({ initialValue, storageKey, isControlled, validate, }: UsePersistentStateOptions<T>): UsePersistentStateResult<T>;
|
|
33
|
+
export declare function usePersistentState<T>({ initialValue, storageKey, isControlled, validate, storage, hydration, maxAgeSeconds, }: UsePersistentStateOptions<T>): UsePersistentStateResult<T>;
|
|
@@ -1,34 +1,54 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { useRef, useState, useEffect } from 'react';
|
|
3
|
+
import { readCookie, writeCookie } from '../utils/cookie.utils';
|
|
3
4
|
import { readLocalStorage, writeLocalStorage } from '../utils/localStorage.utils';
|
|
4
5
|
|
|
6
|
+
function readStorage(storage, key) {
|
|
7
|
+
return storage === "cookie" ? readCookie(key) : readLocalStorage(key);
|
|
8
|
+
}
|
|
9
|
+
function writeStorage(storage, key, value, maxAgeSeconds) {
|
|
10
|
+
if (storage === "cookie") {
|
|
11
|
+
writeCookie(key, value, maxAgeSeconds);
|
|
12
|
+
} else {
|
|
13
|
+
writeLocalStorage(key, value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
5
16
|
function usePersistentState({
|
|
6
17
|
initialValue,
|
|
7
18
|
storageKey,
|
|
8
19
|
isControlled = false,
|
|
9
|
-
validate
|
|
20
|
+
validate,
|
|
21
|
+
storage = "localStorage",
|
|
22
|
+
hydration = "effect",
|
|
23
|
+
maxAgeSeconds
|
|
10
24
|
}) {
|
|
11
25
|
const shouldHydrate = !isControlled && Boolean(storageKey);
|
|
12
|
-
const
|
|
13
|
-
const [value, setValue] = useState(initialValue);
|
|
14
|
-
const hydrated = !shouldHydrate || didHydrate;
|
|
26
|
+
const sync = hydration === "sync";
|
|
15
27
|
const validateRef = useRef(validate);
|
|
28
|
+
const [didHydrate, setDidHydrate] = useState(() => !shouldHydrate || sync);
|
|
29
|
+
const [value, setValue] = useState(() => {
|
|
30
|
+
if (!shouldHydrate || !sync) return initialValue;
|
|
31
|
+
const stored = readStorage(storage, storageKey);
|
|
32
|
+
const currentValidate = validateRef.current;
|
|
33
|
+
return currentValidate == null || currentValidate(stored) ? stored : initialValue;
|
|
34
|
+
});
|
|
35
|
+
const hydrated = !shouldHydrate || didHydrate;
|
|
16
36
|
useEffect(() => {
|
|
17
37
|
validateRef.current = validate;
|
|
18
38
|
}, [validate]);
|
|
19
39
|
useEffect(() => {
|
|
20
|
-
if (!storageKey || !shouldHydrate) return;
|
|
21
|
-
const stored =
|
|
40
|
+
if (!storageKey || !shouldHydrate || sync) return;
|
|
41
|
+
const stored = readStorage(storage, storageKey);
|
|
22
42
|
const currentValidate = validateRef.current;
|
|
23
43
|
const nextValue = currentValidate == null || currentValidate(stored) ? stored : initialValue;
|
|
24
44
|
setValue(nextValue);
|
|
25
45
|
setDidHydrate(true);
|
|
26
|
-
}, [initialValue, shouldHydrate, storageKey]);
|
|
46
|
+
}, [initialValue, shouldHydrate, storageKey, storage, sync]);
|
|
27
47
|
useEffect(() => {
|
|
28
48
|
if (isControlled || !storageKey) return;
|
|
29
49
|
if (!hydrated) return;
|
|
30
|
-
|
|
31
|
-
}, [hydrated, isControlled, storageKey, value]);
|
|
50
|
+
writeStorage(storage, storageKey, value, maxAgeSeconds);
|
|
51
|
+
}, [hydrated, isControlled, storageKey, storage, maxAgeSeconds, value]);
|
|
32
52
|
return {
|
|
33
53
|
value,
|
|
34
54
|
setValue,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;
|
|
4
|
+
function isBrowser() {
|
|
5
|
+
return typeof document !== "undefined";
|
|
6
|
+
}
|
|
7
|
+
function readCookie(name) {
|
|
8
|
+
if (!isBrowser()) return void 0;
|
|
9
|
+
try {
|
|
10
|
+
const match = document.cookie.split("; ").find((row) => row.startsWith(`${name}=`));
|
|
11
|
+
if (match == null) return void 0;
|
|
12
|
+
const raw = decodeURIComponent(match.slice(name.length + 1));
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
} catch {
|
|
16
|
+
return raw;
|
|
17
|
+
}
|
|
18
|
+
} catch {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeCookie(name, value, maxAgeSeconds = ONE_YEAR_SECONDS) {
|
|
23
|
+
if (!isBrowser()) return;
|
|
24
|
+
try {
|
|
25
|
+
if (value === void 0) {
|
|
26
|
+
removeCookie(name);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const raw = typeof value === "string" ? value : JSON.stringify(value);
|
|
30
|
+
document.cookie = `${name}=${encodeURIComponent(raw)}; path=/; max-age=${maxAgeSeconds}`;
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function removeCookie(name) {
|
|
35
|
+
if (!isBrowser()) return;
|
|
36
|
+
try {
|
|
37
|
+
document.cookie = `${name}=; path=/; max-age=0`;
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
exports.readCookie = readCookie;
|
|
43
|
+
exports.removeCookie = removeCookie;
|
|
44
|
+
exports.writeCookie = writeCookie;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safely read from a cookie.
|
|
3
|
+
* - Returns undefined if not in browser or the cookie is missing
|
|
4
|
+
* - Returns parsed JSON if the value is JSON
|
|
5
|
+
* - Returns plain string if not JSON
|
|
6
|
+
* - Never throws
|
|
7
|
+
*/
|
|
8
|
+
export declare function readCookie<T = unknown>(name: string): T | string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Safely write a cookie.
|
|
11
|
+
* - Automatically JSON.stringifies objects/arrays/booleans/numbers
|
|
12
|
+
* - Stores plain strings as-is
|
|
13
|
+
* - Never throws
|
|
14
|
+
*/
|
|
15
|
+
export declare function writeCookie(name: string, value: unknown, maxAgeSeconds?: number): void;
|
|
16
|
+
/**
|
|
17
|
+
* Remove a cookie safely
|
|
18
|
+
*/
|
|
19
|
+
export declare function removeCookie(name: string): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;
|
|
2
|
+
function isBrowser() {
|
|
3
|
+
return typeof document !== "undefined";
|
|
4
|
+
}
|
|
5
|
+
function readCookie(name) {
|
|
6
|
+
if (!isBrowser()) return void 0;
|
|
7
|
+
try {
|
|
8
|
+
const match = document.cookie.split("; ").find((row) => row.startsWith(`${name}=`));
|
|
9
|
+
if (match == null) return void 0;
|
|
10
|
+
const raw = decodeURIComponent(match.slice(name.length + 1));
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(raw);
|
|
13
|
+
} catch {
|
|
14
|
+
return raw;
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function writeCookie(name, value, maxAgeSeconds = ONE_YEAR_SECONDS) {
|
|
21
|
+
if (!isBrowser()) return;
|
|
22
|
+
try {
|
|
23
|
+
if (value === void 0) {
|
|
24
|
+
removeCookie(name);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const raw = typeof value === "string" ? value : JSON.stringify(value);
|
|
28
|
+
document.cookie = `${name}=${encodeURIComponent(raw)}; path=/; max-age=${maxAgeSeconds}`;
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function removeCookie(name) {
|
|
33
|
+
if (!isBrowser()) return;
|
|
34
|
+
try {
|
|
35
|
+
document.cookie = `${name}=; path=/; max-age=0`;
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { readCookie, removeCookie, writeCookie };
|