cursedbelt 5.8.0 → 5.10.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/filter-rail/FilterRail.d.ts.map +1 -1
- package/dist/react/filter-rail/FilterRail.js +5 -52
- package/dist/react/filter-rail/FilterRail.js.map +1 -1
- package/dist/react/filter-rail/index.d.ts +1 -0
- package/dist/react/filter-rail/index.d.ts.map +1 -1
- package/dist/react/filter-rail/index.js +1 -0
- package/dist/react/filter-rail/index.js.map +1 -1
- package/dist/react/filter-rail/railResize.d.ts +63 -0
- package/dist/react/filter-rail/railResize.d.ts.map +1 -0
- package/dist/react/filter-rail/railResize.js +79 -0
- package/dist/react/filter-rail/railResize.js.map +1 -0
- package/dist/react/nav/TopBar.d.ts +65 -0
- package/dist/react/nav/TopBar.d.ts.map +1 -0
- package/dist/react/nav/TopBar.js +40 -0
- package/dist/react/nav/TopBar.js.map +1 -0
- package/dist/react/nav/index.d.ts +1 -0
- package/dist/react/nav/index.d.ts.map +1 -1
- package/dist/react/nav/index.js +1 -0
- package/dist/react/nav/index.js.map +1 -1
- package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
- package/dist/styles-areas/nav.css +10 -8
- package/dist/styles-utilities.css +23 -0
- package/package.json +3 -3
- package/scripts/guardrailsEnforce.spec.ts +24 -0
- package/scripts/guardrailsEnforce.ts +53 -0
- package/src/react/filter-rail/FilterRail.tsx +10 -78
- package/src/react/filter-rail/index.ts +1 -0
- package/src/react/filter-rail/railResize.spec.tsx +92 -0
- package/src/react/filter-rail/railResize.tsx +156 -0
- package/src/react/nav/TopBar.spec.tsx +77 -0
- package/src/react/nav/TopBar.tsx +153 -0
- package/src/react/nav/index.ts +1 -0
- package/src/styles-areas/nav.css +10 -8
- package/src/styles-utilities.css +23 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterRail.d.ts","sourceRoot":"","sources":["../../../src/react/filter-rail/FilterRail.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"FilterRail.d.ts","sourceRoot":"","sources":["../../../src/react/filter-rail/FilterRail.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronsLeft, ChevronsRight } from 'lucide-react';
|
|
3
|
-
import { useCallback, useRef, useState } from 'react';
|
|
4
3
|
import { ScrollArea } from '../components/ScrollArea.js';
|
|
5
4
|
import { cn } from '../lib/cn.js';
|
|
6
5
|
import { Tooltip } from '../components/Tooltip.js';
|
|
7
6
|
import { TAP_TARGET, TAP_TARGET_ALWAYS, TAP_TARGET_HEIGHT } from '../lib/tapTarget.js';
|
|
8
|
-
import {
|
|
7
|
+
import { RailResizeHandle, useRailResize } from './railResize.js';
|
|
9
8
|
/**
|
|
10
9
|
* The rail's body — one long scroller, or a flex column that hands its leftover height to a
|
|
11
10
|
* section that asked for it.
|
|
@@ -65,56 +64,10 @@ function RailBody({ filling, children }) {
|
|
|
65
64
|
* {@link FilterRailSection.selected}.
|
|
66
65
|
*/
|
|
67
66
|
export function FilterRail({ sections, selected, onSelect, label, collapsed = false, onCollapsedChange, header, footer, width = 'w-56', resizeKey, className, }) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
if (!resizeKey || event.button !== 0)
|
|
73
|
-
return;
|
|
74
|
-
const handle = event.currentTarget;
|
|
75
|
-
const startX = event.clientX;
|
|
76
|
-
const startWidth = navRef.current?.getBoundingClientRect().width ?? MIN_RAIL_PX;
|
|
77
|
-
let latest = startWidth;
|
|
78
|
-
handle.setPointerCapture?.(event.pointerId);
|
|
79
|
-
const move = (moveEvent) => {
|
|
80
|
-
latest = clampRailWidth(startWidth + moveEvent.clientX - startX);
|
|
81
|
-
setDragged(latest);
|
|
82
|
-
};
|
|
83
|
-
const end = () => {
|
|
84
|
-
handle.releasePointerCapture?.(event.pointerId);
|
|
85
|
-
handle.removeEventListener('pointermove', move);
|
|
86
|
-
handle.removeEventListener('pointerup', end);
|
|
87
|
-
handle.removeEventListener('pointercancel', end);
|
|
88
|
-
// Remembered on RELEASE — a drag is one decision, not sixty a second.
|
|
89
|
-
writeRailWidth(resizeKey, latest);
|
|
90
|
-
};
|
|
91
|
-
handle.addEventListener('pointermove', move);
|
|
92
|
-
handle.addEventListener('pointerup', end);
|
|
93
|
-
handle.addEventListener('pointercancel', end);
|
|
94
|
-
event.preventDefault();
|
|
95
|
-
}, [resizeKey]);
|
|
96
|
-
const onResizeKey = useCallback((event) => {
|
|
97
|
-
if (!resizeKey)
|
|
98
|
-
return;
|
|
99
|
-
const step = event.shiftKey ? 64 : 16;
|
|
100
|
-
const delta = event.key === 'ArrowRight' ? step : event.key === 'ArrowLeft' ? -step : 0;
|
|
101
|
-
if (!delta)
|
|
102
|
-
return;
|
|
103
|
-
event.preventDefault();
|
|
104
|
-
const next = clampRailWidth((dragged ?? navRef.current?.getBoundingClientRect().width ?? MIN_RAIL_PX) + delta);
|
|
105
|
-
setDragged(next);
|
|
106
|
-
writeRailWidth(resizeKey, next);
|
|
107
|
-
}, [resizeKey, dragged]);
|
|
108
|
-
return (_jsxs("nav", { ref: navRef, "aria-label": label, "data-cb-filter-rail": collapsed ? 'collapsed' : 'expanded', className: cn('flex h-full min-h-0 shrink-0 flex-col border-r border-border bg-surface', collapsed ? 'w-14' : dragged !== null && resizing ? undefined : width, resizing && 'relative', className), ...(resizing && dragged !== null ? { style: { width: dragged } } : {}), children: [resizing ? (_jsx("div", { role: 'separator', "aria-orientation": 'vertical', "aria-label": `Resize ${label}`, "aria-valuemin": MIN_RAIL_PX, "aria-valuemax": MAX_RAIL_PX, "aria-valuenow": Math.round(dragged ?? navRef.current?.getBoundingClientRect().width ?? MIN_RAIL_PX), tabIndex: 0, "data-cb-rail-resize": '', onPointerDown: onResizePointerDown, onKeyDown: onResizeKey, onDoubleClick: () => {
|
|
109
|
-
setDragged(null);
|
|
110
|
-
if (resizeKey)
|
|
111
|
-
writeRailWidth(resizeKey, null);
|
|
112
|
-
},
|
|
113
|
-
// A 1px edge you cannot hit is not a handle: the hotspot is 12px, centred on the
|
|
114
|
-
// border, and only the hairline tints — no layout shift, like `PanelSeparator`.
|
|
115
|
-
// Only utilities the stylesheet already ships, so the handle adds no CSS to any app
|
|
116
|
-
// (`fixtureAppCss.spec.ts` budgets that).
|
|
117
|
-
className: 'absolute inset-y-0 -right-1.5 z-20 w-3 cursor-col-resize touch-none bg-transparent transition-colors hover:bg-accent/20 active:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring' })) : null, onCollapsedChange ? (_jsxs("div", { className: cn('flex flex-none items-center px-2 pt-2', collapsed && 'justify-center'), children: [!collapsed && _jsx("span", { className: 'flex-1' }), _jsx(Tooltip, { content: collapsed ? `Expand ${label}` : `Collapse ${label}`, children: _jsx("button", { type: 'button',
|
|
67
|
+
// The drag, the keys, the clamp, the storage and the reset are `railResize.tsx`, shared with
|
|
68
|
+
// every sidebar that is not a FilterRail (music's LibraryRail).
|
|
69
|
+
const rail = useRailResize({ resizeKey, label, enabled: !collapsed });
|
|
70
|
+
return (_jsxs("nav", { ref: rail.railRef, "aria-label": label, "data-cb-filter-rail": collapsed ? 'collapsed' : 'expanded', className: cn('flex h-full min-h-0 shrink-0 flex-col border-r border-border bg-surface', collapsed ? 'w-14' : rail.width !== null ? undefined : width, rail.active && 'relative', className), ...(rail.style ? { style: rail.style } : {}), children: [_jsx(RailResizeHandle, { rail: rail }), onCollapsedChange ? (_jsxs("div", { className: cn('flex flex-none items-center px-2 pt-2', collapsed && 'justify-center'), children: [!collapsed && _jsx("span", { className: 'flex-1' }), _jsx(Tooltip, { content: collapsed ? `Expand ${label}` : `Collapse ${label}`, children: _jsx("button", { type: 'button',
|
|
118
71
|
// guardrails-ignore no-raw-action-button: the rail's own chrome control — an
|
|
119
72
|
// IconButton here would pull the Button/Tooltip graph into every rail consumer.
|
|
120
73
|
onClick: () => onCollapsedChange(!collapsed), "aria-label": collapsed ? `Expand ${label}` : `Collapse ${label}`, "aria-expanded": !collapsed, className: `grid size-8 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-card-raised hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${TAP_TARGET} [&_svg]:size-4`, children: collapsed ? _jsx(ChevronsRight, {}) : _jsx(ChevronsLeft, {}) }) })] })) : null, header && !collapsed ? _jsx("div", { className: 'flex-none px-2 pt-2', children: header }) : null, _jsx(RailBody, { filling: sections.some((section) => section.fill === true), children: sections.map((section) => (_jsxs("div", { className: cn('mb-3 last:mb-0',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterRail.js","sourceRoot":"","sources":["../../../src/react/filter-rail/FilterRail.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"FilterRail.js","sourceRoot":"","sources":["../../../src/react/filter-rail/FilterRail.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/filter-rail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/filter-rail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/filter-rail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,GAIX,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/filter-rail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,GAIX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAA8C,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drag a sidebar's right edge to resize it, like VS Code's side bar — the gesture half, for
|
|
3
|
+
* any rail, not only `FilterRail`.
|
|
4
|
+
*
|
|
5
|
+
* Owner, 2026-09-24: *"the cb filetree sidebars should allow grabbing their right edge and
|
|
6
|
+
* dragging to resize them like in vscode."* 5.8.0 shipped the handle INSIDE `FilterRail`; the
|
|
7
|
+
* sidebars in music (`LibraryRail`) and others are their own columns around a `FileTree`, so
|
|
8
|
+
* the handle came out here once rather than being copied into each app. `FilterRail` uses
|
|
9
|
+
* exactly this, so there is one gesture to get right:
|
|
10
|
+
*
|
|
11
|
+
* - **pointer capture**, or the drag ends the first time the pointer outruns the strip;
|
|
12
|
+
* - **←/→** (Shift for bigger steps), because a drag-only control is one some people cannot
|
|
13
|
+
* reach;
|
|
14
|
+
* - **a `separator` role with real `aria-value*`**, the ARIA splitter pattern;
|
|
15
|
+
* - **double-click resets** to the rail's own default width, and forgets the stored one;
|
|
16
|
+
* - remembered on RELEASE — a drag is one decision, not sixty writes a second.
|
|
17
|
+
*
|
|
18
|
+
* Clamp and storage are `railWidth.ts`, so the width is announced through `preferenceSync`
|
|
19
|
+
* like every other viewer preference.
|
|
20
|
+
*/
|
|
21
|
+
import type { CSSProperties, KeyboardEvent as ReactKeyboardEvent, PointerEvent as ReactPointerEvent, RefObject } from 'react';
|
|
22
|
+
export interface UseRailResizeOptions {
|
|
23
|
+
/** Where the width is remembered. Absent: not resizable — no handle, no stored width. */
|
|
24
|
+
resizeKey: string | undefined;
|
|
25
|
+
/** The rail's accessible name; the handle announces as "Resize <label>". */
|
|
26
|
+
label: string;
|
|
27
|
+
/** False hides the handle and drops the dragged width (a collapsed rail). Default true. */
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface RailResize<E extends HTMLElement = HTMLElement> {
|
|
31
|
+
/** Put on the rail element — the handle measures it to start a drag. */
|
|
32
|
+
railRef: RefObject<E | null>;
|
|
33
|
+
/** Whether a handle is drawn: a `resizeKey` was given and `enabled` is true. */
|
|
34
|
+
active: boolean;
|
|
35
|
+
/** The dragged width in px, or `null` for "the rail's own default width". Null while inactive. */
|
|
36
|
+
width: number | null;
|
|
37
|
+
/** `{ width }` while there is a dragged width, else `undefined` — spread onto the rail's `style`. */
|
|
38
|
+
style: CSSProperties | undefined;
|
|
39
|
+
/** Forget the dragged width (what a double-click on the handle does). */
|
|
40
|
+
reset: () => void;
|
|
41
|
+
/** @internal — read by `RailResizeHandle`. */
|
|
42
|
+
readonly handle: {
|
|
43
|
+
label: string;
|
|
44
|
+
onPointerDown: (event: ReactPointerEvent<HTMLElement>) => void;
|
|
45
|
+
onKeyDown: (event: ReactKeyboardEvent<HTMLElement>) => void;
|
|
46
|
+
/** The rail's current rendered width, for `aria-valuenow` before any drag. */
|
|
47
|
+
measure: () => number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export declare function useRailResize<E extends HTMLElement = HTMLElement>({ resizeKey, label, enabled, }: UseRailResizeOptions): RailResize<E>;
|
|
51
|
+
/**
|
|
52
|
+
* The handle itself — a 12px hotspot centred on the rail's right border. The RAIL must be
|
|
53
|
+
* `position: relative` (the handle is absolutely positioned against it). Renders nothing while
|
|
54
|
+
* the resize is inactive.
|
|
55
|
+
*
|
|
56
|
+
* Only utilities the stylesheet already ships, so it adds no CSS to any app that imports the
|
|
57
|
+
* `filter-rail` area (`fixtureAppCss.spec.ts` budgets that).
|
|
58
|
+
*/
|
|
59
|
+
export declare function RailResizeHandle({ rail, className }: {
|
|
60
|
+
rail: RailResize;
|
|
61
|
+
className?: string;
|
|
62
|
+
}): import("react").JSX.Element | null;
|
|
63
|
+
//# sourceMappingURL=railResize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"railResize.d.ts","sourceRoot":"","sources":["../../../src/react/filter-rail/railResize.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,IAAI,kBAAkB,EAAE,YAAY,IAAI,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAK9H,MAAM,WAAW,oBAAoB;IACnC,yFAAyF;IACzF,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW;IAC7D,wEAAwE;IACxE,OAAO,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7B,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,kGAAkG;IAClG,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,qGAAqG;IACrG,KAAK,EAAE,aAAa,GAAG,SAAS,CAAC;IACjC,yEAAyE;IACzE,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;QAC/D,SAAS,EAAE,CAAC,KAAK,EAAE,kBAAkB,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;QAC5D,8EAA8E;QAC9E,OAAO,EAAE,MAAM,MAAM,CAAC;KACvB,CAAC;CACH;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,EACjE,SAAS,EACT,KAAK,EACL,OAAc,GACf,EAAE,oBAAoB,GAAG,UAAU,CAAC,CAAC,CAAC,CA6DtC;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,sCAyB7F"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useRef, useState } from 'react';
|
|
3
|
+
import { cn } from '../lib/cn.js';
|
|
4
|
+
import { clampRailWidth, MAX_RAIL_PX, MIN_RAIL_PX, readRailWidth, writeRailWidth } from './railWidth.js';
|
|
5
|
+
export function useRailResize({ resizeKey, label, enabled = true, }) {
|
|
6
|
+
const railRef = useRef(null);
|
|
7
|
+
const [dragged, setDragged] = useState(() => (resizeKey ? readRailWidth(resizeKey) : null));
|
|
8
|
+
const active = Boolean(resizeKey) && enabled;
|
|
9
|
+
const measure = useCallback(() => railRef.current?.getBoundingClientRect().width ?? MIN_RAIL_PX, []);
|
|
10
|
+
const onPointerDown = useCallback((event) => {
|
|
11
|
+
if (!resizeKey || event.button !== 0)
|
|
12
|
+
return;
|
|
13
|
+
const handle = event.currentTarget;
|
|
14
|
+
const startX = event.clientX;
|
|
15
|
+
const startWidth = measure();
|
|
16
|
+
let latest = startWidth;
|
|
17
|
+
handle.setPointerCapture?.(event.pointerId);
|
|
18
|
+
const move = (moveEvent) => {
|
|
19
|
+
latest = clampRailWidth(startWidth + moveEvent.clientX - startX);
|
|
20
|
+
setDragged(latest);
|
|
21
|
+
};
|
|
22
|
+
const end = () => {
|
|
23
|
+
if (handle.hasPointerCapture?.(event.pointerId))
|
|
24
|
+
handle.releasePointerCapture(event.pointerId);
|
|
25
|
+
handle.removeEventListener('pointermove', move);
|
|
26
|
+
handle.removeEventListener('pointerup', end);
|
|
27
|
+
handle.removeEventListener('pointercancel', end);
|
|
28
|
+
writeRailWidth(resizeKey, latest);
|
|
29
|
+
};
|
|
30
|
+
handle.addEventListener('pointermove', move);
|
|
31
|
+
handle.addEventListener('pointerup', end);
|
|
32
|
+
handle.addEventListener('pointercancel', end);
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
}, [resizeKey, measure]);
|
|
35
|
+
const onKeyDown = useCallback((event) => {
|
|
36
|
+
if (!resizeKey)
|
|
37
|
+
return;
|
|
38
|
+
const step = event.shiftKey ? 64 : 16;
|
|
39
|
+
const delta = event.key === 'ArrowRight' ? step : event.key === 'ArrowLeft' ? -step : 0;
|
|
40
|
+
if (!delta)
|
|
41
|
+
return;
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
const next = clampRailWidth((dragged ?? measure()) + delta);
|
|
44
|
+
setDragged(next);
|
|
45
|
+
writeRailWidth(resizeKey, next);
|
|
46
|
+
}, [resizeKey, dragged, measure]);
|
|
47
|
+
const reset = useCallback(() => {
|
|
48
|
+
setDragged(null);
|
|
49
|
+
if (resizeKey)
|
|
50
|
+
writeRailWidth(resizeKey, null);
|
|
51
|
+
}, [resizeKey]);
|
|
52
|
+
const width = active ? dragged : null;
|
|
53
|
+
return {
|
|
54
|
+
railRef,
|
|
55
|
+
active,
|
|
56
|
+
width,
|
|
57
|
+
style: width === null ? undefined : { width },
|
|
58
|
+
reset,
|
|
59
|
+
handle: { label, onPointerDown, onKeyDown, measure },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The handle itself — a 12px hotspot centred on the rail's right border. The RAIL must be
|
|
64
|
+
* `position: relative` (the handle is absolutely positioned against it). Renders nothing while
|
|
65
|
+
* the resize is inactive.
|
|
66
|
+
*
|
|
67
|
+
* Only utilities the stylesheet already ships, so it adds no CSS to any app that imports the
|
|
68
|
+
* `filter-rail` area (`fixtureAppCss.spec.ts` budgets that).
|
|
69
|
+
*/
|
|
70
|
+
export function RailResizeHandle({ rail, className }) {
|
|
71
|
+
if (!rail.active)
|
|
72
|
+
return null;
|
|
73
|
+
const { label, onPointerDown, onKeyDown, measure } = rail.handle;
|
|
74
|
+
return (_jsx("div", { role: 'separator', "aria-orientation": 'vertical', "aria-label": `Resize ${label}`, "aria-valuemin": MIN_RAIL_PX, "aria-valuemax": MAX_RAIL_PX, "aria-valuenow": Math.round(rail.width ?? measure()), tabIndex: 0, "data-cb-rail-resize": '', onPointerDown: onPointerDown, onKeyDown: onKeyDown, onDoubleClick: rail.reset,
|
|
75
|
+
// A 1px edge you cannot hit is not a handle: the hotspot is 12px, centred on the border,
|
|
76
|
+
// and only the hairline tints — no layout shift, like `PanelSeparator`.
|
|
77
|
+
className: cn('absolute inset-y-0 -right-1.5 z-20 w-3 cursor-col-resize touch-none bg-transparent transition-colors hover:bg-accent/20 active:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', className) }));
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=railResize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"railResize.js","sourceRoot":"","sources":["../../../src/react/filter-rail/railResize.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
/** What an `<a>` in the bar is handed — `href`, `onClick`, whatever the app's router needs. */
|
|
3
|
+
export type TopBarLinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'children' | 'className' | 'aria-label' | 'href'>;
|
|
4
|
+
export interface TopBarItem {
|
|
5
|
+
/** Stable key. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** The place's name. It is the accessible name and the hover hint — it is NOT painted. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** The glyph that IS the item. Required: an item without one cannot be drawn in this bar. */
|
|
10
|
+
icon: ReactNode;
|
|
11
|
+
/** The place the user is in. Lit, and `aria-current="page"`. */
|
|
12
|
+
active?: boolean;
|
|
13
|
+
/** Where it goes. A real link, so it still copies and middle-clicks. */
|
|
14
|
+
href: string;
|
|
15
|
+
/** `onClick` and whatever else the app's router needs on the `<a>`. */
|
|
16
|
+
linkProps?: TopBarLinkProps;
|
|
17
|
+
/**
|
|
18
|
+
* 🔴 The purposefully made exception: paint `label` beside the icon. Takes the REASON,
|
|
19
|
+
* not a boolean — the owner's rule (2026-09-25) is icons, not words, "unless there is a
|
|
20
|
+
* purposefully made exception", and a `showLabel` flag is an exception nobody had to make.
|
|
21
|
+
* Even then the words drop below `sm`, because a phone is where they overflowed.
|
|
22
|
+
*/
|
|
23
|
+
wordsBecause?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface TopBarProps {
|
|
26
|
+
/** The app's mark. Its `label` is painted from `md` up and is the accessible name below it. */
|
|
27
|
+
brand: {
|
|
28
|
+
icon: ReactNode;
|
|
29
|
+
label: string;
|
|
30
|
+
href: string;
|
|
31
|
+
linkProps?: TopBarLinkProps;
|
|
32
|
+
};
|
|
33
|
+
/** The places. Icons only — see {@link TopBarItem.wordsBecause} for the one way out. */
|
|
34
|
+
items: readonly TopBarItem[];
|
|
35
|
+
/** The trailing cluster: theme toggle, account menu. Never shrinks. */
|
|
36
|
+
trailing?: ReactNode;
|
|
37
|
+
/** The nav landmark's name. Default `'Places'`. */
|
|
38
|
+
navLabel?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* THE top bar: brand, places, trailing controls — one row that can never be wider than the
|
|
43
|
+
* screen.
|
|
44
|
+
*
|
|
45
|
+
* ── Why it exists (owner, 2026-09-25) ───────────────────────────────────────────────────
|
|
46
|
+
* *"the music app header on iphone goes off the right side of the screen. It should use
|
|
47
|
+
* icons instead of words and it should not be allowed to make the screen have a wide
|
|
48
|
+
* scrollbar … cb should enforce this via a top bar component so each app cannot do it wrong
|
|
49
|
+
* with separately made components."* Every app had hand-rolled its own `<header><nav>`, and
|
|
50
|
+
* music's — "♪ Music · Library · Visualize · YouTube · Add music · Downloads" plus a theme
|
|
51
|
+
* toggle and an avatar — was ~560px of words on a 390px phone, so the whole page scrolled
|
|
52
|
+
* sideways.
|
|
53
|
+
*
|
|
54
|
+
* Two properties are built in rather than asked for:
|
|
55
|
+
* · **Icons, not words.** An item cannot be declared without an icon, and its label is
|
|
56
|
+
* the accessible name + hover hint, never painted — unless `wordsBecause` says why.
|
|
57
|
+
* · **It cannot widen the page.** The row is `min-w-0 max-w-full`; the places take the
|
|
58
|
+
* remaining width and, if a phone is narrower still, scroll INSIDE their own strip.
|
|
59
|
+
* Brand and trailing controls never shrink, so the account menu is always reachable.
|
|
60
|
+
*
|
|
61
|
+
* `guardrails no-raw-nav` is the other half: a hand-rolled `<nav>` in app source fails the
|
|
62
|
+
* app's gate, so the next header is this one.
|
|
63
|
+
*/
|
|
64
|
+
export declare function TopBar({ brand, items, trailing, navLabel, className }: TopBarProps): import("react").JSX.Element;
|
|
65
|
+
//# sourceMappingURL=TopBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TopBar.d.ts","sourceRoot":"","sources":["../../../src/react/nav/TopBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAO7D,+FAA+F;AAC/F,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,oBAAoB,CAAC,iBAAiB,CAAC,EACvC,UAAU,GAAG,WAAW,GAAG,YAAY,GAAG,MAAM,CACjD,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,kBAAkB;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;IACd,6FAA6F;IAC7F,IAAI,EAAE,SAAS,CAAC;IAChB,gEAAgE;IAChE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,+FAA+F;IAC/F,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;IACrF,wFAAwF;IACxF,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAmB,EAAE,SAAS,EAAE,EAAE,WAAW,+BAiD7F"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ScrollArea } from '../components/ScrollArea.js';
|
|
3
|
+
import { Tooltip } from '../components/Tooltip.js';
|
|
4
|
+
import { useScrollEdgeFade } from '../hooks/useScrollEdgeFade.js';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
import { TAP_TARGET_SQUARE } from '../lib/tapTarget.js';
|
|
7
|
+
/**
|
|
8
|
+
* THE top bar: brand, places, trailing controls — one row that can never be wider than the
|
|
9
|
+
* screen.
|
|
10
|
+
*
|
|
11
|
+
* ── Why it exists (owner, 2026-09-25) ───────────────────────────────────────────────────
|
|
12
|
+
* *"the music app header on iphone goes off the right side of the screen. It should use
|
|
13
|
+
* icons instead of words and it should not be allowed to make the screen have a wide
|
|
14
|
+
* scrollbar … cb should enforce this via a top bar component so each app cannot do it wrong
|
|
15
|
+
* with separately made components."* Every app had hand-rolled its own `<header><nav>`, and
|
|
16
|
+
* music's — "♪ Music · Library · Visualize · YouTube · Add music · Downloads" plus a theme
|
|
17
|
+
* toggle and an avatar — was ~560px of words on a 390px phone, so the whole page scrolled
|
|
18
|
+
* sideways.
|
|
19
|
+
*
|
|
20
|
+
* Two properties are built in rather than asked for:
|
|
21
|
+
* · **Icons, not words.** An item cannot be declared without an icon, and its label is
|
|
22
|
+
* the accessible name + hover hint, never painted — unless `wordsBecause` says why.
|
|
23
|
+
* · **It cannot widen the page.** The row is `min-w-0 max-w-full`; the places take the
|
|
24
|
+
* remaining width and, if a phone is narrower still, scroll INSIDE their own strip.
|
|
25
|
+
* Brand and trailing controls never shrink, so the account menu is always reachable.
|
|
26
|
+
*
|
|
27
|
+
* `guardrails no-raw-nav` is the other half: a hand-rolled `<nav>` in app source fails the
|
|
28
|
+
* app's gate, so the next header is this one.
|
|
29
|
+
*/
|
|
30
|
+
export function TopBar({ brand, items, trailing, navLabel = 'Places', className }) {
|
|
31
|
+
const fade = useScrollEdgeFade();
|
|
32
|
+
return (_jsxs("header", { "data-cb-topbar": '', className: cn('flex w-full min-w-0 max-w-full items-center gap-2 px-3 py-1.5 md:gap-3 md:px-6', className), children: [_jsxs("a", { ...brand.linkProps, href: brand.href, "aria-label": brand.label, "data-cb-topbar-brand": '', className: cn('flex shrink-0 items-center gap-2 rounded-md px-1.5 py-1 font-semibold text-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', TAP_TARGET_SQUARE, 'md:pointer-coarse:w-auto'), children: [_jsx("span", { "aria-hidden": 'true', className: 'grid size-6 place-items-center [&_svg]:size-5', children: brand.icon }), _jsx("span", { "aria-hidden": 'true', className: 'hidden md:inline', children: brand.label })] }), _jsx("nav", { "aria-label": navLabel, "data-cb-topbar-nav": '', className: 'min-w-0 flex-1', style: fade.maskStyle, children: _jsx(ScrollArea, { axis: 'x', space: 'none', safeArea: false, scrollRef: fade.ref, onScroll: fade.onScroll, className: 'w-full [scrollbar-width:none] [&::-webkit-scrollbar]:hidden', children: _jsx("div", { className: 'flex w-max items-center gap-1', children: items.map((item) => (_jsx(TopBarLink, { item: item }, item.id))) }) }) }), trailing ? _jsx("div", { className: 'flex shrink-0 items-center gap-1', children: trailing }) : null] }));
|
|
33
|
+
}
|
|
34
|
+
function TopBarLink({ item }) {
|
|
35
|
+
const words = Boolean(item.wordsBecause?.trim());
|
|
36
|
+
return (_jsx(Tooltip, { content: item.label, side: 'bottom', children: _jsxs("a", { ...item.linkProps, href: item.href, "aria-label": item.label, "aria-current": item.active ? 'page' : undefined, "data-active": item.active ? '' : undefined, className: cn('relative flex h-9 min-w-9 shrink-0 items-center justify-center gap-2 rounded-md px-2 transition-colors', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', TAP_TARGET_SQUARE, words && 'sm:pointer-coarse:w-auto', item.active
|
|
37
|
+
? 'bg-muted text-foreground'
|
|
38
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'), children: [_jsx("span", { "aria-hidden": 'true', className: 'grid size-5 place-items-center [&_svg]:size-5', children: item.icon }), words ? (_jsx("span", { "aria-hidden": 'true', className: 'hidden text-sm font-medium sm:inline', children: item.label })) : null] }) }));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=TopBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TopBar.js","sourceRoot":"","sources":["../../../src/react/nav/TopBar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -16,4 +16,5 @@ export { type IconResolver, isPathWithin, NavLink, type NavLinkProps, type NavNa
|
|
|
16
16
|
export { PlaceBar, type PlaceBarProps } from './PlaceBar.js';
|
|
17
17
|
export { type SearchResultGroup, type SearchResultRow, SearchResults, type SearchResultsProps, searchOptionId, } from './SearchResults.js';
|
|
18
18
|
export { SideNav, type SideNavProps } from './SideNav.js';
|
|
19
|
+
export { TopBar, type TopBarItem, type TopBarLinkProps, type TopBarProps } from './TopBar.js';
|
|
19
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/nav/index.ts"],"names":[],"mappings":"AAQA,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,EACrB,eAAe,EACf,KAAK,oBAAoB,EACzB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,mBAAmB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,aAAa,EACb,aAAa,EACb,KAAK,qBAAqB,EAC1B,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EACL,KAAK,YAAY,EACjB,YAAY,EACZ,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,aAAa,EACb,KAAK,kBAAkB,EACvB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/nav/index.ts"],"names":[],"mappings":"AAQA,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,EACrB,eAAe,EACf,KAAK,oBAAoB,EACzB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,mBAAmB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,aAAa,EACb,aAAa,EACb,KAAK,qBAAqB,EAC1B,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EACL,KAAK,YAAY,EACjB,YAAY,EACZ,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,aAAa,EACb,KAAK,kBAAkB,EACvB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/react/nav/index.js
CHANGED
|
@@ -23,4 +23,5 @@ export { isPathWithin, NavLink, } from './navShared.js';
|
|
|
23
23
|
export { PlaceBar } from './PlaceBar.js';
|
|
24
24
|
export { SearchResults, searchOptionId, } from './SearchResults.js';
|
|
25
25
|
export { SideNav } from './SideNav.js';
|
|
26
|
+
export { TopBar } from './TopBar.js';
|
|
26
27
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/nav/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,iGAAiG;AACjG,sFAAsF;AACtF,mFAAmF;AACnF,mFAAmF;AACnF,oFAAoF;AACpF,6FAA6F;AAE7F,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EACL,WAAW,EAEX,eAAe,EAEf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EACL,kBAAkB,EAElB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,GAEpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,qBAAqB,GAEtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAKL,aAAa,EACb,aAAa,EAEb,QAAQ,EAGR,YAAY,GACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,cAAc,EAA4B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAEL,YAAY,EACZ,OAAO,GAGR,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAsB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAGL,aAAa,EAEb,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAqB,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/nav/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,iGAAiG;AACjG,sFAAsF;AACtF,mFAAmF;AACnF,mFAAmF;AACnF,oFAAoF;AACpF,6FAA6F;AAE7F,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EACL,WAAW,EAEX,eAAe,EAEf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EACL,kBAAkB,EAElB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,GAEpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,qBAAqB,GAEtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAKL,aAAa,EACb,aAAa,EAEb,QAAQ,EAGR,YAAY,GACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,cAAc,EAA4B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAEL,YAAY,EACZ,OAAO,GAGR,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAsB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAGL,aAAa,EAEb,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAqB,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,MAAM,EAA2D,MAAM,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardrailsEnforce.d.ts","sourceRoot":"","sources":["../../scripts/guardrailsEnforce.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"guardrailsEnforce.d.ts","sourceRoot":"","sources":["../../scripts/guardrailsEnforce.ts"],"names":[],"mappings":";AAkMA,UAAU,SAAS;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,IAAI;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;CACzD;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CA6B9D;AA+4BD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAEpE;AAgkBD,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAoBtC,CAAC;AASF,eAAO,MAAM,uBAAuB,aAAkD,CAAC;AAGvF,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb;AAED;2FAC2F;AAC3F,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA0C3F;AAED,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAmGhC,wBAAgB,0BAA0B,CAAC,SAAS,GAAE,MAAwB,GAAG,MAAM,EAAE,CAyBxF"}
|
|
@@ -18,14 +18,15 @@
|
|
|
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
|
+
* 107 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("-mt-5 -top-1 [&>*]:min-w-0 [&_svg]:size-3.5
|
|
28
|
-
@source inline("border-r collapse cursor-grab
|
|
27
|
+
@source inline("-mt-5 -top-1 [&::-webkit-scrollbar]:hidden [&>*]:min-w-0 [&_svg]:size-3.5 [scrollbar-width:none]");
|
|
28
|
+
@source inline("bg-accent/15 border-border/40 border-dashed border-l border-r collapse cursor-grab");
|
|
29
|
+
@source inline("data-[state=checked]:ring-2 data-[state=checked]:ring-foreground");
|
|
29
30
|
@source inline("data-[state=checked]:ring-offset-1 data-[state=closed]:slide-out-to-bottom");
|
|
30
31
|
@source inline("data-[state=closed]:slide-out-to-left data-[state=closed]:slide-out-to-right");
|
|
31
32
|
@source inline("data-[state=open]:slide-in-from-bottom data-[state=open]:slide-in-from-left");
|
|
@@ -36,8 +37,9 @@
|
|
|
36
37
|
@source inline("items-stretch justify-around justify-start lead leading-4 left-0 left-3 lg:flex lg:flex-col");
|
|
37
38
|
@source inline("lg:hidden lg:shrink-0 line-clamp-2 max-h-72 max-h-[40dvh] max-h-[65dvh] max-h-[85dvh]");
|
|
38
39
|
@source inline("max-h-[inherit] max-sm:flex-none max-sm:justify-center max-sm:px-0 max-sm:w-10 max-w max-w-64");
|
|
39
|
-
@source inline("max-w-[12rem] max-w-[20rem] md:flex md:flex-col md:hidden md:
|
|
40
|
-
@source inline("min-w-3.5 min-w-8 mix-blend-difference mt-auto
|
|
41
|
-
@source inline("pb-[env(safe-area-inset-bottom,0px)] pl-9 place-items-center
|
|
42
|
-
@source inline("rounded-l-xl rounded-r-xl rounded-t-xl shadow-lg size-7 size-9
|
|
43
|
-
@source inline("space-y-0.5 space-y-2 text-[0.625rem] top-2
|
|
40
|
+
@source inline("max-w-[12rem] max-w-[20rem] md:flex md:flex-col md:gap-3 md:hidden md:pointer-coarse:w-auto");
|
|
41
|
+
@source inline("md:px-6 md:shrink-0 min-h-12 min-h-dvh min-w-3.5 min-w-8 min-w-9 mix-blend-difference mt-auto");
|
|
42
|
+
@source inline("opacity-55 opacity-80 pb-1 pb-2 pb-[env(safe-area-inset-bottom,0px)] pl-9 place-items-center");
|
|
43
|
+
@source inline("pr-9 pt-1 px-0.5 right-1 rounded-l-xl rounded-r-xl rounded-t-xl shadow-lg size-7 size-9");
|
|
44
|
+
@source inline("sm:inline sm:inline-flex sm:pointer-coarse:w-auto space-y-0.5 space-y-2 text-[0.625rem] top-2");
|
|
45
|
+
@source inline("w-0.5 w-52 w-56 w-60 w-max");
|
|
@@ -1015,6 +1015,9 @@
|
|
|
1015
1015
|
.min-w-8 {
|
|
1016
1016
|
min-width: calc(var(--spacing) * 8);
|
|
1017
1017
|
}
|
|
1018
|
+
.min-w-9 {
|
|
1019
|
+
min-width: calc(var(--spacing) * 9);
|
|
1020
|
+
}
|
|
1018
1021
|
.min-w-10 {
|
|
1019
1022
|
min-width: calc(var(--spacing) * 10);
|
|
1020
1023
|
}
|
|
@@ -4080,6 +4083,9 @@
|
|
|
4080
4083
|
.md\:flex-row {
|
|
4081
4084
|
flex-direction: row;
|
|
4082
4085
|
}
|
|
4086
|
+
.md\:gap-3 {
|
|
4087
|
+
gap: calc(var(--spacing) * 3);
|
|
4088
|
+
}
|
|
4083
4089
|
:where(.md\:divide-x > :not(:last-child)) {
|
|
4084
4090
|
--tw-divide-x-reverse: 0;
|
|
4085
4091
|
border-inline-style: var(--tw-border-style);
|
|
@@ -4089,6 +4095,9 @@
|
|
|
4089
4095
|
:where(.md\:divide-border > :not(:last-child)) {
|
|
4090
4096
|
border-color: var(--border);
|
|
4091
4097
|
}
|
|
4098
|
+
.md\:px-6 {
|
|
4099
|
+
padding-inline: calc(var(--spacing) * 6);
|
|
4100
|
+
}
|
|
4092
4101
|
}
|
|
4093
4102
|
@media (width >= 64rem) {
|
|
4094
4103
|
.lg\:inset-8 {
|
|
@@ -4313,6 +4322,20 @@
|
|
|
4313
4322
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
4314
4323
|
}
|
|
4315
4324
|
}
|
|
4325
|
+
@media (width >= 40rem) {
|
|
4326
|
+
@media (pointer: coarse) {
|
|
4327
|
+
.sm\:pointer-coarse\:w-auto {
|
|
4328
|
+
width: auto;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
@media (width >= 48rem) {
|
|
4333
|
+
@media (pointer: coarse) {
|
|
4334
|
+
.md\:pointer-coarse\:w-auto {
|
|
4335
|
+
width: auto;
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4316
4339
|
.\[\&_\.alert-icon\]\:text-accent .alert-icon {
|
|
4317
4340
|
color: var(--accent);
|
|
4318
4341
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.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.",
|
|
@@ -692,8 +692,8 @@
|
|
|
692
692
|
"@types/react-dom": "^19.2.3",
|
|
693
693
|
"@vidstack/react": "1.15.6",
|
|
694
694
|
"@vitejs/plugin-react": "^5.2.0",
|
|
695
|
-
"cursedbelt-server": "^4.
|
|
696
|
-
"cursedops": "^0.
|
|
695
|
+
"cursedbelt-server": "^4.32.1",
|
|
696
|
+
"cursedops": "^0.10.7",
|
|
697
697
|
"diff": "^9.0.0",
|
|
698
698
|
"emoji-mart": "^5.6.0",
|
|
699
699
|
"hls.js": "^1.5.0",
|
|
@@ -1219,3 +1219,27 @@ const html = \`<input type="password">\`;`,
|
|
|
1219
1219
|
expect(check('no-raw-password', `// never write type="password" by hand`)).toHaveLength(0);
|
|
1220
1220
|
});
|
|
1221
1221
|
});
|
|
1222
|
+
|
|
1223
|
+
describe('no-raw-nav', () => {
|
|
1224
|
+
test('🔴 flags a hand-rolled <nav> — the header music shipped on 2026-09-25', () => {
|
|
1225
|
+
expect(
|
|
1226
|
+
check('no-raw-nav', `<header><nav className="app-header__nav"><a href="/">Library</a></nav></header>`),
|
|
1227
|
+
).toHaveLength(1);
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
test('passes TopBar, and a nav with a stated reason in the block above', () => {
|
|
1231
|
+
expect(check('no-raw-nav', `<TopBar brand={brand} items={items} />`)).toHaveLength(0);
|
|
1232
|
+
expect(
|
|
1233
|
+
check(
|
|
1234
|
+
'no-raw-nav',
|
|
1235
|
+
`{/* guardrails-ignore no-raw-nav: the library rail is a side list of folders,
|
|
1236
|
+
not a top bar */}
|
|
1237
|
+
<nav aria-label="Library">`,
|
|
1238
|
+
),
|
|
1239
|
+
).toHaveLength(0);
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
test("a comment that merely mentions <nav> is not a violation", () => {
|
|
1243
|
+
expect(check('no-raw-nav', `// replacing the <nav> would delete the landmark`)).toHaveLength(0);
|
|
1244
|
+
});
|
|
1245
|
+
});
|