cursedbelt 5.94.0 → 5.95.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.
- package/dist/react/components/AppShell.d.ts +12 -1
- package/dist/react/components/AppShell.d.ts.map +1 -1
- package/dist/react/components/AppShell.js +24 -5
- package/dist/react/components/AppShell.js.map +1 -1
- package/dist/react/components/ComboboxField.js +2 -2
- package/dist/react/components/ComboboxField.js.map +1 -1
- package/dist/react/components/DatePicker.d.ts +9 -5
- package/dist/react/components/DatePicker.d.ts.map +1 -1
- package/dist/react/components/DatePicker.js +192 -27
- package/dist/react/components/DatePicker.js.map +1 -1
- package/dist/react/components/InlineEditableText.d.ts +12 -2
- package/dist/react/components/InlineEditableText.d.ts.map +1 -1
- package/dist/react/components/InlineEditableText.js +11 -8
- package/dist/react/components/InlineEditableText.js.map +1 -1
- package/dist/react/components/suggestionList.js +2 -2
- package/dist/react/components/suggestionList.js.map +1 -1
- package/dist/react/lib/dateText.d.ts +21 -0
- package/dist/react/lib/dateText.d.ts.map +1 -0
- package/dist/react/lib/dateText.js +94 -0
- package/dist/react/lib/dateText.js.map +1 -0
- package/dist/react/lib/floatingStyles.d.ts +12 -0
- package/dist/react/lib/floatingStyles.d.ts.map +1 -1
- package/dist/react/lib/floatingStyles.js +12 -0
- package/dist/react/lib/floatingStyles.js.map +1 -1
- package/dist/styles-areas/analytics.css +11 -10
- package/dist/styles-areas/data-table.css +13 -12
- package/dist/styles-areas/fields.css +12 -10
- package/dist/styles-areas/stats.css +12 -11
- package/dist/styles-areas/wizard.css +6 -6
- package/dist/styles-utilities.css +15 -0
- package/package.json +1 -1
- package/src/react/components/AppShell.spec.tsx +53 -0
- package/src/react/components/AppShell.tsx +79 -23
- package/src/react/components/ComboboxField.tsx +2 -2
- package/src/react/components/ControlHeightScale.spec.tsx +2 -2
- package/src/react/components/DatePicker.spec.tsx +186 -5
- package/src/react/components/DatePicker.tsx +416 -63
- package/src/react/components/InlineEditableText.spec.tsx +29 -0
- package/src/react/components/InlineEditableText.tsx +26 -10
- package/src/react/components/suggestionList.tsx +2 -2
- package/src/react/lib/dateText.ts +94 -0
- package/src/react/lib/floatingStyles.spec.ts +36 -1
- package/src/react/lib/floatingStyles.ts +13 -0
- package/src/styles-areas/analytics.css +11 -10
- package/src/styles-areas/data-table.css +13 -12
- package/src/styles-areas/fields.css +12 -10
- package/src/styles-areas/stats.css +12 -11
- package/src/styles-areas/wizard.css +6 -6
- package/src/styles-utilities.css +15 -0
|
@@ -14,9 +14,9 @@ import { RAW_BUTTON } from '../lib/cn.js';
|
|
|
14
14
|
* primitive, used anywhere a title/heading should be edited in place. Fully
|
|
15
15
|
* token-driven (accent border + ring on the editor); the editor focuses and
|
|
16
16
|
* selects its text on mount so typing replaces (and the cursor is never stranded
|
|
17
|
-
* at position 0).
|
|
17
|
+
* at position 0) — or, with `caret='end'`, puts the caret after the text.
|
|
18
18
|
*/
|
|
19
|
-
export function InlineEditableText({ value, onChange, canEdit = true, multiline = false, placeholder = 'Add text…', className, inputClassName, trigger = 'text', wrap = true, ariaLabel, ...rest }) {
|
|
19
|
+
export function InlineEditableText({ value, onChange, canEdit = true, multiline = false, placeholder = 'Add text…', className, inputClassName, trigger = 'text', wrap = true, caret = 'select-all', maxLength = 10_000, ariaLabel, ...rest }) {
|
|
20
20
|
const [editing, setEditing] = useState(false);
|
|
21
21
|
const [draft, setDraft] = useState(value);
|
|
22
22
|
const begin = () => {
|
|
@@ -35,20 +35,23 @@ export function InlineEditableText({ value, onChange, canEdit = true, multiline
|
|
|
35
35
|
setDraft(value);
|
|
36
36
|
setEditing(false);
|
|
37
37
|
};
|
|
38
|
-
// Focus + select-all as soon as the editor mounts (it only renders while
|
|
39
|
-
// MUST be identity-stable: an inline ref callback gets detached/re-attached on every
|
|
38
|
+
// Focus + select-all (or caret-at-end) as soon as the editor mounts (it only renders while
|
|
39
|
+
// editing). MUST be identity-stable: an inline ref callback gets detached/re-attached on every
|
|
40
40
|
// render, so each keystroke's setDraft re-render would re-run el.select() and the next
|
|
41
41
|
// character would replace everything typed so far (one-character-survives bug).
|
|
42
42
|
const focusOnMount = useCallback((el) => {
|
|
43
43
|
if (!el)
|
|
44
44
|
return;
|
|
45
45
|
el.focus();
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
if (caret === 'end')
|
|
47
|
+
el.setSelectionRange(el.value.length, el.value.length);
|
|
48
|
+
else
|
|
49
|
+
el.select();
|
|
50
|
+
}, [caret]);
|
|
48
51
|
if (editing) {
|
|
49
52
|
const editorClass = cn('block w-full rounded-sm border border-accent bg-surface px-1 -mx-1 text-foreground outline-none ring-2 ring-accent/30', className, inputClassName);
|
|
50
53
|
if (multiline) {
|
|
51
|
-
return (_jsx(AutoGrowTextarea, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: cn(editorClass, 'resize-none'), maxLength:
|
|
54
|
+
return (_jsx(AutoGrowTextarea, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: cn(editorClass, 'resize-none'), maxLength: maxLength, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
|
|
52
55
|
if (e.key === 'Escape') {
|
|
53
56
|
// Escape cancels THIS edit only — never bubbles on to close a
|
|
54
57
|
// surrounding Modal/Drawer in the same keystroke.
|
|
@@ -59,7 +62,7 @@ export function InlineEditableText({ value, onChange, canEdit = true, multiline
|
|
|
59
62
|
commit();
|
|
60
63
|
} }));
|
|
61
64
|
}
|
|
62
|
-
return (_jsx(Input, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: editorClass, maxLength:
|
|
65
|
+
return (_jsx(Input, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: editorClass, maxLength: maxLength, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
|
|
63
66
|
if (e.key === 'Escape') {
|
|
64
67
|
// Escape cancels THIS edit only — never bubbles on to close a
|
|
65
68
|
// surrounding Modal/Drawer in the same keystroke.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InlineEditableText.js","sourceRoot":"","sources":["../../../src/react/components/InlineEditableText.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"InlineEditableText.js","sourceRoot":"","sources":["../../../src/react/components/InlineEditableText.tsx"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { Autocomplete } from '@base-ui/react/autocomplete';
|
|
4
4
|
import { cn } from '../lib/cn.js';
|
|
5
|
-
import { FLOATING_COLLISION_PADDING, FLOATING_CONTENT_BASE, FLOATING_ITEM_BASE } from '../lib/floatingStyles.js';
|
|
5
|
+
import { BASE_UI_POSITIONER, FLOATING_COLLISION_PADDING, FLOATING_CONTENT_BASE, FLOATING_ITEM_BASE } from '../lib/floatingStyles.js';
|
|
6
6
|
/**
|
|
7
7
|
* The dropdown of an Autocomplete root — every kit field that suggests as you type draws its list
|
|
8
8
|
* with this one, so the list is the kit's in every theme (`TagsInput` as well as
|
|
@@ -12,7 +12,7 @@ import { FLOATING_COLLISION_PADDING, FLOATING_CONTENT_BASE, FLOATING_ITEM_BASE }
|
|
|
12
12
|
*/
|
|
13
13
|
// no-attribute-forward: its root is Base UI's Portal — no element of its own; the popup is the kit's, not the caller's
|
|
14
14
|
export function SuggestionList({ itemToString = (item) => String(item), renderSuggestion, }) {
|
|
15
|
-
return (_jsx(Autocomplete.Portal, { children: _jsx(Autocomplete.Positioner, { sideOffset: 4, collisionPadding: FLOATING_COLLISION_PADDING, className:
|
|
15
|
+
return (_jsx(Autocomplete.Portal, { children: _jsx(Autocomplete.Positioner, { sideOffset: 4, collisionPadding: FLOATING_COLLISION_PADDING, className: BASE_UI_POSITIONER, children: _jsx(Autocomplete.Popup, { className: cn(FLOATING_CONTENT_BASE,
|
|
16
16
|
// guardrails-ignore no-raw-overflow: floating popover panel outside the page's ScrollArea, height-capped by max-h-64 — no keyboard-inset concern.
|
|
17
17
|
'max-h-64 min-w-[var(--anchor-width)] overflow-y-auto p-0 py-1', 'transition-[transform,opacity] duration-100', 'data-[starting-style]:scale-95 data-[starting-style]:opacity-0', 'data-[ending-style]:scale-95 data-[ending-style]:opacity-0'), children: _jsx(Autocomplete.List, { children: (s, index) => (_jsx(Autocomplete.Item, { value: s, index: index, className: cn(FLOATING_ITEM_BASE, 'data-[highlighted]:bg-accent/10 data-[highlighted]:text-accent-strong'), children: renderSuggestion ? renderSuggestion(s) : itemToString(s) }, itemToString(s))) }) }) }) }));
|
|
18
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suggestionList.js","sourceRoot":"","sources":["../../../src/react/components/suggestionList.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,0BAA0B,
|
|
1
|
+
{"version":3,"file":"suggestionList.js","sourceRoot":"","sources":["../../../src/react/components/suggestionList.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EACrD,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9E;;;;;;GAMG;AACH,uHAAuH;AACvH,MAAM,UAAU,cAAc,CAAI,EAChC,YAAY,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EACrC,gBAAgB,GAIjB;IACC,OAAO,CACL,KAAC,YAAY,CAAC,MAAM,cAOlB,KAAC,YAAY,CAAC,UAAU,IACtB,UAAU,EAAE,CAAC,EACb,gBAAgB,EAAE,0BAA0B,EAC5C,SAAS,EAAE,kBAAkB,YAE7B,KAAC,YAAY,CAAC,KAAK,IACjB,SAAS,EAAE,EAAE,CACX,qBAAqB;gBACrB,kJAAkJ;gBAClJ,+DAA+D,EAC/D,6CAA6C,EAC7C,gEAAgE,EAChE,4DAA4D,CAC7D,YAKD,KAAC,YAAY,CAAC,IAAI,cACf,CAAC,CAAI,EAAE,KAAa,EAAE,EAAE,CAAC,CACxB,KAAC,YAAY,CAAC,IAAI,IAEhB,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,EAAE,CAAC,kBAAkB,EAAE,uEAAuE,CAAC,YAEzG,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IALpD,YAAY,CAAC,CAAC,CAAC,CAMF,CACrB,GACiB,GACD,GACG,GACN,CACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed-date parsing for {@link DatePicker} (forge task 3764).
|
|
3
|
+
*
|
|
4
|
+
* Accepts what a person actually types, American English first:
|
|
5
|
+
* `2015-12-05`, `12/5/2015` (month first), `12-5-2015`, `12.5.2015`, `Dec 5 2015`,
|
|
6
|
+
* `December 5, 2015`, `5 Dec 2015`, `Dec 5th, 2015`, `2015 Dec 5`. A two-digit year is
|
|
7
|
+
* read as the nearest sensible century: `15` is 2015, and `99` is 1999 (a year more than
|
|
8
|
+
* ten years ahead of today reads as the century before). Anything else — a day that does
|
|
9
|
+
* not exist, a three-digit year, stray words — is refused with a short reason, never
|
|
10
|
+
* guessed at.
|
|
11
|
+
*/
|
|
12
|
+
export type DateTextResult = {
|
|
13
|
+
ok: true;
|
|
14
|
+
iso: string;
|
|
15
|
+
} | {
|
|
16
|
+
ok: false;
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
/** Parse typed text into an ISO `YYYY-MM-DD` day. Pure; `now` only picks the century of a 2-digit year. */
|
|
20
|
+
export declare function parseDateText(text: string, now?: Date): DateTextResult;
|
|
21
|
+
//# sourceMappingURL=dateText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dateText.d.ts","sourceRoot":"","sources":["../../../src/react/lib/dateText.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAkDvF,2GAA2G;AAC3G,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,cAAc,CA8BlF"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed-date parsing for {@link DatePicker} (forge task 3764).
|
|
3
|
+
*
|
|
4
|
+
* Accepts what a person actually types, American English first:
|
|
5
|
+
* `2015-12-05`, `12/5/2015` (month first), `12-5-2015`, `12.5.2015`, `Dec 5 2015`,
|
|
6
|
+
* `December 5, 2015`, `5 Dec 2015`, `Dec 5th, 2015`, `2015 Dec 5`. A two-digit year is
|
|
7
|
+
* read as the nearest sensible century: `15` is 2015, and `99` is 1999 (a year more than
|
|
8
|
+
* ten years ahead of today reads as the century before). Anything else — a day that does
|
|
9
|
+
* not exist, a three-digit year, stray words — is refused with a short reason, never
|
|
10
|
+
* guessed at.
|
|
11
|
+
*/
|
|
12
|
+
const MONTHS = [
|
|
13
|
+
'january',
|
|
14
|
+
'february',
|
|
15
|
+
'march',
|
|
16
|
+
'april',
|
|
17
|
+
'may',
|
|
18
|
+
'june',
|
|
19
|
+
'july',
|
|
20
|
+
'august',
|
|
21
|
+
'september',
|
|
22
|
+
'october',
|
|
23
|
+
'november',
|
|
24
|
+
'december',
|
|
25
|
+
];
|
|
26
|
+
/** A month index (0-11) for a name or a 3+ letter prefix of one ("sept", "Dec"), else -1. */
|
|
27
|
+
function monthIndex(word) {
|
|
28
|
+
const w = word.toLowerCase().replace(/\.$/, '');
|
|
29
|
+
if (w.length < 3)
|
|
30
|
+
return -1;
|
|
31
|
+
return MONTHS.findIndex((name) => name.startsWith(w));
|
|
32
|
+
}
|
|
33
|
+
function expandYear(token, now) {
|
|
34
|
+
if (/^\d{4}$/.test(token)) {
|
|
35
|
+
const year = Number(token);
|
|
36
|
+
return year >= 1000 ? year : null;
|
|
37
|
+
}
|
|
38
|
+
if (/^\d{2}$/.test(token)) {
|
|
39
|
+
const year = 2000 + Number(token);
|
|
40
|
+
return year > now.getFullYear() + 10 ? year - 100 : year;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function build(year, month, day) {
|
|
45
|
+
if (year === null)
|
|
46
|
+
return { ok: false, reason: 'Use a four-digit year' };
|
|
47
|
+
if (month < 0 || month > 11)
|
|
48
|
+
return { ok: false, reason: 'That month does not exist' };
|
|
49
|
+
const date = new Date(year, month, day);
|
|
50
|
+
if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
|
|
51
|
+
return { ok: false, reason: 'That date does not exist' };
|
|
52
|
+
}
|
|
53
|
+
const mm = String(month + 1).padStart(2, '0');
|
|
54
|
+
const dd = String(day).padStart(2, '0');
|
|
55
|
+
return { ok: true, iso: `${String(year).padStart(4, '0')}-${mm}-${dd}` };
|
|
56
|
+
}
|
|
57
|
+
const INVALID = { ok: false, reason: 'Enter a date like Dec 5, 2015 or 12/5/2015' };
|
|
58
|
+
/** Parse typed text into an ISO `YYYY-MM-DD` day. Pure; `now` only picks the century of a 2-digit year. */
|
|
59
|
+
export function parseDateText(text, now = new Date()) {
|
|
60
|
+
const raw = text.trim();
|
|
61
|
+
if (!raw)
|
|
62
|
+
return INVALID;
|
|
63
|
+
// Numeric forms: Y-M-D (ISO first), then M/D/Y with / - or . between.
|
|
64
|
+
const numeric = raw.match(/^(\d{1,4})([/.-])(\d{1,2})\2(\d{1,4})$/);
|
|
65
|
+
if (numeric) {
|
|
66
|
+
const [, a, , b, c] = numeric;
|
|
67
|
+
if (a.length === 4)
|
|
68
|
+
return build(expandYear(a, now), Number(b) - 1, Number(c));
|
|
69
|
+
return build(expandYear(c, now), Number(a) - 1, Number(b));
|
|
70
|
+
}
|
|
71
|
+
// Month-name forms, in any order of day / month / year.
|
|
72
|
+
const tokens = raw
|
|
73
|
+
.replace(/,/g, ' ')
|
|
74
|
+
.split(/\s+/)
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.filter((t) => t.toLowerCase() !== 'of');
|
|
77
|
+
if (tokens.length !== 3)
|
|
78
|
+
return INVALID;
|
|
79
|
+
const nameAt = tokens.findIndex((t) => monthIndex(t) >= 0);
|
|
80
|
+
if (nameAt < 0)
|
|
81
|
+
return INVALID;
|
|
82
|
+
const month = monthIndex(tokens[nameAt]);
|
|
83
|
+
const nums = tokens
|
|
84
|
+
.filter((_, i) => i !== nameAt)
|
|
85
|
+
.map((t) => t.replace(/^(\d{1,2})(st|nd|rd|th)$/i, '$1'));
|
|
86
|
+
if (!nums.every((t) => /^\d+$/.test(t)))
|
|
87
|
+
return INVALID;
|
|
88
|
+
const [first, second] = nums;
|
|
89
|
+
// "2015 Dec 5": a four-digit first number is the year. Otherwise day then year.
|
|
90
|
+
if (first.length === 4)
|
|
91
|
+
return build(expandYear(first, now), month, Number(second));
|
|
92
|
+
return build(expandYear(second, now), month, Number(first));
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=dateText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dateText.js","sourceRoot":"","sources":["../../../src/react/lib/dateText.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,18 @@
|
|
|
23
23
|
* so it reads as a floating surface rather than as a pane that ran out of screen.
|
|
24
24
|
*/
|
|
25
25
|
export declare const FLOATING_COLLISION_PADDING = 16;
|
|
26
|
+
/**
|
|
27
|
+
* The class every Base UI `*.Positioner` carries (ComboboxField's list, the Autocomplete
|
|
28
|
+
* suggestion list) — its stacking AND its pointer events, one string so the two cannot drift.
|
|
29
|
+
*
|
|
30
|
+
* 🔴 `pointer-events-auto`: Base UI portals the list into `<body>`, and an open Radix modal
|
|
31
|
+
* (a belt `Modal` or `Drawer`) sets `body { pointer-events: none }`, so a list opened inside a
|
|
32
|
+
* dialog painted over it and every tap fell through to the dialog (measured 2026-10-25, photos'
|
|
33
|
+
* place picker in the phone's Details drawer, forge task 3764). Radix still counts a press on the
|
|
34
|
+
* list as inside — its DismissableLayer tracks the React tree — so the dialog stays open.
|
|
35
|
+
* `floatingStyles.spec.ts` fails on a Positioner in this library that does not use it.
|
|
36
|
+
*/
|
|
37
|
+
export declare const BASE_UI_POSITIONER = "pointer-events-auto z-[var(--z-popover)]";
|
|
26
38
|
/**
|
|
27
39
|
* Base surface for any floating panel: overlay elevation, popover tokens, ring-free.
|
|
28
40
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floatingStyles.d.ts","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QAIY,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,QAOS,CAAC;AAEzC;;8BAE8B;AAC9B,eAAO,MAAM,kBAAkB,QAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"floatingStyles.d.ts","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAE7E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QAIY,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,QAOS,CAAC;AAEzC;;8BAE8B;AAC9B,eAAO,MAAM,kBAAkB,QAAkB,CAAC"}
|
|
@@ -25,6 +25,18 @@ import { TAP_TARGET_HEIGHT } from './tapTarget.js';
|
|
|
25
25
|
* so it reads as a floating surface rather than as a pane that ran out of screen.
|
|
26
26
|
*/
|
|
27
27
|
export const FLOATING_COLLISION_PADDING = 16;
|
|
28
|
+
/**
|
|
29
|
+
* The class every Base UI `*.Positioner` carries (ComboboxField's list, the Autocomplete
|
|
30
|
+
* suggestion list) — its stacking AND its pointer events, one string so the two cannot drift.
|
|
31
|
+
*
|
|
32
|
+
* 🔴 `pointer-events-auto`: Base UI portals the list into `<body>`, and an open Radix modal
|
|
33
|
+
* (a belt `Modal` or `Drawer`) sets `body { pointer-events: none }`, so a list opened inside a
|
|
34
|
+
* dialog painted over it and every tap fell through to the dialog (measured 2026-10-25, photos'
|
|
35
|
+
* place picker in the phone's Details drawer, forge task 3764). Radix still counts a press on the
|
|
36
|
+
* list as inside — its DismissableLayer tracks the React tree — so the dialog stays open.
|
|
37
|
+
* `floatingStyles.spec.ts` fails on a Positioner in this library that does not use it.
|
|
38
|
+
*/
|
|
39
|
+
export const BASE_UI_POSITIONER = 'pointer-events-auto z-[var(--z-popover)]';
|
|
28
40
|
/**
|
|
29
41
|
* Base surface for any floating panel: overlay elevation, popover tokens, ring-free.
|
|
30
42
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floatingStyles.js","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,0FAA0F;IAC1F,8CAA8C;IAC9C,iFAAiF;IACjF,4CAA4C,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC7B,0GAA0G;IAC1G,+EAA+E;IAC/E,gFAAgF;IAChF,6EAA6E;IAC7E,GAAG,iBAAiB,GAAG;IACvB,iEAAiE;IACjE,sCAAsC,CAAC;AAEzC;;8BAE8B;AAC9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"floatingStyles.js","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,0CAA0C,CAAC;AAE7E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,0FAA0F;IAC1F,8CAA8C;IAC9C,iFAAiF;IACjF,4CAA4C,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC7B,0GAA0G;IAC1G,+EAA+E;IAC/E,gFAAgF;IAChF,6EAA6E;IAC7E,GAAG,iBAAiB,GAAG;IACvB,iEAAiE;IACjE,sCAAsC,CAAC;AAEzC;;8BAE8B;AAC9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* after an app's responsive variant (task 2124). An app that imports every area gets exactly
|
|
19
19
|
* what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
|
|
20
20
|
* src/stylesAreas.spec.ts, which is also what fails when this file is stale.
|
|
21
|
-
*
|
|
21
|
+
* 150 candidate(s).
|
|
22
22
|
*
|
|
23
23
|
* Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
|
|
24
24
|
* `bun run build` runs. The map of what is in which area, and why the areas are
|
|
@@ -34,14 +34,15 @@
|
|
|
34
34
|
@source inline("[&>*]:hidden [&>*]:max-w-none [&>svg]:size-3.5 active:cursor-grabbing basis basis-full");
|
|
35
35
|
@source inline("bg-[var(--cb-frozen-bg)] bg-accent/15 bg-card/95 bg-gradient-to-t bg-muted/30 bg-muted/50");
|
|
36
36
|
@source inline("border-accent-solid border-accent/60 border-b-0! border-field-error border-solid border-t-2 col");
|
|
37
|
-
@source inline("collapse cursor-grab disabled:opacity-
|
|
38
|
-
@source inline("gap-x-6 h-0 h-105
|
|
39
|
-
@source inline("
|
|
40
|
-
@source inline("
|
|
37
|
+
@source inline("collapse cursor-grab disabled:opacity-30 disabled:opacity-40 fade-in");
|
|
38
|
+
@source inline("focus-visible:ring-field-ring/40 from-card gap-x-6 grid-cols-3 grid-cols-4 h-0 h-105 h-3.5");
|
|
39
|
+
@source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-background hover:bg-muted/40");
|
|
40
|
+
@source inline("hover:border-field-ring hover:text-warning italic justify-self-end last:border-0 left-11");
|
|
41
|
+
@source inline("left-2.5 list-none max-h-60 max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0");
|
|
41
42
|
@source inline("max-sm:min-w-24 max-sm:min-w-32 max-sm:ml-13.5 max-sm:w-auto max-w-32 max-w-36 max-w-45 max-w-48");
|
|
42
43
|
@source inline("max-w-60 min-h-20 min-h-dvh min-w-0! min-w-20 min-w-4 min-w-52 min-w-max mr-2 mt-1.5 mt-3 mt-4");
|
|
43
|
-
@source inline("not-italic ordinal p-0! pb-1.5 pl-1.5 pr-0.5 pr-10 pr-16 pr-3 pr-9
|
|
44
|
-
@source inline("resize-y right-3 ring-field-error/30 self self-stretch
|
|
45
|
-
@source inline("shrink-[2] size-7 slide-in-from-top-1 table-fixed
|
|
46
|
-
@source inline("text-
|
|
47
|
-
@source inline("w-36 w-40 w-88");
|
|
44
|
+
@source inline("not-italic ordinal p-0! pb-1.5 pl-1.5 pointer-coarse:pr-12 pr-0.5 pr-10 pr-11 pr-16 pr-3 pr-9");
|
|
45
|
+
@source inline("pt-1 py-0! resize-none resize-y right-3 ring-field-error/30 self self-stretch");
|
|
46
|
+
@source inline("shadow-[1px_0_0_0_var(--color-border)] shrink-[2] size-7 slide-in-from-top-1 table-fixed");
|
|
47
|
+
@source inline("text-[length:0] text-accent-solid-foreground text-field-placeholder text-success text-xl");
|
|
48
|
+
@source inline("to-transparent top-3 w-0 w-0! w-11 w-12 w-20 w-3.5 w-32 w-36 w-40 w-88");
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* after an app's responsive variant (task 2124). An app that imports every area gets exactly
|
|
19
19
|
* what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
|
|
20
20
|
* src/stylesAreas.spec.ts, which is also what fails when this file is stale.
|
|
21
|
-
*
|
|
21
|
+
* 145 candidate(s).
|
|
22
22
|
*
|
|
23
23
|
* Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
|
|
24
24
|
* `bun run build` runs. The map of what is in which area, and why the areas are
|
|
@@ -34,14 +34,15 @@
|
|
|
34
34
|
@source inline("[&>*]:hidden [&>*]:max-w-none [&>svg]:size-3.5 active:cursor-grabbing basis-full");
|
|
35
35
|
@source inline("bg-[var(--cb-frozen-bg)] bg-accent/15 bg-card/95 bg-gradient-to-t bg-muted/30 bg-muted/50");
|
|
36
36
|
@source inline("border-accent-solid border-accent/60 border-b-0! border-field-error border-solid border-t-2 col");
|
|
37
|
-
@source inline("collapse cursor-grab disabled:opacity-
|
|
38
|
-
@source inline("gap-x-6 h-0 h-105
|
|
39
|
-
@source inline("
|
|
40
|
-
@source inline("
|
|
41
|
-
@source inline("max-
|
|
42
|
-
@source inline("max-
|
|
43
|
-
@source inline("
|
|
44
|
-
@source inline("
|
|
45
|
-
@source inline("
|
|
46
|
-
@source inline("
|
|
47
|
-
@source inline("
|
|
37
|
+
@source inline("collapse cursor-grab disabled:opacity-30 disabled:opacity-40 fade-in");
|
|
38
|
+
@source inline("focus-visible:ring-field-ring/40 from-card gap-x-6 grid-cols-3 grid-cols-4 h-0 h-105 h-3.5");
|
|
39
|
+
@source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-background hover:bg-muted/40");
|
|
40
|
+
@source inline("hover:border-field-ring hover:text-warning justify-self-end last:border-0 left-11 left-2.5");
|
|
41
|
+
@source inline("list-none max-h-60 max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0 max-sm:min-w-24");
|
|
42
|
+
@source inline("max-sm:min-w-32 max-sm:ml-13.5 max-sm:w-auto max-w-32 max-w-36 max-w-45 max-w-48 max-w-60");
|
|
43
|
+
@source inline("min-h-20 min-h-dvh min-w-0! min-w-4 min-w-52 min-w-max mr-2 mt-1.5 mt-3 mt-4 not-italic ordinal");
|
|
44
|
+
@source inline("p-0! pb-1.5 pl-1.5 pointer-coarse:pr-12 pr-0.5 pr-10 pr-11 pr-16 pr-3 pr-9 pt-1 py-0!");
|
|
45
|
+
@source inline("resize-none resize-y right-3 ring-field-error/30 self self-stretch");
|
|
46
|
+
@source inline("shadow-[1px_0_0_0_var(--color-border)] size-7 slide-in-from-top-1 table-fixed text-[length:0]");
|
|
47
|
+
@source inline("text-accent-solid-foreground text-field-placeholder text-success text-xl to-transparent top-3");
|
|
48
|
+
@source inline("w-0! w-11 w-12 w-20 w-3.5 w-32 w-36 w-40 w-88");
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* after an app's responsive variant (task 2124). An app that imports every area gets exactly
|
|
19
19
|
* what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
|
|
20
20
|
* src/stylesAreas.spec.ts, which is also what fails when this file is stale.
|
|
21
|
-
*
|
|
21
|
+
* 94 candidate(s).
|
|
22
22
|
*
|
|
23
23
|
* Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
|
|
24
24
|
* `bun run build` runs. The map of what is in which area, and why the areas are
|
|
@@ -28,17 +28,19 @@
|
|
|
28
28
|
@source inline("[scrollbar-width:none] appearance-none aria-checked:ring-2 aria-checked:ring-foreground");
|
|
29
29
|
@source inline("aria-checked:ring-offset-1 bg-accent/15 bg-surface-sunken/80 bg-warning/10 border-border/40");
|
|
30
30
|
@source inline("border-field-error caret-transparent cursor-text data-[state=checked]:ring-foreground");
|
|
31
|
-
@source inline("data-[state=checked]:ring-offset-1 disabled:hover:scale-100 disabled:opacity-
|
|
32
|
-
@source inline("focus-visible:border-border-strong focus-visible:ring-0
|
|
33
|
-
@source inline("focus-
|
|
34
|
-
@source inline("
|
|
35
|
-
@source inline("
|
|
31
|
+
@source inline("data-[state=checked]:ring-offset-1 disabled:hover:scale-100 disabled:opacity-30");
|
|
32
|
+
@source inline("disabled:opacity-40 focus-visible:border-border-strong focus-visible:ring-0");
|
|
33
|
+
@source inline("focus-visible:ring-field-ring/40 focus-within:ring-offset-1 focus-within:ring-ring grid-cols-3");
|
|
34
|
+
@source inline("grid-cols-4 group-focus-within:opacity-100 h-105 h-12 h-3.5");
|
|
35
|
+
@source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-muted/50 hover:border-field-ring");
|
|
36
|
+
@source inline("hover:scale-110 hover:text-warning italic left-2.5 li list-none max-h-60 md:flex-col md:gap-0.5");
|
|
36
37
|
@source inline("md:justify-start md:overflow-visible md:text-left md:w-full min-h-20 min-w-64 min-w-full");
|
|
37
38
|
@source inline("min-w-max mix-blend-difference mr-2 pl-1.5 pl-2.5 pointer-coarse:before:-left-1");
|
|
38
39
|
@source inline("pointer-coarse:before:-right-1 pointer-coarse:before:absolute");
|
|
39
40
|
@source inline("pointer-coarse:before:bottom-[calc(var(--hit-slop-y)*-1)]");
|
|
40
|
-
@source inline("pointer-coarse:before:top-[calc(var(--hit-slop-y)*-1)] pointer-coarse:items-center
|
|
41
|
-
@source inline("pr-3 pr-9 pt-1 resize-none resize-y right-1.5 right-3
|
|
42
|
-
@source inline("self-stretch size-7 size-9 size-full sm:w-11 sm:w-56
|
|
43
|
-
@source inline("top-1.5 w-20 w-32 w-36 w-40 w-88 w-fit w-max
|
|
41
|
+
@source inline("pointer-coarse:before:top-[calc(var(--hit-slop-y)*-1)] pointer-coarse:items-center");
|
|
42
|
+
@source inline("pointer-coarse:pr-12 pr-11 pr-16 pr-2 pr-3 pr-9 pt-1 resize-none resize-y right-1.5 right-3");
|
|
43
|
+
@source inline("ring-accent/30 ring-field-error/30 self-stretch size-7 size-9 size-full sm:w-11 sm:w-56");
|
|
44
|
+
@source inline("text-field-placeholder text-success text-xl top-1.5 w-20 w-3.5 w-32 w-36 w-40 w-88 w-fit w-max");
|
|
45
|
+
@source inline("whitespace-pre");
|
|
44
46
|
@source inline('pointer-coarse:before:content-[""]');
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* after an app's responsive variant (task 2124). An app that imports every area gets exactly
|
|
19
19
|
* what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
|
|
20
20
|
* src/stylesAreas.spec.ts, which is also what fails when this file is stale.
|
|
21
|
-
*
|
|
21
|
+
* 94 candidate(s).
|
|
22
22
|
*
|
|
23
23
|
* Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
|
|
24
24
|
* `bun run build` runs. The map of what is in which area, and why the areas are
|
|
@@ -27,16 +27,17 @@
|
|
|
27
27
|
@source inline("[&>div]:sm:[grid-template-columns:…] [&_svg]:size-3.5 basis-40 bg-accent/15 bg-accent/40");
|
|
28
28
|
@source inline("bg-background/70 bg-success/10 bg-warning/10 bg-warning/20 border-field-error border-l-2");
|
|
29
29
|
@source inline("border-l-border-strong border-l-destructive border-l-warning bottom-3 col col-span-2 col-start-2");
|
|
30
|
-
@source inline("collapse cursor-help divide-x first:pl-1 focus-visible:ring-field-ring/40
|
|
31
|
-
@source inline("gap-y-0.5 grid-cols-[minmax(0,1fr)_auto]
|
|
32
|
-
@source inline("group-focus-visible/
|
|
33
|
-
@source inline("group-
|
|
34
|
-
@source inline("group/meter group/stat group/tile h-105
|
|
35
|
-
@source inline("hover:bg-muted/50 hover:border-field-ring
|
|
36
|
-
@source inline("left-[var(--timeline-rail-x)] list-none
|
|
37
|
-
@source inline("
|
|
38
|
-
@source inline("
|
|
30
|
+
@source inline("collapse cursor-help disabled:opacity-30 divide-x first:pl-1 focus-visible:ring-field-ring/40");
|
|
31
|
+
@source inline("gap-x-1.5 gap-x-2 gap-y-0.5 grid-cols-3 grid-cols-4 grid-cols-[minmax(0,1fr)_auto]");
|
|
32
|
+
@source inline("group-focus-visible/meter:opacity-100 group-focus-visible/stat:opacity-100");
|
|
33
|
+
@source inline("group-focus-visible/tile:opacity-100 group-hover/meter:opacity-100 group-hover/stat:opacity-100");
|
|
34
|
+
@source inline("group-hover/tile:opacity-100 group/meter group/stat group/tile h-105 h-3.5");
|
|
35
|
+
@source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-muted/50 hover:border-field-ring");
|
|
36
|
+
@source inline("hover:text-warning last:pr-1 left-3 left-[var(--timeline-rail-x)] list-none max-h-60 min-h-20");
|
|
37
|
+
@source inline("min-h-9 min-w-34 min-w-full mr-0.5 mr-1.5 mr-2 normal-case pl-1.5 pointer-coarse:pr-12 pr-11");
|
|
38
|
+
@source inline("pr-16 pr-3 pt-1 resize-none resize-y ring-field-error/30 rounded-b-md rounded-t-md row-start-1");
|
|
39
|
+
@source inline("self-stretch shadow-md size-7 sm:col-auto sm:col-span-1 sm:flex-nowrap");
|
|
39
40
|
@source inline("sm:grid-cols-[minmax(7rem,18rem)_minmax(4rem,1fr)_auto]");
|
|
40
41
|
@source inline("sm:grid-cols-[minmax(7rem,34rem)_minmax(4rem,1fr)_auto] sm:row-auto sm:truncate text-3xl");
|
|
41
42
|
@source inline("text-[length:var(--text-glyph)] text-field-placeholder text-foreground/80 text-success text-xl");
|
|
42
|
-
@source inline("top-7 tracking-normal w-36 w-48 w-88 whitespace-pre z-[1]");
|
|
43
|
+
@source inline("top-7 tracking-normal w-3.5 w-36 w-48 w-88 whitespace-pre z-[1]");
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
* after an app's responsive variant (task 2124). An app that imports every area gets exactly
|
|
19
19
|
* what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
|
|
20
20
|
* src/stylesAreas.spec.ts, which is also what fails when this file is stale.
|
|
21
|
-
*
|
|
21
|
+
* 33 candidate(s).
|
|
22
22
|
*
|
|
23
23
|
* Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
|
|
24
24
|
* `bun run build` runs. The map of what is in which area, and why the areas are
|
|
25
25
|
* import closures rather than a hand-written list, is scripts/styleAreas.ts.
|
|
26
26
|
*/
|
|
27
|
-
@source inline("border-field-error collapse disabled:bg-transparent disabled:opacity-100");
|
|
28
|
-
@source inline("focus-visible:ring-field-ring/40
|
|
29
|
-
@source inline("hover:border-field-ring hover:text-warning
|
|
30
|
-
@source inline("
|
|
31
|
-
@source inline("w-88");
|
|
27
|
+
@source inline("border-field-error collapse disabled:bg-transparent disabled:opacity-100 disabled:opacity-30");
|
|
28
|
+
@source inline("focus-visible:ring-field-ring/40 grid-cols-3 grid-cols-4 h-105 h-3.5");
|
|
29
|
+
@source inline("has-[[aria-invalid=true]]:border-field-error hover:border-field-ring hover:text-warning max-h-60");
|
|
30
|
+
@source inline("min-h-20 mr-1.5 mr-2 pl-1.5 pointer-coarse:pr-12 pr-11 pr-16 pr-3 resize-none resize-y");
|
|
31
|
+
@source inline("ring-field-error/30 self-stretch size-7 sm:inline text-field-placeholder text-xl w-3.5 w-36 w-88");
|
|
@@ -773,6 +773,9 @@
|
|
|
773
773
|
.max-h-56 {
|
|
774
774
|
max-height: calc(var(--spacing) * 56);
|
|
775
775
|
}
|
|
776
|
+
.max-h-60 {
|
|
777
|
+
max-height: calc(var(--spacing) * 60);
|
|
778
|
+
}
|
|
776
779
|
.max-h-64 {
|
|
777
780
|
max-height: calc(var(--spacing) * 64);
|
|
778
781
|
}
|
|
@@ -1371,6 +1374,9 @@
|
|
|
1371
1374
|
.grid-cols-2 {
|
|
1372
1375
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1373
1376
|
}
|
|
1377
|
+
.grid-cols-3 {
|
|
1378
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
1379
|
+
}
|
|
1374
1380
|
.grid-cols-4 {
|
|
1375
1381
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
1376
1382
|
}
|
|
@@ -2733,6 +2739,9 @@
|
|
|
2733
2739
|
.pr-10 {
|
|
2734
2740
|
padding-right: calc(var(--spacing) * 10);
|
|
2735
2741
|
}
|
|
2742
|
+
.pr-11 {
|
|
2743
|
+
padding-right: calc(var(--spacing) * 11);
|
|
2744
|
+
}
|
|
2736
2745
|
.pr-16 {
|
|
2737
2746
|
padding-right: calc(var(--spacing) * 16);
|
|
2738
2747
|
}
|
|
@@ -4183,6 +4192,9 @@
|
|
|
4183
4192
|
.disabled\:opacity-0:disabled {
|
|
4184
4193
|
opacity: 0%;
|
|
4185
4194
|
}
|
|
4195
|
+
.disabled\:opacity-30:disabled {
|
|
4196
|
+
opacity: 30%;
|
|
4197
|
+
}
|
|
4186
4198
|
.disabled\:opacity-40:disabled {
|
|
4187
4199
|
opacity: 40%;
|
|
4188
4200
|
}
|
|
@@ -5243,6 +5255,9 @@
|
|
|
5243
5255
|
.pointer-coarse\:p-0 {
|
|
5244
5256
|
padding: 0px;
|
|
5245
5257
|
}
|
|
5258
|
+
.pointer-coarse\:pr-12 {
|
|
5259
|
+
padding-right: calc(var(--spacing) * 12);
|
|
5260
|
+
}
|
|
5246
5261
|
.pointer-coarse\:opacity-100 {
|
|
5247
5262
|
opacity: 100%;
|
|
5248
5263
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.95.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
|
|
@@ -58,6 +58,59 @@ describe('AppShell', () => {
|
|
|
58
58
|
expect(q(container, '[data-cb-nav-backdrop]')).toBeNull();
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
test('🔴 navCollapsed folds the lg sidebar to the icon rail, and the fold button in the rail head asks for the next state (forge task 3773)', () => {
|
|
62
|
+
const changes: boolean[] = [];
|
|
63
|
+
const { container, rerender } = render(
|
|
64
|
+
<AppShell nav={station} navHeader={<span data-brand=''>Photos</span>} account={<span>CU</span>} onNavCollapsedChange={(next) => changes.push(next)}>
|
|
65
|
+
x
|
|
66
|
+
</AppShell>,
|
|
67
|
+
);
|
|
68
|
+
const [icon, full] = qa(container, '[data-cb-nav-rail]') as [HTMLElement, HTMLElement];
|
|
69
|
+
// Open: the icon rail hides at lg, the full sidebar shows there — as before.
|
|
70
|
+
expect(icon.className).toContain('lg:hidden');
|
|
71
|
+
expect(full.className).toContain('lg:flex');
|
|
72
|
+
// The fold button is in BOTH heads (the rail must be able to unfold itself), and says what it does.
|
|
73
|
+
const folds = qa(container, '[data-cb-rail-fold]');
|
|
74
|
+
expect(folds).toHaveLength(2);
|
|
75
|
+
expect(folds.map((b) => b.getAttribute('aria-label'))).toEqual(['Show the sidebar', 'Hide the sidebar']);
|
|
76
|
+
// With no ⌘K row it sits on the brand line; with one, beside the ⌘K trigger — never a third control
|
|
77
|
+
// beside a worded account launcher, which left the brand no width (photos, WebKit, 2026-10-25).
|
|
78
|
+
const { container: noCommand } = render(
|
|
79
|
+
<AppShell nav={station} commandPalette={false} navHeader={<span>Photos</span>} account={<span>CU</span>} onNavCollapsedChange={() => {}}>
|
|
80
|
+
x
|
|
81
|
+
</AppShell>,
|
|
82
|
+
);
|
|
83
|
+
const fullNoCommand = qa(noCommand, '[data-cb-nav-rail]')[1] as HTMLElement;
|
|
84
|
+
expect(q(fullNoCommand, '[data-cb-rail-command-row]')).toBeNull();
|
|
85
|
+
expect(q(fullNoCommand, '[data-cb-account]')?.parentElement?.querySelector('[data-cb-rail-fold]')).not.toBeNull();
|
|
86
|
+
const { container: withCommand } = render(
|
|
87
|
+
<AppShell nav={station} navHeader={<span>Photos</span>} account={<span>Curt Phillips ▾</span>} onOpenCommand={() => {}} onNavCollapsedChange={() => {}}>
|
|
88
|
+
x
|
|
89
|
+
</AppShell>,
|
|
90
|
+
);
|
|
91
|
+
const fullWithCommand = qa(withCommand, '[data-cb-nav-rail]')[1] as HTMLElement;
|
|
92
|
+
expect(q(fullWithCommand, '[data-cb-account]')?.parentElement?.querySelector('[data-cb-rail-fold]')).toBeNull();
|
|
93
|
+
expect(q(fullWithCommand, '[data-cb-rail-command-row] [data-cb-rail-fold]')).not.toBeNull();
|
|
94
|
+
expect(q(fullWithCommand, '[data-cb-rail-command-row] [data-cb-command-trigger]')).not.toBeNull();
|
|
95
|
+
fireEvent.click(folds[1] as HTMLElement);
|
|
96
|
+
expect(changes).toEqual([true]);
|
|
97
|
+
// Folded: the icon rail stays at lg and the full sidebar is gone from every width.
|
|
98
|
+
rerender(
|
|
99
|
+
<AppShell nav={station} navHeader={<span data-brand=''>Photos</span>} account={<span>CU</span>} navCollapsed onNavCollapsedChange={(next) => changes.push(next)}>
|
|
100
|
+
x
|
|
101
|
+
</AppShell>,
|
|
102
|
+
);
|
|
103
|
+
const [iconFolded, fullFolded] = qa(container, '[data-cb-nav-rail]') as [HTMLElement, HTMLElement];
|
|
104
|
+
expect(iconFolded.className).not.toContain('lg:hidden');
|
|
105
|
+
expect(fullFolded.className).not.toContain('lg:flex');
|
|
106
|
+
expect(fullFolded.className).toContain('hidden');
|
|
107
|
+
fireEvent.click(q(iconFolded, '[data-cb-rail-fold]') as HTMLElement);
|
|
108
|
+
expect(changes).toEqual([true, false]);
|
|
109
|
+
// Without the callback nothing is offered: a shell that cannot remember the choice does not ask for it.
|
|
110
|
+
rerender(<AppShell nav={station}>x</AppShell>);
|
|
111
|
+
expect(qa(container, '[data-cb-rail-fold]')).toHaveLength(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
61
114
|
test('🔴 NAV-15 · with a sidebar the header is phone-only, and the brand, account and ⌘K head the sidebar (owner, 2026-10-20)', () => {
|
|
62
115
|
const { container } = render(
|
|
63
116
|
<AppShell
|