@fungi.computer/caps 0.0.2 → 0.1.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/README.md +6 -5
- package/dist/components/field-guide.d.ts +0 -10
- package/dist/components/field-guide.js +0 -4
- package/dist/components/window-crumbs.d.ts +44 -0
- package/dist/components/window-crumbs.js +69 -0
- package/dist/components/window.d.ts +21 -1
- package/dist/components/window.js +5 -3
- package/dist/styles.css +85 -109
- package/docs/api/README.md +1 -0
- package/docs/api/components/field-guide.md +0 -49
- package/docs/api/components/window-crumbs.md +185 -0
- package/docs/api/components/window.md +34 -9
- package/package.json +4 -4
- package/src/styles.css +91 -33
package/README.md
CHANGED
|
@@ -550,9 +550,9 @@ marginalia, growth and type tokens, so they look right in `field-guide`,
|
|
|
550
550
|
`field-guide-night` and the other editorial themes.
|
|
551
551
|
|
|
552
552
|
- `@fungi.computer/caps/components/field-guide`: `Keycap` (a native keyboard
|
|
553
|
-
key), `
|
|
554
|
-
|
|
555
|
-
|
|
553
|
+
key), `CartridgeLabel` (a ruled cartridge face) and `SpecimenLabel` (plate
|
|
554
|
+
annotation, catalogue metadata, title and summary). Status lights are the
|
|
555
|
+
gooey `Status` bead from `@fungi.computer/caps/components/status`.
|
|
556
556
|
- `@fungi.computer/caps/components/technonomicon`: `CigaretteCard`, `Masthead`,
|
|
557
557
|
`SectionBar`, `Folio`, `IssueMasthead`, `Spread` and related editorial pieces.
|
|
558
558
|
|
|
@@ -1006,7 +1006,8 @@ its own control finish.
|
|
|
1006
1006
|
### Editorial pieces
|
|
1007
1007
|
|
|
1008
1008
|
```tsx
|
|
1009
|
-
import { Keycap
|
|
1009
|
+
import { Keycap } from "@fungi.computer/caps/components/field-guide";
|
|
1010
|
+
import { Status } from "@fungi.computer/caps/components/status";
|
|
1010
1011
|
import {
|
|
1011
1012
|
Folio,
|
|
1012
1013
|
Masthead,
|
|
@@ -1030,7 +1031,7 @@ export function FrontPage() {
|
|
|
1030
1031
|
/>
|
|
1031
1032
|
<p>
|
|
1032
1033
|
Press <Keycap>J</Keycap> for the next story.{" "}
|
|
1033
|
-
<
|
|
1034
|
+
<Status tone="success" label="Online" />
|
|
1034
1035
|
</p>
|
|
1035
1036
|
<Folio items={["The Spore Print", "Page A1"]} />
|
|
1036
1037
|
</>
|
|
@@ -5,16 +5,6 @@ export type KeycapProps = {
|
|
|
5
5
|
} & HTMLAttributes<HTMLElement>;
|
|
6
6
|
/** The shared field-guide treatment for a keyboard key. */
|
|
7
7
|
export declare function Keycap({ children, className, ...props }: KeycapProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
/** Semantic accent pigments for the static status light. */
|
|
9
|
-
export type StatusLedTone = "spore" | "mycelium" | "fruiting";
|
|
10
|
-
/** Props accepted by StatusLed. */
|
|
11
|
-
export type StatusLedProps = {
|
|
12
|
-
active: boolean;
|
|
13
|
-
label: string;
|
|
14
|
-
tone?: StatusLedTone;
|
|
15
|
-
} & Omit<HTMLAttributes<HTMLSpanElement>, "children">;
|
|
16
|
-
/** A static, labelled indicator using the current field-guide pigment tokens. */
|
|
17
|
-
export declare function StatusLed({ active, className, label, tone, ...props }: StatusLedProps): import("react/jsx-runtime").JSX.Element;
|
|
18
8
|
/** Props accepted by CartridgeLabel. */
|
|
19
9
|
export type CartridgeLabelProps = {
|
|
20
10
|
title: string;
|
|
@@ -4,10 +4,6 @@ import { cn } from "../lib/cn.js";
|
|
|
4
4
|
export function Keycap({ children, className, ...props }) {
|
|
5
5
|
return (_jsx("kbd", { ...props, className: cn("caps-keycap", className), children: children }));
|
|
6
6
|
}
|
|
7
|
-
/** A static, labelled indicator using the current field-guide pigment tokens. */
|
|
8
|
-
export function StatusLed({ active, className, label, tone = "mycelium", ...props }) {
|
|
9
|
-
return (_jsxs("span", { ...props, "aria-label": `${label}: ${active ? "on" : "off"}`, className: cn("caps-led", className), "data-active": active, "data-tone": tone, role: "img", children: [_jsx("span", { "aria-hidden": "true", className: "caps-led-bead" }), _jsx("span", { "aria-hidden": "true", children: label })] }));
|
|
10
|
-
}
|
|
11
7
|
/** A ruled, token-backed printed label for a physical game cartridge. */
|
|
12
8
|
export function CartridgeLabel({ className, mark, subtitle, title, ...props }) {
|
|
13
9
|
return (_jsxs("figure", { ...props, "aria-label": `${title} cartridge label`, className: cn("caps-cartridge-label", className), children: [_jsx("span", { "aria-hidden": "true", className: "caps-cartridge-label-rule" }), mark ? (_jsx("span", { "aria-hidden": "true", className: "caps-cartridge-label-mark", children: mark })) : null, _jsx("span", { className: "caps-cartridge-label-title", children: title }), subtitle ? (_jsx("span", { className: "caps-cartridge-label-subtitle", children: subtitle })) : null, _jsx("span", { "aria-hidden": "true", className: "caps-cartridge-label-rule" })] }));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type StatusTone } from "./status.js";
|
|
2
|
+
/** One place in the content's path; `siblings` makes it a switcher. */
|
|
3
|
+
export type WindowCrumb = {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
/** Other places at this level, offered by a ▾ switcher on the crumb. */
|
|
7
|
+
siblings?: readonly {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}[];
|
|
11
|
+
};
|
|
12
|
+
/** A command the content offers in the header; the content owns its effect. */
|
|
13
|
+
export type WindowCommand = {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
/** Which Caps button a primary command wears; default `outline`. */
|
|
17
|
+
variant?: "primary" | "outline" | "ghost";
|
|
18
|
+
/** A menu item that removes or discards something. */
|
|
19
|
+
tone?: "destructive";
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
};
|
|
22
|
+
/** A quiet fact about the view: muted text led by the Caps status bead. */
|
|
23
|
+
export type WindowStatus = {
|
|
24
|
+
id: string;
|
|
25
|
+
label: string;
|
|
26
|
+
tone?: Extract<StatusTone, "neutral" | "info" | "warning" | "success" | "error">;
|
|
27
|
+
};
|
|
28
|
+
/** The path as a trail; on a phone only `‹ parent › current` remain. */
|
|
29
|
+
export declare function WindowCrumbs({ crumbs, popover, onCrumb, }: {
|
|
30
|
+
crumbs: readonly WindowCrumb[];
|
|
31
|
+
popover?: HTMLElement | null | undefined;
|
|
32
|
+
onCrumb?: ((crumbId: string, siblingId?: string) => void) | undefined;
|
|
33
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
/** Primary commands as buttons, the rest behind a ⋯ menu. */
|
|
35
|
+
export declare function WindowCommands({ primary, menu, popover, onAction, }: {
|
|
36
|
+
primary?: readonly WindowCommand[] | undefined;
|
|
37
|
+
menu?: readonly WindowCommand[] | undefined;
|
|
38
|
+
popover?: HTMLElement | null | undefined;
|
|
39
|
+
onAction?: ((id: string) => void) | undefined;
|
|
40
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
41
|
+
/** Muted state text with the Caps status bead; hidden on a phone. */
|
|
42
|
+
export declare function WindowStatuses({ status, }: {
|
|
43
|
+
status: readonly WindowStatus[];
|
|
44
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDown, Ellipsis } from "lucide-react";
|
|
3
|
+
import { Button } from "./button.js";
|
|
4
|
+
import { Status } from "./status.js";
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "./dropdown-menu.js";
|
|
6
|
+
import { IconButton } from "./icon-button.js";
|
|
7
|
+
/** The longest path shown whole; longer paths fold the middle into "…". */
|
|
8
|
+
const CRUMBS_SHOWN = 4;
|
|
9
|
+
function Separator() {
|
|
10
|
+
return (_jsx("span", { className: "caps-window-crumb-separator", "aria-hidden": "true", children: "\u203A" }));
|
|
11
|
+
}
|
|
12
|
+
function Crumb({ crumb, current, popover, onCrumb, }) {
|
|
13
|
+
const label = _jsx("span", { className: "caps-window-crumb-label", children: crumb.label });
|
|
14
|
+
const common = {
|
|
15
|
+
"data-caps-window-crumb": crumb.id,
|
|
16
|
+
"aria-current": current ? "page" : undefined,
|
|
17
|
+
};
|
|
18
|
+
if (crumb.siblings?.length) {
|
|
19
|
+
return (_jsxs(DropdownMenu, { modal: false, children: [_jsxs(DropdownMenuTrigger, { ...common, className: "caps-window-crumb shrink", render: _jsx(Button, { size: "sm", variant: "ghost" }), "aria-label": `${crumb.label}, switch`, children: [label, _jsx(ChevronDown, { "aria-hidden": "true", className: "size-3" })] }), _jsx(DropdownMenuContent, { align: "start", container: popover, children: crumb.siblings.map((sibling) => (_jsx(DropdownMenuItem, { onSelect: () => {
|
|
20
|
+
onCrumb?.(crumb.id, sibling.id);
|
|
21
|
+
}, children: sibling.label }, sibling.id))) })] }));
|
|
22
|
+
}
|
|
23
|
+
if (current || onCrumb === undefined)
|
|
24
|
+
return (_jsx("span", { ...common, className: "caps-window-crumb", children: label }));
|
|
25
|
+
return (_jsx(Button, { ...common, size: "sm", variant: "ghost", className: "caps-window-crumb shrink", onClick: () => {
|
|
26
|
+
onCrumb(crumb.id);
|
|
27
|
+
}, children: label }));
|
|
28
|
+
}
|
|
29
|
+
/** The path as a trail; on a phone only `‹ parent › current` remain. */
|
|
30
|
+
export function WindowCrumbs({ crumbs, popover, onCrumb, }) {
|
|
31
|
+
const folded = crumbs.length > CRUMBS_SHOWN;
|
|
32
|
+
const head = folded ? crumbs.slice(0, 1) : crumbs.slice(0, -1);
|
|
33
|
+
const middle = folded ? crumbs.slice(1, -2) : [];
|
|
34
|
+
const tail = folded ? crumbs.slice(-2) : crumbs.slice(-1);
|
|
35
|
+
const last = crumbs.at(-1);
|
|
36
|
+
const parts = [];
|
|
37
|
+
const add = (crumb) => {
|
|
38
|
+
parts.push(_jsxs("span", { className: "caps-window-crumb-item", "data-caps-crumb-position": crumb.id === last?.id
|
|
39
|
+
? "current"
|
|
40
|
+
: crumb.id === crumbs.at(-2)?.id
|
|
41
|
+
? "parent"
|
|
42
|
+
: "ancestor", children: [parts.length > 0 ? _jsx(Separator, {}) : null, _jsx(Crumb, { crumb: crumb, current: crumb.id === last?.id, popover: popover, onCrumb: onCrumb })] }, crumb.id));
|
|
43
|
+
};
|
|
44
|
+
for (const crumb of head)
|
|
45
|
+
add(crumb);
|
|
46
|
+
if (middle.length > 0)
|
|
47
|
+
parts.push(_jsxs("span", { className: "caps-window-crumb-item", "data-caps-crumb-position": "ancestor", children: [_jsx(Separator, {}), _jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { className: "caps-window-crumb", render: _jsx(Button, { size: "sm", variant: "ghost" }), "aria-label": "More of the path", children: _jsx(Ellipsis, { "aria-hidden": "true", className: "size-3" }) }), _jsx(DropdownMenuContent, { align: "start", container: popover, children: middle.map((crumb) => (_jsx(DropdownMenuItem, { onSelect: () => {
|
|
48
|
+
onCrumb?.(crumb.id);
|
|
49
|
+
}, children: crumb.label }, crumb.id))) })] })] }, "fold"));
|
|
50
|
+
for (const crumb of tail)
|
|
51
|
+
add(crumb);
|
|
52
|
+
return (_jsx("nav", { className: "caps-window-crumbs", "aria-label": "Path", children: parts }));
|
|
53
|
+
}
|
|
54
|
+
/** Primary commands as buttons, the rest behind a ⋯ menu. */
|
|
55
|
+
export function WindowCommands({ primary, menu, popover, onAction, }) {
|
|
56
|
+
if (!primary?.length && !menu?.length)
|
|
57
|
+
return null;
|
|
58
|
+
return (_jsxs("div", { className: "caps-window-commands", role: "group", "aria-label": "Commands", children: [primary?.map((command) => (_jsx(Button, { size: "sm", variant: command.variant ?? "outline", disabled: command.disabled === true, onClick: () => {
|
|
59
|
+
onAction?.(command.id);
|
|
60
|
+
}, children: command.label }, command.id))), menu?.length ? (_jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { render: _jsx(IconButton, { icon: _jsx(Ellipsis, {}), "aria-label": "More" }) }), _jsx(DropdownMenuContent, { align: "end", container: popover, children: menu.map((command) => (_jsx(DropdownMenuItem, { disabled: command.disabled === true, variant: command.tone === "destructive" ? "destructive" : "default", onSelect: () => {
|
|
61
|
+
onAction?.(command.id);
|
|
62
|
+
}, children: command.label }, command.id))) })] })) : null] }));
|
|
63
|
+
}
|
|
64
|
+
/** Muted state text with the Caps status bead; hidden on a phone. */
|
|
65
|
+
export function WindowStatuses({ status, }) {
|
|
66
|
+
if (status.length === 0)
|
|
67
|
+
return null;
|
|
68
|
+
return (_jsx("div", { className: "caps-window-statuses", role: "group", "aria-label": "State", children: status.map((item) => (_jsx(Status, { glyph: "dot", size: "compact", tone: item.tone ?? "neutral", label: item.label, title: item.label, className: "caps-window-status" }, item.id))) }));
|
|
69
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type WindowStatus, type WindowCommand, type WindowCrumb } from "./window-crumbs.js";
|
|
3
|
+
export type { WindowCommand, WindowCrumb, WindowStatus, } from "./window-crumbs.js";
|
|
2
4
|
/** Window presentation only. The host owns geometry, focus order and lifecycle. */
|
|
3
5
|
export type WindowFrameProps = HTMLAttributes<HTMLElement> & {
|
|
4
6
|
active?: boolean;
|
|
@@ -33,12 +35,30 @@ export type WindowHeaderProps = Omit<HTMLAttributes<HTMLElement>, "title" | "chi
|
|
|
33
35
|
actions?: ReactNode;
|
|
34
36
|
/** Present only while there is somewhere to go back to. */
|
|
35
37
|
back?: WindowBackAction;
|
|
38
|
+
/**
|
|
39
|
+
* The content's path, shown in place of the title and subtitle. The last
|
|
40
|
+
* crumb is the current place; earlier ones are links, and a crumb with
|
|
41
|
+
* siblings is a switcher.
|
|
42
|
+
*/
|
|
43
|
+
crumbs?: readonly WindowCrumb[];
|
|
44
|
+
/** Selected crumb, or the sibling chosen from a switcher crumb. */
|
|
45
|
+
onCrumb?: (crumbId: string, siblingId?: string) => void;
|
|
46
|
+
/** Quiet facts about the view, such as "Valid" or "Saved", with beads. */
|
|
47
|
+
status?: readonly WindowStatus[];
|
|
48
|
+
/** The view's own commands, as buttons before the window controls. */
|
|
49
|
+
primary?: readonly WindowCommand[];
|
|
50
|
+
/** Less common commands, behind a ⋯ menu. */
|
|
51
|
+
menu?: readonly WindowCommand[];
|
|
52
|
+
/** A primary or menu command the person used, by its id. */
|
|
53
|
+
onAction?: (id: string) => void;
|
|
54
|
+
/** Where header menus open; the host's popover layer. */
|
|
55
|
+
popover?: HTMLElement | null;
|
|
36
56
|
/** `compact` is a slim bar for tiled layouts; touch targets keep 44px. */
|
|
37
57
|
density?: "regular" | "compact";
|
|
38
58
|
handleProps?: Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" | "type">;
|
|
39
59
|
};
|
|
40
60
|
/** The handle, Back and actions are siblings, so presses never start a drag. */
|
|
41
|
-
export declare function WindowHeader({ title, subtitle, icon, actions, back, density, handleProps, className, ...props }: WindowHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
61
|
+
export declare function WindowHeader({ title, subtitle, icon, actions, back, crumbs, onCrumb, primary, menu, status, onAction, popover, density, handleProps, className, ...props }: WindowHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
42
62
|
/** Available callbacks determine available controls; no inert placeholder actions. */
|
|
43
63
|
export type WindowActionsProps = {
|
|
44
64
|
title: string;
|
|
@@ -3,6 +3,7 @@ import { ArrowLeft, ArrowUpRight, Columns2, Maximize2, Minimize2, Minus, PanelsT
|
|
|
3
3
|
import { forwardRef, } from "react";
|
|
4
4
|
import { cn } from "../lib/cn.js";
|
|
5
5
|
import { IconButton } from "./icon-button.js";
|
|
6
|
+
import { WindowCommands, WindowCrumbs, WindowStatuses, } from "./window-crumbs.js";
|
|
6
7
|
/** A clipped, rounded shell for either a tiled or floating application. */
|
|
7
8
|
export const WindowFrame = forwardRef(function WindowFrame({ active = false, floating = false, fitContent = false, className, ...props }, ref) {
|
|
8
9
|
return (_jsx("section", { ...props, ref: ref, className: cn("caps-window", className), "data-active": active, "data-floating": floating, "data-fit-content": fitContent }));
|
|
@@ -12,10 +13,11 @@ export function WindowSnapPreview({ className, ...props }) {
|
|
|
12
13
|
return (_jsx("div", { ...props, "aria-hidden": "true", className: cn("caps-window-snap-preview", className) }));
|
|
13
14
|
}
|
|
14
15
|
/** The handle, Back and actions are siblings, so presses never start a drag. */
|
|
15
|
-
export function WindowHeader({ title, subtitle, icon, actions, back, density = "regular", handleProps, className, ...props }) {
|
|
16
|
+
export function WindowHeader({ title, subtitle, icon, actions, back, crumbs, onCrumb, primary, menu, status, onAction, popover, density = "regular", handleProps, className, ...props }) {
|
|
16
17
|
const label = subtitle ? `${title} · ${subtitle}` : title;
|
|
17
|
-
const
|
|
18
|
-
|
|
18
|
+
const trail = crumbs !== undefined && crumbs.length > 0;
|
|
19
|
+
const content = trail ? null : (_jsxs(_Fragment, { children: [icon && (_jsx("span", { className: "caps-window-icon", "aria-hidden": "true", children: icon })), _jsx("span", { className: "caps-window-name", children: title }), subtitle && (_jsxs("span", { className: "caps-window-subtitle", children: [_jsx("span", { "aria-hidden": "true", children: "\u00B7" }), " ", subtitle] }))] }));
|
|
20
|
+
return (_jsxs("header", { ...props, className: cn("caps-window-header", className), "data-density": density, children: [back && (_jsx(IconButton, { className: "caps-window-back", "aria-label": back.label, title: back.hint ?? back.label, icon: _jsx(ArrowLeft, {}), onClick: back.onBack })), trail ? (_jsxs(_Fragment, { children: [icon && (_jsx("span", { className: "caps-window-icon", "aria-hidden": "true", children: icon })), _jsx(WindowCrumbs, { crumbs: crumbs, popover: popover, onCrumb: onCrumb })] })) : null, handleProps ? (_jsx("button", { ...handleProps, type: "button", "data-caps-window-handle": true, className: cn("caps-window-title", handleProps.className), title: label, ...(trail ? { "aria-label": label } : {}), children: content })) : (_jsx("div", { className: "caps-window-title", title: label, ...(trail ? { "aria-hidden": true } : {}), children: content })), status ? _jsx(WindowStatuses, { status: status }) : null, _jsx(WindowCommands, { primary: primary, menu: menu, popover: popover, onAction: onAction }), actions] }));
|
|
19
21
|
}
|
|
20
22
|
/** Consistent icons, ordering and accessible names for host-owned commands. */
|
|
21
23
|
export function WindowActions({ title, floating = false, maximized = false, onPromote, onToggleFloating, onToggleMaximize, onMinimize, onClose, onSelect, }) {
|
package/dist/styles.css
CHANGED
|
@@ -5466,9 +5466,6 @@
|
|
|
5466
5466
|
.mt-sm {
|
|
5467
5467
|
margin-top: var(--spacing-sm);
|
|
5468
5468
|
}
|
|
5469
|
-
.mb-xs {
|
|
5470
|
-
margin-bottom: var(--spacing-xs);
|
|
5471
|
-
}
|
|
5472
5469
|
.status {
|
|
5473
5470
|
@layer daisyui.l1.l2.l3 {
|
|
5474
5471
|
display: inline-block;
|
|
@@ -5908,15 +5905,9 @@
|
|
|
5908
5905
|
.w-12 {
|
|
5909
5906
|
width: calc(var(--spacing) * 12);
|
|
5910
5907
|
}
|
|
5911
|
-
.w-20 {
|
|
5912
|
-
width: calc(var(--spacing) * 20);
|
|
5913
|
-
}
|
|
5914
5908
|
.w-48 {
|
|
5915
5909
|
width: calc(var(--spacing) * 48);
|
|
5916
5910
|
}
|
|
5917
|
-
.w-\[calc\(100\%-1rem\)\] {
|
|
5918
|
-
width: calc(100% - 1rem);
|
|
5919
|
-
}
|
|
5920
5911
|
.w-auto {
|
|
5921
5912
|
width: auto;
|
|
5922
5913
|
}
|
|
@@ -5980,12 +5971,12 @@
|
|
|
5980
5971
|
.flex-1 {
|
|
5981
5972
|
flex: 1;
|
|
5982
5973
|
}
|
|
5974
|
+
.shrink {
|
|
5975
|
+
flex-shrink: 1;
|
|
5976
|
+
}
|
|
5983
5977
|
.shrink-0 {
|
|
5984
5978
|
flex-shrink: 0;
|
|
5985
5979
|
}
|
|
5986
|
-
.grow {
|
|
5987
|
-
flex-grow: 1;
|
|
5988
|
-
}
|
|
5989
5980
|
.-translate-x-1\/2 {
|
|
5990
5981
|
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
|
|
5991
5982
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
@@ -6175,9 +6166,6 @@
|
|
|
6175
6166
|
.self-stretch {
|
|
6176
6167
|
align-self: stretch;
|
|
6177
6168
|
}
|
|
6178
|
-
.justify-self-end {
|
|
6179
|
-
justify-self: flex-end;
|
|
6180
|
-
}
|
|
6181
6169
|
.truncate {
|
|
6182
6170
|
overflow: hidden;
|
|
6183
6171
|
text-overflow: ellipsis;
|
|
@@ -6394,9 +6382,6 @@
|
|
|
6394
6382
|
.p-lg {
|
|
6395
6383
|
padding: var(--spacing-lg);
|
|
6396
6384
|
}
|
|
6397
|
-
.p-lg\! {
|
|
6398
|
-
padding: var(--spacing-lg) !important;
|
|
6399
|
-
}
|
|
6400
6385
|
.p-md {
|
|
6401
6386
|
padding: var(--spacing-md);
|
|
6402
6387
|
}
|
|
@@ -6409,9 +6394,6 @@
|
|
|
6409
6394
|
.p-xs {
|
|
6410
6395
|
padding: var(--spacing-xs);
|
|
6411
6396
|
}
|
|
6412
|
-
.p-xs\! {
|
|
6413
|
-
padding: var(--spacing-xs) !important;
|
|
6414
|
-
}
|
|
6415
6397
|
.caps-stat-compact {
|
|
6416
6398
|
& > .stat {
|
|
6417
6399
|
padding-block: var(--spacing-xs);
|
|
@@ -6448,15 +6430,6 @@
|
|
|
6448
6430
|
.py-xs {
|
|
6449
6431
|
padding-block: var(--spacing-xs);
|
|
6450
6432
|
}
|
|
6451
|
-
.ps-md {
|
|
6452
|
-
padding-inline-start: var(--spacing-md);
|
|
6453
|
-
}
|
|
6454
|
-
.ps-xs {
|
|
6455
|
-
padding-inline-start: var(--spacing-xs);
|
|
6456
|
-
}
|
|
6457
|
-
.pe-sm {
|
|
6458
|
-
padding-inline-end: var(--spacing-sm);
|
|
6459
|
-
}
|
|
6460
6433
|
.pt-3xl {
|
|
6461
6434
|
padding-top: var(--spacing-3xl);
|
|
6462
6435
|
}
|
|
@@ -6582,9 +6555,6 @@
|
|
|
6582
6555
|
.whitespace-nowrap {
|
|
6583
6556
|
white-space: nowrap;
|
|
6584
6557
|
}
|
|
6585
|
-
.whitespace-pre-wrap {
|
|
6586
|
-
white-space: pre-wrap;
|
|
6587
|
-
}
|
|
6588
6558
|
.alert-error {
|
|
6589
6559
|
@layer daisyui.l1.l2 {
|
|
6590
6560
|
color: var(--color-error-content);
|
|
@@ -6771,9 +6741,6 @@
|
|
|
6771
6741
|
.underline {
|
|
6772
6742
|
text-decoration-line: underline;
|
|
6773
6743
|
}
|
|
6774
|
-
.opacity-50 {
|
|
6775
|
-
opacity: 50%;
|
|
6776
|
-
}
|
|
6777
6744
|
.opacity-55 {
|
|
6778
6745
|
opacity: 55%;
|
|
6779
6746
|
}
|
|
@@ -7172,20 +7139,6 @@
|
|
|
7172
7139
|
}
|
|
7173
7140
|
}
|
|
7174
7141
|
}
|
|
7175
|
-
.hover\:gap-md {
|
|
7176
|
-
&:hover {
|
|
7177
|
-
@media (hover: hover) {
|
|
7178
|
-
gap: var(--spacing-md);
|
|
7179
|
-
}
|
|
7180
|
-
}
|
|
7181
|
-
}
|
|
7182
|
-
.hover\:gap-sm {
|
|
7183
|
-
&:hover {
|
|
7184
|
-
@media (hover: hover) {
|
|
7185
|
-
gap: var(--spacing-sm);
|
|
7186
|
-
}
|
|
7187
|
-
}
|
|
7188
|
-
}
|
|
7189
7142
|
.hover\:bg-base-200 {
|
|
7190
7143
|
&:hover {
|
|
7191
7144
|
@media (hover: hover) {
|
|
@@ -7214,11 +7167,6 @@
|
|
|
7214
7167
|
}
|
|
7215
7168
|
}
|
|
7216
7169
|
}
|
|
7217
|
-
.focus\:gap-xs {
|
|
7218
|
-
&:focus {
|
|
7219
|
-
gap: var(--spacing-xs);
|
|
7220
|
-
}
|
|
7221
|
-
}
|
|
7222
7170
|
.focus-visible\:scale-100 {
|
|
7223
7171
|
&:focus-visible {
|
|
7224
7172
|
--tw-scale-x: 100%;
|
|
@@ -7373,16 +7321,6 @@
|
|
|
7373
7321
|
}
|
|
7374
7322
|
}
|
|
7375
7323
|
}
|
|
7376
|
-
.data-\[open\]\:bg-base-200 {
|
|
7377
|
-
&[data-open] {
|
|
7378
|
-
background-color: var(--color-base-200);
|
|
7379
|
-
}
|
|
7380
|
-
}
|
|
7381
|
-
.data-\[open\]\:bg-base-300 {
|
|
7382
|
-
&[data-open] {
|
|
7383
|
-
background-color: var(--color-base-300);
|
|
7384
|
-
}
|
|
7385
|
-
}
|
|
7386
7324
|
.motion-reduce\:transition-none {
|
|
7387
7325
|
@media (prefers-reduced-motion: reduce) {
|
|
7388
7326
|
transition-property: none;
|
|
@@ -7448,16 +7386,6 @@
|
|
|
7448
7386
|
padding-inline: calc(var(--spacing) * 2);
|
|
7449
7387
|
}
|
|
7450
7388
|
}
|
|
7451
|
-
.md\:px-lg {
|
|
7452
|
-
@media (width >= 48rem) {
|
|
7453
|
-
padding-inline: var(--spacing-lg);
|
|
7454
|
-
}
|
|
7455
|
-
}
|
|
7456
|
-
.md\:px-xs {
|
|
7457
|
-
@media (width >= 48rem) {
|
|
7458
|
-
padding-inline: var(--spacing-xs);
|
|
7459
|
-
}
|
|
7460
|
-
}
|
|
7461
7389
|
.md\:btn-xs {
|
|
7462
7390
|
@media (width >= 48rem) {
|
|
7463
7391
|
@layer daisyui.l1.l2 {
|
|
@@ -9152,37 +9080,6 @@
|
|
|
9152
9080
|
font-family: var(--font-mono);
|
|
9153
9081
|
line-height: 1;
|
|
9154
9082
|
}
|
|
9155
|
-
.caps-led {
|
|
9156
|
-
display: inline-flex;
|
|
9157
|
-
align-items: center;
|
|
9158
|
-
gap: var(--spacing-xs);
|
|
9159
|
-
color: var(--marginalia-ink);
|
|
9160
|
-
font-size: 0.75rem;
|
|
9161
|
-
}
|
|
9162
|
-
.caps-led-bead {
|
|
9163
|
-
width: 0.625rem;
|
|
9164
|
-
height: 0.625rem;
|
|
9165
|
-
flex: none;
|
|
9166
|
-
border: var(--card-rule-width) solid var(--card-rule);
|
|
9167
|
-
border-radius: var(--radius-selector);
|
|
9168
|
-
background: var(--caps-led-color);
|
|
9169
|
-
box-shadow: 0 0 0 var(--card-rule-width) var(--caps-led-color);
|
|
9170
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
9171
|
-
box-shadow: 0 0 0 var(--card-rule-width) color-mix(in srgb, var(--caps-led-color) 36%, transparent);
|
|
9172
|
-
}
|
|
9173
|
-
}
|
|
9174
|
-
.caps-led[data-tone="spore"] {
|
|
9175
|
-
--caps-led-color: var(--growth-spore);
|
|
9176
|
-
}
|
|
9177
|
-
.caps-led[data-tone="mycelium"] {
|
|
9178
|
-
--caps-led-color: var(--growth-mycelium);
|
|
9179
|
-
}
|
|
9180
|
-
.caps-led[data-tone="fruiting"] {
|
|
9181
|
-
--caps-led-color: var(--growth-fruiting);
|
|
9182
|
-
}
|
|
9183
|
-
.caps-led[data-active="false"] {
|
|
9184
|
-
--caps-led-color: var(--marginalia-ink);
|
|
9185
|
-
}
|
|
9186
9083
|
.caps-cartridge-label {
|
|
9187
9084
|
display: grid;
|
|
9188
9085
|
justify-items: center;
|
|
@@ -14075,6 +13972,88 @@
|
|
|
14075
13972
|
min-height: 44px;
|
|
14076
13973
|
}
|
|
14077
13974
|
}
|
|
13975
|
+
.caps-window-header:has(> .caps-window-crumbs) {
|
|
13976
|
+
min-height: 40px;
|
|
13977
|
+
}
|
|
13978
|
+
.caps-window-crumbs {
|
|
13979
|
+
display: flex;
|
|
13980
|
+
align-items: center;
|
|
13981
|
+
min-width: 0;
|
|
13982
|
+
flex-shrink: 1;
|
|
13983
|
+
font-family: var(--caps-label-font, inherit);
|
|
13984
|
+
font-size: var(--text-sm);
|
|
13985
|
+
font-weight: 500;
|
|
13986
|
+
}
|
|
13987
|
+
.caps-window-crumb-item[data-caps-crumb-position="current"] {
|
|
13988
|
+
font-weight: 600;
|
|
13989
|
+
}
|
|
13990
|
+
.caps-window-crumb-item {
|
|
13991
|
+
display: flex;
|
|
13992
|
+
align-items: center;
|
|
13993
|
+
min-width: 0;
|
|
13994
|
+
}
|
|
13995
|
+
.caps-window-crumb-separator {
|
|
13996
|
+
padding-inline: var(--spacing-2xs);
|
|
13997
|
+
color: currentColor;
|
|
13998
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
13999
|
+
color: color-mix(in srgb, currentColor 50%, transparent);
|
|
14000
|
+
}
|
|
14001
|
+
}
|
|
14002
|
+
.caps-window-crumb {
|
|
14003
|
+
min-width: 0;
|
|
14004
|
+
font-size: inherit;
|
|
14005
|
+
font-weight: inherit;
|
|
14006
|
+
gap: var(--spacing-2xs);
|
|
14007
|
+
}
|
|
14008
|
+
.caps-window-crumb-label {
|
|
14009
|
+
overflow: hidden;
|
|
14010
|
+
text-overflow: ellipsis;
|
|
14011
|
+
white-space: nowrap;
|
|
14012
|
+
}
|
|
14013
|
+
.caps-window-crumb-item[data-caps-crumb-position="current"] .caps-window-crumb {
|
|
14014
|
+
padding-inline: var(--spacing-xs);
|
|
14015
|
+
}
|
|
14016
|
+
.caps-window-crumbs:has(+ .caps-window-title) {
|
|
14017
|
+
max-width: 70%;
|
|
14018
|
+
}
|
|
14019
|
+
.caps-window-statuses {
|
|
14020
|
+
display: flex;
|
|
14021
|
+
align-items: center;
|
|
14022
|
+
gap: var(--spacing-sm);
|
|
14023
|
+
flex-shrink: 0;
|
|
14024
|
+
padding-inline-end: var(--spacing-sm);
|
|
14025
|
+
font-size: var(--text-sm);
|
|
14026
|
+
color: currentColor;
|
|
14027
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
14028
|
+
color: color-mix(in srgb, currentColor 65%, transparent);
|
|
14029
|
+
}
|
|
14030
|
+
}
|
|
14031
|
+
.caps-window-commands {
|
|
14032
|
+
display: flex;
|
|
14033
|
+
align-items: center;
|
|
14034
|
+
gap: var(--spacing-2xs);
|
|
14035
|
+
flex-shrink: 0;
|
|
14036
|
+
}
|
|
14037
|
+
@media (max-width: 640px) {
|
|
14038
|
+
.caps-window-statuses {
|
|
14039
|
+
gap: var(--spacing-2xs);
|
|
14040
|
+
padding-inline-end: var(--spacing-2xs);
|
|
14041
|
+
}
|
|
14042
|
+
.caps-window-status > span {
|
|
14043
|
+
position: absolute;
|
|
14044
|
+
width: 1px;
|
|
14045
|
+
height: 1px;
|
|
14046
|
+
overflow: hidden;
|
|
14047
|
+
clip-path: inset(50%);
|
|
14048
|
+
white-space: nowrap;
|
|
14049
|
+
}
|
|
14050
|
+
.caps-window-crumb-item[data-caps-crumb-position="ancestor"] {
|
|
14051
|
+
display: none;
|
|
14052
|
+
}
|
|
14053
|
+
.caps-window-crumb-item[data-caps-crumb-position="parent"] .caps-window-crumb-separator {
|
|
14054
|
+
display: none;
|
|
14055
|
+
}
|
|
14056
|
+
}
|
|
14078
14057
|
.caps-window-body {
|
|
14079
14058
|
flex: 1;
|
|
14080
14059
|
min-height: 0;
|
|
@@ -15112,9 +15091,6 @@
|
|
|
15112
15091
|
font-size: var(--text-xs);
|
|
15113
15092
|
white-space: nowrap;
|
|
15114
15093
|
}
|
|
15115
|
-
.caps-status-compact .caps-status-bead {
|
|
15116
|
-
transform: scale(0.6);
|
|
15117
|
-
}
|
|
15118
15094
|
.caps-action-row-trailing {
|
|
15119
15095
|
display: flex;
|
|
15120
15096
|
width: 6rem;
|
package/docs/api/README.md
CHANGED
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
| [components/undo-action](components/undo-action.md) | - |
|
|
119
119
|
| [components/undo-toast](components/undo-toast.md) | - |
|
|
120
120
|
| [components/visually-hidden](components/visually-hidden.md) | - |
|
|
121
|
+
| [components/window-crumbs](components/window-crumbs.md) | - |
|
|
121
122
|
| [components/window](components/window.md) | - |
|
|
122
123
|
| [components/wizard](components/wizard.md) | - |
|
|
123
124
|
| [enhance/cigarette-card](enhance/cigarette-card.md) | - |
|
|
@@ -26,35 +26,6 @@ A visible keyboard key from a shortcut or instruction.
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
-
### StatusLedTone
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
type StatusLedTone = "spore" | "mycelium" | "fruiting";
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Semantic accent pigments for the static status light.
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
### StatusLedProps
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
type StatusLedProps = object &
|
|
43
|
-
Omit<HTMLAttributes<HTMLSpanElement>, "children">;
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Props accepted by StatusLed.
|
|
47
|
-
|
|
48
|
-
#### Type Declaration
|
|
49
|
-
|
|
50
|
-
| Name | Type |
|
|
51
|
-
| -------- | --------------------------------- |
|
|
52
|
-
| `active` | `boolean` |
|
|
53
|
-
| `label` | `string` |
|
|
54
|
-
| `tone?` | [`StatusLedTone`](#statusledtone) |
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
29
|
### CartridgeLabelProps
|
|
59
30
|
|
|
60
31
|
```ts
|
|
@@ -115,26 +86,6 @@ The shared field-guide treatment for a keyboard key.
|
|
|
115
86
|
|
|
116
87
|
---
|
|
117
88
|
|
|
118
|
-
### StatusLed()
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
|
-
function StatusLed(__namedParameters): Element;
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
A static, labelled indicator using the current field-guide pigment tokens.
|
|
125
|
-
|
|
126
|
-
#### Parameters
|
|
127
|
-
|
|
128
|
-
| Parameter | Type |
|
|
129
|
-
| ------------------- | ----------------------------------- |
|
|
130
|
-
| `__namedParameters` | [`StatusLedProps`](#statusledprops) |
|
|
131
|
-
|
|
132
|
-
#### Returns
|
|
133
|
-
|
|
134
|
-
`Element`
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
89
|
### CartridgeLabel()
|
|
139
90
|
|
|
140
91
|
```ts
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<!-- Generated by Botanical API docs (TypeDoc + typedoc-plugin-markdown). Do not edit. -->
|
|
2
|
+
|
|
3
|
+
[**@fungi.computer/caps**](../README.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
[@fungi.computer/caps](../README.md) / components/window-crumbs
|
|
8
|
+
|
|
9
|
+
# components/window-crumbs
|
|
10
|
+
|
|
11
|
+
## Type Aliases
|
|
12
|
+
|
|
13
|
+
### WindowCrumb
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
type WindowCrumb = object;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
One place in the content's path; `siblings` makes it a switcher.
|
|
20
|
+
|
|
21
|
+
#### Properties
|
|
22
|
+
|
|
23
|
+
##### id
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
id: string;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
##### label
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
label: string;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
##### siblings?
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
optional siblings?: readonly object[];
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Other places at this level, offered by a ▾ switcher on the crumb.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### WindowCommand
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
type WindowCommand = object;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A command the content offers in the header; the content owns its effect.
|
|
52
|
+
|
|
53
|
+
#### Properties
|
|
54
|
+
|
|
55
|
+
##### id
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
id: string;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
##### label
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
label: string;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
##### variant?
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
optional variant?: "primary" | "outline" | "ghost";
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Which Caps button a primary command wears; default `outline`.
|
|
74
|
+
|
|
75
|
+
##### tone?
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
optional tone?: "destructive";
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
A menu item that removes or discards something.
|
|
82
|
+
|
|
83
|
+
##### disabled?
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
optional disabled?: boolean;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### WindowStatus
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
type WindowStatus = object;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
A quiet fact about the view: muted text led by the Caps status bead.
|
|
98
|
+
|
|
99
|
+
#### Properties
|
|
100
|
+
|
|
101
|
+
##### id
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
id: string;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
##### label
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
label: string;
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
##### tone?
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
optional tone?: Extract<StatusTone, "neutral" | "info" | "warning" | "success" | "error">;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Functions
|
|
120
|
+
|
|
121
|
+
### WindowCrumbs()
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
function WindowCrumbs(__namedParameters): Element;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The path as a trail; on a phone only `‹ parent › current` remain.
|
|
128
|
+
|
|
129
|
+
#### Parameters
|
|
130
|
+
|
|
131
|
+
| Parameter | Type |
|
|
132
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `__namedParameters` | \{ `crumbs`: readonly [`WindowCrumb`](#windowcrumb)[]; `popover?`: `HTMLElement` \| `null`; `onCrumb?`: (`crumbId`, `siblingId?`) => `void`; \} |
|
|
134
|
+
| `__namedParameters.crumbs` | readonly [`WindowCrumb`](#windowcrumb)[] |
|
|
135
|
+
| `__namedParameters.popover?` | `HTMLElement` \| `null` |
|
|
136
|
+
| `__namedParameters.onCrumb?` | (`crumbId`, `siblingId?`) => `void` |
|
|
137
|
+
|
|
138
|
+
#### Returns
|
|
139
|
+
|
|
140
|
+
`Element`
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### WindowCommands()
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
function WindowCommands(__namedParameters): Element | null;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Primary commands as buttons, the rest behind a ⋯ menu.
|
|
151
|
+
|
|
152
|
+
#### Parameters
|
|
153
|
+
|
|
154
|
+
| Parameter | Type |
|
|
155
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
156
|
+
| `__namedParameters` | \{ `primary?`: readonly [`WindowCommand`](#windowcommand)[]; `menu?`: readonly [`WindowCommand`](#windowcommand)[]; `popover?`: `HTMLElement` \| `null`; `onAction?`: (`id`) => `void`; \} |
|
|
157
|
+
| `__namedParameters.primary?` | readonly [`WindowCommand`](#windowcommand)[] |
|
|
158
|
+
| `__namedParameters.menu?` | readonly [`WindowCommand`](#windowcommand)[] |
|
|
159
|
+
| `__namedParameters.popover?` | `HTMLElement` \| `null` |
|
|
160
|
+
| `__namedParameters.onAction?` | (`id`) => `void` |
|
|
161
|
+
|
|
162
|
+
#### Returns
|
|
163
|
+
|
|
164
|
+
`Element` \| `null`
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### WindowStatuses()
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
function WindowStatuses(__namedParameters): Element | null;
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Muted state text with the Caps status bead; hidden on a phone.
|
|
175
|
+
|
|
176
|
+
#### Parameters
|
|
177
|
+
|
|
178
|
+
| Parameter | Type |
|
|
179
|
+
| -------------------------- | ----------------------------------------------------------- |
|
|
180
|
+
| `__namedParameters` | \{ `status`: readonly [`WindowStatus`](#windowstatus)[]; \} |
|
|
181
|
+
| `__namedParameters.status` | readonly [`WindowStatus`](#windowstatus)[] |
|
|
182
|
+
|
|
183
|
+
#### Returns
|
|
184
|
+
|
|
185
|
+
`Element` \| `null`
|
|
@@ -82,15 +82,22 @@ Supply handleProps only when the title is an interactive layout handle.
|
|
|
82
82
|
|
|
83
83
|
#### Type Declaration
|
|
84
84
|
|
|
85
|
-
| Name | Type | Description
|
|
86
|
-
| -------------- | --------------------------------------------------------------------------------- |
|
|
87
|
-
| `title` | `string` | -
|
|
88
|
-
| `subtitle?` | `string` | Where the content is now, shown after the title and truncated first.
|
|
89
|
-
| `icon?` | `ReactNode` | -
|
|
90
|
-
| `actions?` | `ReactNode` | -
|
|
91
|
-
| `back?` | [`WindowBackAction`](#windowbackaction) | Present only while there is somewhere to go back to.
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
85
|
+
| Name | Type | Description |
|
|
86
|
+
| -------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| `title` | `string` | - |
|
|
88
|
+
| `subtitle?` | `string` | Where the content is now, shown after the title and truncated first. |
|
|
89
|
+
| `icon?` | `ReactNode` | - |
|
|
90
|
+
| `actions?` | `ReactNode` | - |
|
|
91
|
+
| `back?` | [`WindowBackAction`](#windowbackaction) | Present only while there is somewhere to go back to. |
|
|
92
|
+
| `crumbs?` | readonly [`WindowCrumb`](window-crumbs.md#windowcrumb)[] | The content's path, shown in place of the title and subtitle. The last crumb is the current place; earlier ones are links, and a crumb with siblings is a switcher. |
|
|
93
|
+
| `onCrumb()?` | (`crumbId`, `siblingId?`) => `void` | Selected crumb, or the sibling chosen from a switcher crumb. |
|
|
94
|
+
| `status?` | readonly [`WindowStatus`](window-crumbs.md#windowstatus)[] | Quiet facts about the view, such as "Valid" or "Saved", with beads. |
|
|
95
|
+
| `primary?` | readonly [`WindowCommand`](window-crumbs.md#windowcommand)[] | The view's own commands, as buttons before the window controls. |
|
|
96
|
+
| `menu?` | readonly [`WindowCommand`](window-crumbs.md#windowcommand)[] | Less common commands, behind a ⋯ menu. |
|
|
97
|
+
| `onAction()?` | (`id`) => `void` | A primary or menu command the person used, by its id. |
|
|
98
|
+
| `popover?` | `HTMLElement` \| `null` | Where header menus open; the host's popover layer. |
|
|
99
|
+
| `density?` | `"regular"` \| `"compact"` | `compact` is a slim bar for tiled layouts; touch targets keep 44px. |
|
|
100
|
+
| `handleProps?` | `Omit`\<`ButtonHTMLAttributes`\<`HTMLButtonElement`\>, `"children"` \| `"type"`\> | - |
|
|
94
101
|
|
|
95
102
|
---
|
|
96
103
|
|
|
@@ -289,3 +296,21 @@ Consistent icons, ordering and accessible names for host-owned commands.
|
|
|
289
296
|
#### Returns
|
|
290
297
|
|
|
291
298
|
`Element`
|
|
299
|
+
|
|
300
|
+
## References
|
|
301
|
+
|
|
302
|
+
### WindowCommand
|
|
303
|
+
|
|
304
|
+
Re-exports [WindowCommand](window-crumbs.md#windowcommand)
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
### WindowCrumb
|
|
309
|
+
|
|
310
|
+
Re-exports [WindowCrumb](window-crumbs.md#windowcrumb)
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### WindowStatus
|
|
315
|
+
|
|
316
|
+
Re-exports [WindowStatus](window-crumbs.md#windowstatus)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fungi.computer/caps",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"author": "Levi <levi@technomancy.dev>",
|
|
5
5
|
"description": "Product-neutral React primitives and blocks for Fungi interfaces",
|
|
6
6
|
"type": "module",
|
|
@@ -469,8 +469,8 @@
|
|
|
469
469
|
},
|
|
470
470
|
"devDependencies": {
|
|
471
471
|
"@fungi.computer/maosaico": "0.0.1",
|
|
472
|
-
"@fungi.computer/sprite": "0.0
|
|
473
|
-
"@fungi.computer/sprite-zdog": "0.0
|
|
472
|
+
"@fungi.computer/sprite": "0.1.0",
|
|
473
|
+
"@fungi.computer/sprite-zdog": "0.1.0",
|
|
474
474
|
"@ladle/react": "5.1.1",
|
|
475
475
|
"@tailwindcss/cli": "4.2.1",
|
|
476
476
|
"@types/react": "18.3.3",
|
|
@@ -487,7 +487,7 @@
|
|
|
487
487
|
"typescript": "5.9.3",
|
|
488
488
|
"vite": "8.2.2",
|
|
489
489
|
"vitest": "4.1.11",
|
|
490
|
-
"@fungi.computer/sigils": "0.0
|
|
490
|
+
"@fungi.computer/sigils": "0.1.0",
|
|
491
491
|
"postcss-value-parser": "4.2.0",
|
|
492
492
|
"@tanstack/react-form": "1.33.2",
|
|
493
493
|
"effect": "4.0.2",
|
package/src/styles.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Caps' single style entry. Stipe owns Daisy's theme engine and all shared
|
|
3
|
-
* semantic values; Caps only asks Tailwind to scan its
|
|
3
|
+
* semantic values; Caps only asks Tailwind to scan its own components.
|
|
4
4
|
* Caps owns reading rhythm through Tailwind Typography's `prose`.
|
|
5
5
|
*/
|
|
6
6
|
@import "@fungi.computer/stipe/styles.css";
|
|
@@ -9,7 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
@plugin "@tailwindcss/typography";
|
|
11
11
|
|
|
12
|
+
/* In the workspace Tailwind scans the component source; published, this
|
|
13
|
+
* entry ships without it, and the compiled components in dist carry the same
|
|
14
|
+
* class names. Tests ship in neither, so neither build styles them. */
|
|
12
15
|
@source "./";
|
|
16
|
+
@source "../dist/**/*.js";
|
|
17
|
+
@source not "./**/*.test.tsx";
|
|
18
|
+
@source not "./test-utils";
|
|
13
19
|
|
|
14
20
|
/* The themed brand fill stays untouched. Link ink and focus rings keep the
|
|
15
21
|
primary's hue and chroma and only move lightness toward the ink side of
|
|
@@ -411,35 +417,6 @@
|
|
|
411
417
|
font-family: var(--font-mono);
|
|
412
418
|
line-height: 1;
|
|
413
419
|
}
|
|
414
|
-
.caps-led {
|
|
415
|
-
display: inline-flex;
|
|
416
|
-
align-items: center;
|
|
417
|
-
gap: var(--spacing-xs);
|
|
418
|
-
color: var(--marginalia-ink);
|
|
419
|
-
font-size: 0.75rem;
|
|
420
|
-
}
|
|
421
|
-
.caps-led-bead {
|
|
422
|
-
width: 0.625rem;
|
|
423
|
-
height: 0.625rem;
|
|
424
|
-
flex: none;
|
|
425
|
-
border: var(--card-rule-width) solid var(--card-rule);
|
|
426
|
-
border-radius: var(--radius-selector);
|
|
427
|
-
background: var(--caps-led-color);
|
|
428
|
-
box-shadow: 0 0 0 var(--card-rule-width)
|
|
429
|
-
color-mix(in srgb, var(--caps-led-color) 36%, transparent);
|
|
430
|
-
}
|
|
431
|
-
.caps-led[data-tone="spore"] {
|
|
432
|
-
--caps-led-color: var(--growth-spore);
|
|
433
|
-
}
|
|
434
|
-
.caps-led[data-tone="mycelium"] {
|
|
435
|
-
--caps-led-color: var(--growth-mycelium);
|
|
436
|
-
}
|
|
437
|
-
.caps-led[data-tone="fruiting"] {
|
|
438
|
-
--caps-led-color: var(--growth-fruiting);
|
|
439
|
-
}
|
|
440
|
-
.caps-led[data-active="false"] {
|
|
441
|
-
--caps-led-color: var(--marginalia-ink);
|
|
442
|
-
}
|
|
443
420
|
.caps-cartridge-label {
|
|
444
421
|
display: grid;
|
|
445
422
|
justify-items: center;
|
|
@@ -5849,6 +5826,90 @@
|
|
|
5849
5826
|
min-height: 44px;
|
|
5850
5827
|
}
|
|
5851
5828
|
}
|
|
5829
|
+
/* The path trail: flat text crumbs on the header face, no new colours.
|
|
5830
|
+
A header that carries a path reads at body size, one control high. */
|
|
5831
|
+
.caps-window-header:has(> .caps-window-crumbs) {
|
|
5832
|
+
min-height: 40px;
|
|
5833
|
+
}
|
|
5834
|
+
.caps-window-crumbs {
|
|
5835
|
+
display: flex;
|
|
5836
|
+
align-items: center;
|
|
5837
|
+
min-width: 0;
|
|
5838
|
+
flex-shrink: 1;
|
|
5839
|
+
font-family: var(--caps-label-font, inherit);
|
|
5840
|
+
font-size: var(--text-sm);
|
|
5841
|
+
font-weight: 500;
|
|
5842
|
+
}
|
|
5843
|
+
.caps-window-crumb-item[data-caps-crumb-position="current"] {
|
|
5844
|
+
font-weight: 600;
|
|
5845
|
+
}
|
|
5846
|
+
.caps-window-crumb-item {
|
|
5847
|
+
display: flex;
|
|
5848
|
+
align-items: center;
|
|
5849
|
+
min-width: 0;
|
|
5850
|
+
}
|
|
5851
|
+
.caps-window-crumb-separator {
|
|
5852
|
+
padding-inline: var(--spacing-2xs);
|
|
5853
|
+
color: color-mix(in srgb, currentColor 50%, transparent);
|
|
5854
|
+
}
|
|
5855
|
+
.caps-window-crumb {
|
|
5856
|
+
min-width: 0;
|
|
5857
|
+
font-size: inherit;
|
|
5858
|
+
font-weight: inherit;
|
|
5859
|
+
gap: var(--spacing-2xs);
|
|
5860
|
+
}
|
|
5861
|
+
.caps-window-crumb-label {
|
|
5862
|
+
overflow: hidden;
|
|
5863
|
+
text-overflow: ellipsis;
|
|
5864
|
+
white-space: nowrap;
|
|
5865
|
+
}
|
|
5866
|
+
.caps-window-crumb-item[data-caps-crumb-position="current"]
|
|
5867
|
+
.caps-window-crumb {
|
|
5868
|
+
padding-inline: var(--spacing-xs);
|
|
5869
|
+
}
|
|
5870
|
+
/* The trail keeps its width beside the empty title handle (a zero basis),
|
|
5871
|
+
and gives way, crumb labels first, only when the row runs out of room. */
|
|
5872
|
+
.caps-window-crumbs:has(+ .caps-window-title) {
|
|
5873
|
+
max-width: 70%;
|
|
5874
|
+
}
|
|
5875
|
+
.caps-window-statuses {
|
|
5876
|
+
display: flex;
|
|
5877
|
+
align-items: center;
|
|
5878
|
+
gap: var(--spacing-sm);
|
|
5879
|
+
flex-shrink: 0;
|
|
5880
|
+
padding-inline-end: var(--spacing-sm);
|
|
5881
|
+
font-size: var(--text-sm);
|
|
5882
|
+
color: color-mix(in srgb, currentColor 65%, transparent);
|
|
5883
|
+
}
|
|
5884
|
+
.caps-window-commands {
|
|
5885
|
+
display: flex;
|
|
5886
|
+
align-items: center;
|
|
5887
|
+
gap: var(--spacing-2xs);
|
|
5888
|
+
flex-shrink: 0;
|
|
5889
|
+
}
|
|
5890
|
+
/* A phone keeps `‹ parent › current`; the rest of the trail is a tap away on Back. */
|
|
5891
|
+
@media (max-width: 640px) {
|
|
5892
|
+
/* State shrinks to its beads; the words stay for screen readers and a long press. */
|
|
5893
|
+
.caps-window-statuses {
|
|
5894
|
+
gap: var(--spacing-2xs);
|
|
5895
|
+
padding-inline-end: var(--spacing-2xs);
|
|
5896
|
+
}
|
|
5897
|
+
.caps-window-status > span {
|
|
5898
|
+
position: absolute;
|
|
5899
|
+
width: 1px;
|
|
5900
|
+
height: 1px;
|
|
5901
|
+
overflow: hidden;
|
|
5902
|
+
clip-path: inset(50%);
|
|
5903
|
+
white-space: nowrap;
|
|
5904
|
+
}
|
|
5905
|
+
.caps-window-crumb-item[data-caps-crumb-position="ancestor"] {
|
|
5906
|
+
display: none;
|
|
5907
|
+
}
|
|
5908
|
+
.caps-window-crumb-item[data-caps-crumb-position="parent"]
|
|
5909
|
+
.caps-window-crumb-separator {
|
|
5910
|
+
display: none;
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5852
5913
|
.caps-window-body {
|
|
5853
5914
|
flex: 1;
|
|
5854
5915
|
min-height: 0;
|
|
@@ -7063,9 +7124,6 @@
|
|
|
7063
7124
|
font-size: var(--text-xs);
|
|
7064
7125
|
white-space: nowrap;
|
|
7065
7126
|
}
|
|
7066
|
-
.caps-status-compact .caps-status-bead {
|
|
7067
|
-
transform: scale(0.6);
|
|
7068
|
-
}
|
|
7069
7127
|
.caps-action-row-trailing {
|
|
7070
7128
|
display: flex;
|
|
7071
7129
|
width: 6rem;
|