@lerianstudio/sindarian-ui 1.2.0-beta.14 → 1.2.0-beta.15
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/form/radio-group-field/index.d.ts +1 -1
- package/dist/components/form/radio-group-field/index.d.ts.map +1 -1
- package/dist/components/form/radio-group-field/index.js +8 -1
- package/dist/components/ui/alert-dialog/index.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog/index.js +1 -1
- package/dist/components/ui/copy-field/index.d.ts.map +1 -1
- package/dist/components/ui/copy-field/index.js +6 -1
- package/dist/components/ui/file-upload/index.d.ts.map +1 -1
- package/dist/components/ui/file-upload/index.js +8 -1
- package/dist/domain/delinquency-aging/aging-buckets.d.ts +22 -2
- package/dist/domain/delinquency-aging/aging-buckets.d.ts.map +1 -1
- package/dist/domain/delinquency-aging/aging-buckets.js +17 -4
- package/dist/domain/delinquency-aging/index.d.ts +17 -2
- package/dist/domain/delinquency-aging/index.d.ts.map +1 -1
- package/dist/domain/delinquency-aging/index.js +4 -2
- package/dist/domain/index.d.ts +2 -1
- package/dist/domain/index.d.ts.map +1 -1
- package/dist/domain/money-diff.d.ts +9 -4
- package/dist/domain/money-diff.d.ts.map +1 -1
- package/dist/domain/section-label/index.d.ts +3 -3
- package/dist/domain/section-label/index.d.ts.map +1 -1
- package/dist/domain/section-label/index.js +6 -2
- package/dist/domain/status-rail/index.d.ts.map +1 -1
- package/dist/domain/status-rail/index.js +9 -1
- package/dist/domain/threshold-gauge/index.d.ts.map +1 -1
- package/dist/domain/threshold-gauge/index.js +1 -1
- package/dist/enterprise/date-range-picker/index.d.ts.map +1 -1
- package/dist/enterprise/date-range-picker/index.js +28 -10
- package/dist/enterprise/search-input/index.d.ts.map +1 -1
- package/dist/enterprise/search-input/index.js +13 -6
- package/dist/enterprise/use-is-mobile.d.ts +13 -4
- package/dist/enterprise/use-is-mobile.d.ts.map +1 -1
- package/dist/enterprise/use-is-mobile.js +15 -6
- package/dist/esm/components/form/radio-group-field/index.d.ts +1 -1
- package/dist/esm/components/form/radio-group-field/index.d.ts.map +1 -1
- package/dist/esm/components/form/radio-group-field/index.js +8 -1
- package/dist/esm/components/ui/alert-dialog/index.d.ts.map +1 -1
- package/dist/esm/components/ui/alert-dialog/index.js +1 -1
- package/dist/esm/components/ui/copy-field/index.d.ts.map +1 -1
- package/dist/esm/components/ui/copy-field/index.js +6 -1
- package/dist/esm/components/ui/file-upload/index.d.ts.map +1 -1
- package/dist/esm/components/ui/file-upload/index.js +8 -1
- package/dist/esm/domain/delinquency-aging/aging-buckets.d.ts +22 -2
- package/dist/esm/domain/delinquency-aging/aging-buckets.d.ts.map +1 -1
- package/dist/esm/domain/delinquency-aging/aging-buckets.js +17 -4
- package/dist/esm/domain/delinquency-aging/index.d.ts +17 -2
- package/dist/esm/domain/delinquency-aging/index.d.ts.map +1 -1
- package/dist/esm/domain/delinquency-aging/index.js +4 -2
- package/dist/esm/domain/index.d.ts +2 -1
- package/dist/esm/domain/index.d.ts.map +1 -1
- package/dist/esm/domain/money-diff.d.ts +9 -4
- package/dist/esm/domain/money-diff.d.ts.map +1 -1
- package/dist/esm/domain/section-label/index.d.ts +3 -3
- package/dist/esm/domain/section-label/index.d.ts.map +1 -1
- package/dist/esm/domain/section-label/index.js +6 -2
- package/dist/esm/domain/status-rail/index.d.ts.map +1 -1
- package/dist/esm/domain/status-rail/index.js +9 -1
- package/dist/esm/domain/threshold-gauge/index.d.ts.map +1 -1
- package/dist/esm/domain/threshold-gauge/index.js +1 -1
- package/dist/esm/enterprise/date-range-picker/index.d.ts.map +1 -1
- package/dist/esm/enterprise/date-range-picker/index.js +29 -11
- package/dist/esm/enterprise/search-input/index.d.ts.map +1 -1
- package/dist/esm/enterprise/search-input/index.js +13 -6
- package/dist/esm/enterprise/use-is-mobile.d.ts +13 -4
- package/dist/esm/enterprise/use-is-mobile.d.ts.map +1 -1
- package/dist/esm/enterprise/use-is-mobile.js +13 -5
- package/dist/esm/theme/theme-provider.d.ts.map +1 -1
- package/dist/esm/theme/theme-provider.js +15 -1
- package/dist/theme/theme-provider.d.ts.map +1 -1
- package/dist/theme/theme-provider.js +15 -1
- package/package.json +1 -1
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
|
-
* DateRangePicker — a
|
|
4
|
+
* DateRangePicker — a two-segment popover range control.
|
|
5
5
|
*
|
|
6
|
-
* Two labelled mono trigger segments (from / to)
|
|
7
|
-
* react-day-picker range calendar
|
|
8
|
-
* strings (empty string = unset) so state shapes
|
|
9
|
-
* `date_to` stay untouched. The picker guarantees
|
|
10
|
-
* reorders inverted picks);
|
|
11
|
-
* external validation wiring
|
|
6
|
+
* Two labelled mono trigger segments (from / to) each open the SAME two-month
|
|
7
|
+
* react-day-picker range calendar over the same range selection. The external
|
|
8
|
+
* contract is plain YYYY-MM-DD strings (empty string = unset) so state shapes
|
|
9
|
+
* that speak `date_from`/`date_to` stay untouched. The picker guarantees
|
|
10
|
+
* from <= to (react-day-picker reorders inverted picks);
|
|
11
|
+
* aria-invalid/aria-describedby pass-throughs keep external validation wiring
|
|
12
|
+
* alive. i18n is via plain string/locale props.
|
|
13
|
+
*
|
|
14
|
+
* ONE POPOVER ROOT PER SEGMENT, deliberately. A single `Popover.Root` holds ONE
|
|
15
|
+
* trigger ref and ONE open state, so wrapping the whole two-button row in a
|
|
16
|
+
* single `PopoverTrigger asChild` put Radix's dialog attributes
|
|
17
|
+
* (`aria-haspopup`, `aria-expanded`, `aria-controls`) on a plain, non-focusable
|
|
18
|
+
* `div` — an aria-allowed-attr violation, two buttons advertising the same
|
|
19
|
+
* expanded state, and focus that Radix could drop on `document.body` when the
|
|
20
|
+
* popover closed. Each segment is now a real `PopoverTrigger` button: focusable,
|
|
21
|
+
* correctly named, and the element Radix returns focus to on dismissal.
|
|
12
22
|
*/
|
|
13
23
|
import { useState } from 'react';
|
|
14
24
|
import { CalendarDays } from 'lucide-react';
|
|
@@ -51,7 +61,11 @@ function toDayString(value) {
|
|
|
51
61
|
return `${year}-${month}-${day}`;
|
|
52
62
|
}
|
|
53
63
|
export function DateRangePicker({ value, onValueChange, fromId, toId, fromLabel, toLabel, invalid = false, errorId, placeholder = 'Any date', ariaLabel = 'Select date range', clearLabel = 'Clear', locale = enUS }) {
|
|
54
|
-
|
|
64
|
+
// Which segment's popover is open, if any. One root per segment means one open
|
|
65
|
+
// state per segment; holding the identity of the open one here keeps them
|
|
66
|
+
// mutually exclusive, so clicking "to" while "from" is open swaps the anchor
|
|
67
|
+
// instead of stacking two calendars.
|
|
68
|
+
const [openSegment, setOpenSegment] = useState(null);
|
|
55
69
|
const fromDate = parseDay(value.from);
|
|
56
70
|
const toDate = parseDay(value.to);
|
|
57
71
|
const selected = fromDate || toDate ? { from: fromDate, to: toDate } : undefined;
|
|
@@ -66,6 +80,10 @@ export function DateRangePicker({ value, onValueChange, fromId, toId, fromLabel,
|
|
|
66
80
|
});
|
|
67
81
|
};
|
|
68
82
|
const triggerClass = cn('border-input bg-card hover:bg-secondary focus-visible:ring-ring flex h-9 items-center gap-2 rounded-md border px-3 text-left font-mono text-sm tabular-nums transition-colors duration-150 ease-out focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:outline-none sm:w-40', invalid && 'border-destructive');
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
// The one calendar, rendered inside whichever segment's popover is open. Both
|
|
84
|
+
// segments read and write the SAME range selection, so which one you opened
|
|
85
|
+
// changes only the anchor, never the state.
|
|
86
|
+
const calendar = (_jsxs(_Fragment, { children: [_jsx(Calendar, { mode: "range", numberOfMonths: 2, defaultMonth: fromDate ?? toDate ?? new Date(), selected: selected, onSelect: onSelect, locale: locale }), _jsx("div", { className: "border-border flex items-center justify-end border-t px-3 py-2", children: _jsx(Button, { type: "button", variant: "plain", size: "small", disabled: !hasValue, onClick: () => onValueChange({ from: '', to: '' }), children: clearLabel }) })] }));
|
|
87
|
+
const renderSegment = (segment, id, label, dateValue) => (_jsxs(Popover, { open: openSegment === segment, onOpenChange: (next) => setOpenSegment(next ? segment : null), children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: id, className: SECTION_LABEL_CLASS, children: label }), _jsxs(PopoverTrigger, { type: "button", id: id, className: triggerClass, "aria-invalid": invalid || undefined, "aria-describedby": invalid ? errorId : undefined, children: [_jsx(CalendarDays, { className: "text-muted-foreground size-4 shrink-0", "aria-hidden": true }), dateValue ? (_jsx("span", { children: dateValue })) : (_jsx("span", { className: "text-muted-foreground font-sans", children: placeholder }))] })] }), _jsx(PopoverContent, { align: "start", className: "w-auto p-0", "aria-label": ariaLabel, children: calendar })] }, segment));
|
|
88
|
+
return (_jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end", children: [renderSegment('from', fromId, fromLabel, value.from), renderSegment('to', toId, toLabel, value.to)] }));
|
|
71
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/enterprise/search-input/index.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,aAAa,EACb,UAAgB,EAChB,WAAuB,EACvB,YAAY,EAAE,SAAS,EACvB,UAA2B,EAC3B,QAAgB,EAChB,SAAS,EACT,EAAE,EACH,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/enterprise/search-input/index.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAa9B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,aAAa,EACb,UAAgB,EAChB,WAAuB,EACvB,YAAY,EAAE,SAAS,EACvB,UAA2B,EAC3B,QAAgB,EAChB,SAAS,EACT,EAAE,EACH,EAAE,gBAAgB,qBAsGlB"}
|
|
@@ -36,13 +36,20 @@ export function SearchInput({ value, onValueChange, debounceMs = 250, placeholde
|
|
|
36
36
|
// initial value is never mistaken for an echo of something we sent.
|
|
37
37
|
const lastEmitted = React.useRef(NO_PENDING_ECHO);
|
|
38
38
|
React.useEffect(() => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const isEcho = lastEmitted.current === value;
|
|
40
|
+
// ANY external change consumes the token, echo or not. Consuming it only on
|
|
41
|
+
// a match left a DROPPED emission pending forever: emit "pix", the parent
|
|
42
|
+
// rejects it and holds "", the parent later sets "foo" (draft follows, token
|
|
43
|
+
// still holding "pix"), the parent then sets "pix" — read as our own echo,
|
|
44
|
+
// so the draft stayed on "foo" and the visible field disagreed with the
|
|
45
|
+
// controlled value. A later external change is proof the parent moved on,
|
|
46
|
+
// which invalidates any echo still outstanding.
|
|
47
|
+
lastEmitted.current = NO_PENDING_ECHO;
|
|
48
|
+
// A genuine echo must NOT reach the draft: the parent echoes one render
|
|
49
|
+
// later, by which time the user may have typed further, and syncing would
|
|
50
|
+
// rewind the field and swallow those keystrokes.
|
|
51
|
+
if (isEcho)
|
|
44
52
|
return;
|
|
45
|
-
}
|
|
46
53
|
setDraft(value);
|
|
47
54
|
}, [value]);
|
|
48
55
|
const clearTimer = React.useCallback(() => {
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
/** The one breakpoint the hook subscribes at. Exported so its tests can bind to
|
|
2
|
+
* it instead of re-stating the number. */
|
|
3
|
+
export declare const MOBILE_BREAKPOINT = 768;
|
|
1
4
|
/**
|
|
2
5
|
* True while the viewport is narrower than the 768px mobile breakpoint.
|
|
3
6
|
*
|
|
4
7
|
* Always false on the FIRST render, server and client alike, so a narrow
|
|
5
|
-
* viewport can never produce a hydration mismatch
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* viewport can never produce a hydration mismatch. The correction lands in a
|
|
9
|
+
* `useEffect`, which runs AFTER the browser paints: on a narrow viewport the
|
|
10
|
+
* first painted frame therefore shows the desktop branch and the mobile branch
|
|
11
|
+
* appears on the next frame. That one frame is the deliberate price of
|
|
12
|
+
* hydration safety — a layout effect would remove it but would also change the
|
|
13
|
+
* render timing every existing consumer is built on.
|
|
14
|
+
*
|
|
15
|
+
* Every reading comes from the media query itself rather than a mix of
|
|
16
|
+
* `matchMedia` and `window.innerWidth`, so the initial value and the change
|
|
17
|
+
* events can never disagree at the breakpoint boundary.
|
|
9
18
|
*/
|
|
10
19
|
export declare function useIsMobile(): boolean;
|
|
11
20
|
//# sourceMappingURL=use-is-mobile.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-is-mobile.d.ts","sourceRoot":"","sources":["../../../src/enterprise/use-is-mobile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-is-mobile.d.ts","sourceRoot":"","sources":["../../../src/enterprise/use-is-mobile.ts"],"names":[],"mappings":"AAIA;2CAC2C;AAC3C,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAEpC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,YAY1B"}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
|
|
3
|
+
/** The one breakpoint the hook subscribes at. Exported so its tests can bind to
|
|
4
|
+
* it instead of re-stating the number. */
|
|
5
|
+
export const MOBILE_BREAKPOINT = 768;
|
|
4
6
|
/**
|
|
5
7
|
* True while the viewport is narrower than the 768px mobile breakpoint.
|
|
6
8
|
*
|
|
7
9
|
* Always false on the FIRST render, server and client alike, so a narrow
|
|
8
|
-
* viewport can never produce a hydration mismatch
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* viewport can never produce a hydration mismatch. The correction lands in a
|
|
11
|
+
* `useEffect`, which runs AFTER the browser paints: on a narrow viewport the
|
|
12
|
+
* first painted frame therefore shows the desktop branch and the mobile branch
|
|
13
|
+
* appears on the next frame. That one frame is the deliberate price of
|
|
14
|
+
* hydration safety — a layout effect would remove it but would also change the
|
|
15
|
+
* render timing every existing consumer is built on.
|
|
16
|
+
*
|
|
17
|
+
* Every reading comes from the media query itself rather than a mix of
|
|
18
|
+
* `matchMedia` and `window.innerWidth`, so the initial value and the change
|
|
19
|
+
* events can never disagree at the breakpoint boundary.
|
|
12
20
|
*/
|
|
13
21
|
export function useIsMobile() {
|
|
14
22
|
const [isMobile, setIsMobile] = React.useState(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../../src/theme/theme-provider.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAgBzD,KAAK,iBAAiB,GAAG;IACvB,oEAAoE;IACpE,KAAK,EAAE,eAAe,CAAA;IACtB,yEAAyE;IACzE,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC,mDAAmD;IACnD,aAAa,EAAE,OAAO,GAAG,MAAM,CAAA;CAChC,CAAA;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,YAAY,CAAC,EAAE,eAAe,CAAA;CAC/B;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,UAA8B,EAC9B,YAAuB,EACxB,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../../src/theme/theme-provider.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAgBzD,KAAK,iBAAiB,GAAG;IACvB,oEAAoE;IACpE,KAAK,EAAE,eAAe,CAAA;IACtB,yEAAyE;IACzE,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC,mDAAmD;IACnD,aAAa,EAAE,OAAO,GAAG,MAAM,CAAA;CAChC,CAAA;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,YAAY,CAAC,EAAE,eAAe,CAAA;CAC/B;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,UAA8B,EAC9B,YAAuB,EACxB,EAAE,kBAAkB,qBAwFpB;AAED,uEAAuE;AACvE,wBAAgB,QAAQ,IAAI,iBAAiB,CAM5C"}
|
|
@@ -78,7 +78,21 @@ export function ThemeProvider({ children, storageKey = 'sindarian.theme', defaul
|
|
|
78
78
|
if (typeof window === 'undefined')
|
|
79
79
|
return;
|
|
80
80
|
const onStorage = (event) => {
|
|
81
|
-
|
|
81
|
+
// WHICH STORE first, then which key. Only the store the preference lives
|
|
82
|
+
// in may change it: a sessionStorage event carrying the SAME key would
|
|
83
|
+
// otherwise mutate a localStorage-backed preference. `storageArea` is
|
|
84
|
+
// absent only on a hand-dispatched synthetic event — apps use those for
|
|
85
|
+
// same-tab sync — and one of those is not attributable to any store, so it
|
|
86
|
+
// is allowed through rather than silently dropped.
|
|
87
|
+
if (event.storageArea != null &&
|
|
88
|
+
event.storageArea !== window.localStorage)
|
|
89
|
+
return;
|
|
90
|
+
// A null key means another tab called `clear()` on the whole store, so the
|
|
91
|
+
// stored preference is gone even though it is not named. Returning early
|
|
92
|
+
// on that left the provider holding a deleted preference — and the wrong
|
|
93
|
+
// `dark` class on the document. Fall through with newValue null, which
|
|
94
|
+
// lands on the fallback.
|
|
95
|
+
if (event.key !== null && event.key !== storageKey)
|
|
82
96
|
return;
|
|
83
97
|
setThemeState(isThemePreference(event.newValue) ? event.newValue : defaultTheme);
|
|
84
98
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/theme/theme-provider.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAgBzD,KAAK,iBAAiB,GAAG;IACvB,oEAAoE;IACpE,KAAK,EAAE,eAAe,CAAA;IACtB,yEAAyE;IACzE,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC,mDAAmD;IACnD,aAAa,EAAE,OAAO,GAAG,MAAM,CAAA;CAChC,CAAA;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,YAAY,CAAC,EAAE,eAAe,CAAA;CAC/B;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,UAA8B,EAC9B,YAAuB,EACxB,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/theme/theme-provider.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAgBzD,KAAK,iBAAiB,GAAG;IACvB,oEAAoE;IACpE,KAAK,EAAE,eAAe,CAAA;IACtB,yEAAyE;IACzE,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC,mDAAmD;IACnD,aAAa,EAAE,OAAO,GAAG,MAAM,CAAA;CAChC,CAAA;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,YAAY,CAAC,EAAE,eAAe,CAAA;CAC/B;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,UAA8B,EAC9B,YAAuB,EACxB,EAAE,kBAAkB,qBAwFpB;AAED,uEAAuE;AACvE,wBAAgB,QAAQ,IAAI,iBAAiB,CAM5C"}
|
|
@@ -115,7 +115,21 @@ function ThemeProvider({ children, storageKey = 'sindarian.theme', defaultTheme
|
|
|
115
115
|
if (typeof window === 'undefined')
|
|
116
116
|
return;
|
|
117
117
|
const onStorage = (event) => {
|
|
118
|
-
|
|
118
|
+
// WHICH STORE first, then which key. Only the store the preference lives
|
|
119
|
+
// in may change it: a sessionStorage event carrying the SAME key would
|
|
120
|
+
// otherwise mutate a localStorage-backed preference. `storageArea` is
|
|
121
|
+
// absent only on a hand-dispatched synthetic event — apps use those for
|
|
122
|
+
// same-tab sync — and one of those is not attributable to any store, so it
|
|
123
|
+
// is allowed through rather than silently dropped.
|
|
124
|
+
if (event.storageArea != null &&
|
|
125
|
+
event.storageArea !== window.localStorage)
|
|
126
|
+
return;
|
|
127
|
+
// A null key means another tab called `clear()` on the whole store, so the
|
|
128
|
+
// stored preference is gone even though it is not named. Returning early
|
|
129
|
+
// on that left the provider holding a deleted preference — and the wrong
|
|
130
|
+
// `dark` class on the document. Fall through with newValue null, which
|
|
131
|
+
// lands on the fallback.
|
|
132
|
+
if (event.key !== null && event.key !== storageKey)
|
|
119
133
|
return;
|
|
120
134
|
setThemeState(isThemePreference(event.newValue) ? event.newValue : defaultTheme);
|
|
121
135
|
};
|