@cueplusplus/ui 0.20.1 → 0.21.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/CHANGELOG.md +115 -0
- package/dist/chat/agent-pile.js +2 -2
- package/dist/chrome/_status-bar.js +1 -1
- package/dist/chrome/app-shell.js +1 -1
- package/dist/chrome/navigation-menu.js +3 -3
- package/dist/chrome/row.js +1 -1
- package/dist/chrome/scroll-area.js +1 -1
- package/dist/chrome/section-header.js +1 -1
- package/dist/chrome/segmented-control.js +1 -1
- package/dist/chrome/tabs.js +1 -1
- package/dist/chrome/toolbar.js +1 -1
- package/dist/chrome/tree.js +3 -3
- package/dist/date/calendar.js +3 -3
- package/dist/dmx/channel-matrix.js +1 -1
- package/dist/dmx/universe-grid.js +1 -1
- package/dist/elements/agent-handoff.js +3 -3
- package/dist/elements/agent-plan.js +1 -1
- package/dist/elements/agent-status.js +1 -1
- package/dist/elements/agent-surface.d.ts +9 -6
- package/dist/elements/agent-surface.js +3 -2
- package/dist/elements/approval-card.d.ts +29 -3
- package/dist/elements/approval-card.js +18 -8
- package/dist/elements/artifact-card.js +2 -2
- package/dist/elements/canvas-split.js +3 -3
- package/dist/elements/chart.js +1 -1
- package/dist/elements/chat-panel.js +2 -2
- package/dist/elements/checkpoint-history.js +1 -1
- package/dist/elements/code-diff.js +1 -1
- package/dist/elements/code-runner.js +1 -1
- package/dist/elements/composer.d.ts +8 -2
- package/dist/elements/composer.js +20 -12
- package/dist/elements/computer-use.js +2 -2
- package/dist/elements/confidence-marker.js +1 -1
- package/dist/elements/connection-state.js +2 -2
- package/dist/elements/context-breakdown.js +1 -1
- package/dist/elements/context-usage.js +1 -1
- package/dist/elements/conversation-search.js +2 -2
- package/dist/elements/cost-meter.js +1 -1
- package/dist/elements/data-table.js +1 -1
- package/dist/elements/diagram.js +1 -1
- package/dist/elements/draft-restore.js +2 -2
- package/dist/elements/edit-message.js +1 -1
- package/dist/elements/elements.css +134 -0
- package/dist/elements/elicitation-form.d.ts +8 -1
- package/dist/elements/elicitation-form.js +24 -16
- package/dist/elements/empty-state.js +3 -3
- package/dist/elements/error-state.js +2 -2
- package/dist/elements/event-feed.d.ts +88 -0
- package/dist/elements/event-feed.js +190 -0
- package/dist/elements/feedback-dialog.js +2 -2
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/flow-graph.js +2 -2
- package/dist/elements/generative.css +13 -0
- package/dist/elements/generative.d.ts +8 -6
- package/dist/elements/generative.js +4 -3
- package/dist/elements/image-generation.js +2 -2
- package/dist/elements/index.d.ts +7 -3
- package/dist/elements/index.js +5 -2
- package/dist/elements/inline-citation.js +1 -1
- package/dist/elements/job-progress.js +1 -1
- package/dist/elements/launcher-bubble.js +3 -3
- package/dist/elements/loading-state.js +1 -1
- package/dist/elements/map-answer.js +1 -1
- package/dist/elements/math-block.js +1 -1
- package/dist/elements/mcp-server-panel.js +3 -3
- package/dist/elements/memory-chips.js +1 -1
- package/dist/elements/message-attachment.js +3 -3
- package/dist/elements/message-branches.js +1 -1
- package/dist/elements/message-pair.js +2 -2
- package/dist/elements/message-queue.js +1 -1
- package/dist/elements/message-timing.js +1 -1
- package/dist/elements/mobile-composer.js +1 -1
- package/dist/elements/model-picker.js +1 -1
- package/dist/elements/number-ticker.js +1 -1
- package/dist/elements/onboarding.js +3 -3
- package/dist/elements/permission-grant.js +3 -3
- package/dist/elements/prompt-library.js +1 -1
- package/dist/elements/question-card.d.ts +50 -0
- package/dist/elements/question-card.js +117 -0
- package/dist/elements/queue-dock.js +1 -1
- package/dist/elements/quota-banner.js +1 -1
- package/dist/elements/quote-reply.js +3 -3
- package/dist/elements/read-aloud.js +2 -2
- package/dist/elements/reasoning-effort.js +2 -2
- package/dist/elements/reasoning-panel.js +3 -3
- package/dist/elements/recommendation-card.js +2 -2
- package/dist/elements/regenerate-menu.js +1 -1
- package/dist/elements/request-delivery.js +31 -0
- package/dist/elements/research-report.js +1 -1
- package/dist/elements/retrieval-chunks.js +3 -3
- package/dist/elements/revert-dock.js +1 -1
- package/dist/elements/reviewable-diff.js +4 -4
- package/dist/elements/schedule-card.js +2 -2
- package/dist/elements/score-breakdown.js +2 -2
- package/dist/elements/scroll-anchor.js +2 -2
- package/dist/elements/settings-panel.js +3 -3
- package/dist/elements/sources.js +1 -1
- package/dist/elements/spec-sheet.js +1 -1
- package/dist/elements/stopped-run.js +2 -2
- package/dist/elements/streaming-text.js +2 -2
- package/dist/elements/subagent-list.js +4 -4
- package/dist/elements/suggestions.js +1 -1
- package/dist/elements/surfaces.d.ts +6 -6
- package/dist/elements/surfaces.js +7 -7
- package/dist/elements/tail-status.js +1 -1
- package/dist/elements/terminal-block.js +1 -1
- package/dist/elements/thinking-indicator.js +1 -1
- package/dist/elements/timeline.js +1 -1
- package/dist/elements/todo-list.js +1 -1
- package/dist/elements/tool-call.d.ts +18 -2
- package/dist/elements/tool-call.js +20 -11
- package/dist/elements/tool-error.js +2 -2
- package/dist/elements/tool-group.d.ts +11 -3
- package/dist/elements/tool-group.js +62 -38
- package/dist/elements/tool-state.d.ts +13 -0
- package/dist/elements/tool-state.js +44 -0
- package/dist/elements/tool-timeline.js +2 -2
- package/dist/elements/trace-waterfall.js +2 -2
- package/dist/elements/unsupported-request-card.d.ts +31 -0
- package/dist/elements/unsupported-request-card.js +80 -0
- package/dist/elements/vocabulary.d.ts +50 -1
- package/dist/elements/vocabulary.js +50 -1
- package/dist/elements/voice-conversation.js +5 -5
- package/dist/elements/web-preview.js +1 -1
- package/dist/elements/web-search.js +2 -2
- package/dist/elements/work-collapse.js +1 -1
- package/dist/forms/file-upload.js +1 -1
- package/dist/forms/rating.js +1 -1
- package/dist/forms/select.js +1 -1
- package/dist/forms/switch.js +2 -2
- package/dist/forms/toggle.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/instruments/_ledger-disclosure.js +1 -1
- package/dist/instruments/data-tree.js +1 -1
- package/dist/instruments/meter.js +1 -1
- package/dist/instruments/seam-grid.js +1 -1
- package/dist/layout/_collapse.js +3 -3
- package/dist/layout/bento.js +1 -1
- package/dist/layout/breadcrumb.js +1 -1
- package/dist/layout/carousel.js +1 -1
- package/dist/layout/collapsible.js +1 -1
- package/dist/layout/index.d.ts +3 -1
- package/dist/layout/index.js +3 -1
- package/dist/layout/item.js +1 -1
- package/dist/layout/link.js +1 -1
- package/dist/layout/pagination.js +1 -1
- package/dist/layout/pane-seam.d.ts +75 -0
- package/dist/layout/pane-seam.js +127 -0
- package/dist/layout/resizable.d.ts +2 -1
- package/dist/layout/resizable.js +3 -2
- package/dist/layout/sidebar.js +3 -3
- package/dist/layout/split-pane.d.ts +74 -0
- package/dist/layout/split-pane.js +135 -0
- package/dist/layout/stepper.js +1 -1
- package/dist/lib/auto-grow.js +42 -0
- package/dist/overlays/_surface.d.ts +4 -4
- package/dist/overlays/_surface.js +4 -4
- package/dist/overlays/drawer.js +1 -1
- package/dist/overlays/menubar.js +1 -1
- package/dist/overlays/sheet.js +1 -1
- package/dist/overlays/toast.js +1 -1
- package/dist/primitives/button.js +1 -1
- package/dist/primitives/chip.d.ts +1 -1
- package/dist/system/agent-skin.d.ts +11 -5
- package/manifest/components/agent-surface.json +5 -4
- package/manifest/components/approval-card.json +45 -7
- package/manifest/components/composer-input.json +16 -2
- package/manifest/components/elicitation-form.json +16 -1
- package/manifest/components/event-feed.json +171 -0
- package/manifest/components/event-row.json +116 -0
- package/manifest/components/generative-ui.json +4 -3
- package/manifest/components/pane-seam.json +171 -0
- package/manifest/components/question-card.json +157 -0
- package/manifest/components/resizable.json +1 -1
- package/manifest/components/split-pane.json +224 -0
- package/manifest/components/tool-call.json +46 -2
- package/manifest/components/tool-group.json +15 -3
- package/manifest/components/unsupported-request-card.json +117 -0
- package/manifest/fixtures.json +16 -0
- package/manifest/manifest.json +88 -10
- package/manifest/tokens.json +9 -2
- package/package.json +2 -2
package/dist/layout/_collapse.js
CHANGED
|
@@ -30,7 +30,7 @@ import { QUIET_INK } from "../system/_style-quiet-ink.js";
|
|
|
30
30
|
* size and leading and the hover ground, and lane R6.Z its weight (falling back
|
|
31
31
|
* through the label weight to the `400` it drew), the same way.
|
|
32
32
|
*/
|
|
33
|
-
const collapseTriggerClasses = `group/collapse flex ${COLLAPSE_HEIGHT} w-full cursor-pointer items-center ${COLLAPSE_GAP} ${COLLAPSE_PAD} ${COLLAPSE_RADIUS} text-left ${CONTROL_FACE} ${COLLAPSE_SIZE} ${COLLAPSE_LEADING} ${QUIET_INK} ${COLLAPSE_WEIGHT} transition-colors duration-
|
|
33
|
+
const collapseTriggerClasses = `group/collapse flex ${COLLAPSE_HEIGHT} w-full cursor-pointer items-center ${COLLAPSE_GAP} ${COLLAPSE_PAD} ${COLLAPSE_RADIUS} text-left ${CONTROL_FACE} ${COLLAPSE_SIZE} ${COLLAPSE_LEADING} ${QUIET_INK} ${COLLAPSE_WEIGHT} transition-colors duration-fast outline-none select-none ${COLLAPSE_HOVER_INK} ${COLLAPSE_HOVER_GROUND} ${COLLAPSE_FOCUS_RING} ${COLLAPSE_OPEN_INK} data-[disabled]:pointer-events-none data-[disabled]:opacity-40`;
|
|
34
34
|
/** The label between the summary row's gutters — truncates, never wraps. */
|
|
35
35
|
const collapseLabelClasses = "min-w-0 flex-1 truncate";
|
|
36
36
|
/**
|
|
@@ -39,7 +39,7 @@ const collapseLabelClasses = "min-w-0 flex-1 truncate";
|
|
|
39
39
|
* Its size and ink read the style set (round 6, lane R6.Z), falling back to
|
|
40
40
|
* the `icon-sm` and `fg-subtle` it drew.
|
|
41
41
|
*/
|
|
42
|
-
const collapseCaretClasses = `${COLLAPSE_CARET_SIZE} shrink-0 ${COLLAPSE_CARET_INK} ${COLLAPSE_CARET_OPACITY} transition-transform duration-
|
|
42
|
+
const collapseCaretClasses = `${COLLAPSE_CARET_SIZE} shrink-0 ${COLLAPSE_CARET_INK} ${COLLAPSE_CARET_OPACITY} transition-transform duration-fast ease-out group-data-[panel-open]/collapse:rotate-90 motion-reduce:transition-none`;
|
|
43
43
|
/**
|
|
44
44
|
* Enter/exit for a panel, animated from the height Base UI measures for us.
|
|
45
45
|
*
|
|
@@ -48,7 +48,7 @@ const collapseCaretClasses = `${COLLAPSE_CARET_SIZE} shrink-0 ${COLLAPSE_CARET_I
|
|
|
48
48
|
* make the panel jump at both ends. The body lives one element in — see
|
|
49
49
|
* {@link collapsePanelBodyClasses}.
|
|
50
50
|
*/
|
|
51
|
-
const PANEL_MOTION = "overflow-hidden transition-[height] duration-
|
|
51
|
+
const PANEL_MOTION = "overflow-hidden transition-[height] duration-fast ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0";
|
|
52
52
|
/** {@link PANEL_MOTION} against the accordion's measured height. */
|
|
53
53
|
const accordionPanelClasses = `${PANEL_MOTION} h-(--accordion-panel-height)`;
|
|
54
54
|
/** {@link PANEL_MOTION} against the collapsible's measured height. */
|
package/dist/layout/bento.js
CHANGED
|
@@ -58,7 +58,7 @@ const MIN_HEIGHT_CLASSES = {
|
|
|
58
58
|
/** The tile surface, shared by both variants so a link tile is not a second look. */
|
|
59
59
|
const TILE_CLASS = "flex flex-col gap-(--cue-space-3) overflow-hidden rounded-surface border border-border bg-surface-1 p-(--cue-space-5)";
|
|
60
60
|
/** What a link tile adds: the hover tint and the focus ring the whole surface takes. */
|
|
61
|
-
const LINK_TILE_CLASS = `no-underline transition-colors duration-
|
|
61
|
+
const LINK_TILE_CLASS = `no-underline transition-colors duration-fast outline-none hover:bg-surface-2 ${FOCUS_RING_OFFSET_2}`;
|
|
62
62
|
/** The host elements a link tile may not contain, plus anything carrying `tabIndex`. */
|
|
63
63
|
const INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
64
64
|
"a",
|
|
@@ -11,7 +11,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
* face, hover ink and the trail's gap read the style set (round 6, spec §11,
|
|
12
12
|
* lane R6.Z: `_style-crumb.ts`), each falling back to what it drew.
|
|
13
13
|
*/
|
|
14
|
-
const CRUMB_LINK_CLASSES = `cursor-pointer rounded-control border-0 bg-transparent p-0 ${CRUMB_FACE} ${CRUMB_SIZE} ${CRUMB_INK} no-underline transition-colors duration-
|
|
14
|
+
const CRUMB_LINK_CLASSES = `cursor-pointer rounded-control border-0 bg-transparent p-0 ${CRUMB_FACE} ${CRUMB_SIZE} ${CRUMB_INK} no-underline transition-colors duration-fast outline-none ${CRUMB_HOVER_INK} ${FOCUS_RING_OFFSET_2}`;
|
|
15
15
|
/**
|
|
16
16
|
* Where you are, and how to get back.
|
|
17
17
|
*
|
package/dist/layout/carousel.js
CHANGED
|
@@ -203,7 +203,7 @@ const Carousel = {
|
|
|
203
203
|
"aria-label": dotLabel(index),
|
|
204
204
|
"aria-current": index === selectedIndex ? "true" : void 0,
|
|
205
205
|
onClick: () => scrollTo(index),
|
|
206
|
-
className: cn(`size-[0.25rem] cursor-pointer rounded-full border-0 p-0 transition-colors duration-
|
|
206
|
+
className: cn(`size-[0.25rem] cursor-pointer rounded-full border-0 p-0 transition-colors duration-fast outline-none ${FOCUS_RING_OFFSET_2} motion-reduce:transition-none`, index === selectedIndex ? "bg-accent" : "bg-border-strong")
|
|
207
207
|
}, index))
|
|
208
208
|
});
|
|
209
209
|
})
|
|
@@ -99,7 +99,7 @@ const Disclosure = React.forwardRef(function Disclosure({ className, label = "ad
|
|
|
99
99
|
children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
|
|
100
100
|
ref,
|
|
101
101
|
"data-slot": "disclosure-trigger",
|
|
102
|
-
className: cn(`group/disclosure inline-flex cursor-pointer items-center gap-(--cue-space-2) self-start border-0 bg-transparent p-0 py-(--cue-space-2) text-(length:--cue-text-label) ${DISCLOSURE_INK} ${DISCLOSURE_WEIGHT} transition-colors duration-
|
|
102
|
+
className: cn(`group/disclosure inline-flex cursor-pointer items-center gap-(--cue-space-2) self-start border-0 bg-transparent p-0 py-(--cue-space-2) text-(length:--cue-text-label) ${DISCLOSURE_INK} ${DISCLOSURE_WEIGHT} transition-colors duration-fast outline-none select-none ${DISCLOSURE_HOVER_INK} ${FOCUS_RING_OFFSET_2}`, LABEL_FACE, LABEL_TRACKING, LABEL_CASE, className),
|
|
103
103
|
...elementProps,
|
|
104
104
|
children: [
|
|
105
105
|
/* @__PURE__ */ jsx("span", {
|
package/dist/layout/index.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { Accordion, AccordionHeaderProps, AccordionItemProps, AccordionPanelProp
|
|
|
5
5
|
import { SpaceStep, Stack, StackAlign, StackJustify, StackProps } from "./stack.js";
|
|
6
6
|
import { Bento, BentoColumns, BentoMinHeight, BentoRootProps, BentoRowSpan, BentoRows, BentoSpan, BentoTileProps } from "./bento.js";
|
|
7
7
|
import { Breadcrumb, BreadcrumbItem, BreadcrumbProps } from "./breadcrumb.js";
|
|
8
|
+
import { PaneSeam, PaneSeamEdge, PaneSeamOrientation, PaneSeamProps } from "./pane-seam.js";
|
|
8
9
|
import { Reveal, RevealProps } from "./reveal.js";
|
|
10
|
+
import { SplitPane, SplitPaneLayout, SplitPaneProps } from "./split-pane.js";
|
|
9
11
|
import { Card, CardContentProps, CardDescriptionProps, CardFooterProps, CardHeaderProps, CardRootProps, CardTitleProps } from "./card.js";
|
|
10
12
|
import { Collapsible, CollapsiblePanelProps, CollapsibleRootProps, CollapsibleTriggerProps, Disclosure, DisclosureProps } from "./collapsible.js";
|
|
11
13
|
import { DescriptionList, DescriptionListDetailProps, DescriptionListLayout, DescriptionListRootProps, DescriptionListTermProps } from "./description-list.js";
|
|
@@ -17,4 +19,4 @@ import { Pagination, PaginationEntry, PaginationProps, paginationRange } from ".
|
|
|
17
19
|
import { Preview, PreviewAxis, PreviewPresentation, PreviewProps, PreviewResizeOptions } from "./preview.js";
|
|
18
20
|
import { Stepper, StepperProps, StepperState, StepperStep } from "./stepper.js";
|
|
19
21
|
import { Timeline, TimelineContentProps, TimelineItemProps, TimelineMarkerProps, TimelineRootProps } from "./timeline.js";
|
|
20
|
-
export { Accordion, type AccordionHeaderProps, type AccordionItemProps, type AccordionPanelProps, type AccordionRootProps, type AccordionTriggerProps, Bento, type BentoColumns, type BentoMinHeight, type BentoRootProps, type BentoRowSpan, type BentoRows, type BentoSpan, type BentoTileProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Card, type CardContentProps, type CardDescriptionProps, type CardFooterProps, type CardHeaderProps, type CardRootProps, type CardTitleProps, Collapsible, type CollapsiblePanelProps, type CollapsibleRootProps, type CollapsibleTriggerProps, Container, type ContainerProps, type ContainerWidth, DescriptionList, type DescriptionListDetailProps, type DescriptionListLayout, type DescriptionListRootProps, type DescriptionListTermProps, Disclosure, type DisclosureProps, Frames, type FramesHeight, type FramesItemProps, type FramesLayout, type FramesRootProps, Grid, type GridColumns, type GridProps, Item, type ItemProps, Link, type LinkProps, Pagination, type PaginationEntry, type PaginationProps, Preview, type PreviewAxis, type PreviewGround, type PreviewPresentation, type PreviewProps, type PreviewResizeOptions, Reveal, type RevealProps, Sidebar, type SidebarItemProps, type SidebarRailProps, type SidebarRootProps, type SidebarSectionProps, type SpaceStep, Stack, type StackAlign, type StackJustify, type StackProps, Stepper, type StepperProps, type StepperState, type StepperStep, Timeline, type TimelineContentProps, type TimelineItemProps, type TimelineMarkerProps, type TimelineRootProps, itemVariants, linkVariants, paginationRange, useSidebar };
|
|
22
|
+
export { Accordion, type AccordionHeaderProps, type AccordionItemProps, type AccordionPanelProps, type AccordionRootProps, type AccordionTriggerProps, Bento, type BentoColumns, type BentoMinHeight, type BentoRootProps, type BentoRowSpan, type BentoRows, type BentoSpan, type BentoTileProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Card, type CardContentProps, type CardDescriptionProps, type CardFooterProps, type CardHeaderProps, type CardRootProps, type CardTitleProps, Collapsible, type CollapsiblePanelProps, type CollapsibleRootProps, type CollapsibleTriggerProps, Container, type ContainerProps, type ContainerWidth, DescriptionList, type DescriptionListDetailProps, type DescriptionListLayout, type DescriptionListRootProps, type DescriptionListTermProps, Disclosure, type DisclosureProps, Frames, type FramesHeight, type FramesItemProps, type FramesLayout, type FramesRootProps, Grid, type GridColumns, type GridProps, Item, type ItemProps, Link, type LinkProps, Pagination, type PaginationEntry, type PaginationProps, PaneSeam, type PaneSeamEdge, type PaneSeamOrientation, type PaneSeamProps, Preview, type PreviewAxis, type PreviewGround, type PreviewPresentation, type PreviewProps, type PreviewResizeOptions, Reveal, type RevealProps, Sidebar, type SidebarItemProps, type SidebarRailProps, type SidebarRootProps, type SidebarSectionProps, type SpaceStep, SplitPane, type SplitPaneLayout, type SplitPaneProps, Stack, type StackAlign, type StackJustify, type StackProps, Stepper, type StepperProps, type StepperState, type StepperStep, Timeline, type TimelineContentProps, type TimelineItemProps, type TimelineMarkerProps, type TimelineRootProps, itemVariants, linkVariants, paginationRange, useSidebar };
|
package/dist/layout/index.js
CHANGED
|
@@ -5,7 +5,9 @@ import { Accordion } from "./accordion.js";
|
|
|
5
5
|
import { Stack } from "./stack.js";
|
|
6
6
|
import { Bento } from "./bento.js";
|
|
7
7
|
import { Breadcrumb } from "./breadcrumb.js";
|
|
8
|
+
import { PaneSeam } from "./pane-seam.js";
|
|
8
9
|
import { Reveal } from "./reveal.js";
|
|
10
|
+
import { SplitPane } from "./split-pane.js";
|
|
9
11
|
import { Card } from "./card.js";
|
|
10
12
|
import { Collapsible, Disclosure } from "./collapsible.js";
|
|
11
13
|
import { DescriptionList } from "./description-list.js";
|
|
@@ -16,4 +18,4 @@ import { Pagination, paginationRange } from "./pagination.js";
|
|
|
16
18
|
import { Preview } from "./preview.js";
|
|
17
19
|
import { Stepper } from "./stepper.js";
|
|
18
20
|
import { Timeline } from "./timeline.js";
|
|
19
|
-
export { Accordion, Bento, Breadcrumb, Card, Collapsible, Container, DescriptionList, Disclosure, Frames, Grid, Item, Link, Pagination, Preview, Reveal, Sidebar, Stack, Stepper, Timeline, itemVariants, linkVariants, paginationRange, useSidebar };
|
|
21
|
+
export { Accordion, Bento, Breadcrumb, Card, Collapsible, Container, DescriptionList, Disclosure, Frames, Grid, Item, Link, Pagination, PaneSeam, Preview, Reveal, Sidebar, SplitPane, Stack, Stepper, Timeline, itemVariants, linkVariants, paginationRange, useSidebar };
|
package/dist/layout/item.js
CHANGED
|
@@ -24,7 +24,7 @@ const itemVariants = cva("flex w-full items-center gap-(--cue-space-4) rounded-c
|
|
|
24
24
|
soft: "bg-surface-2"
|
|
25
25
|
},
|
|
26
26
|
interactive: {
|
|
27
|
-
true: `cursor-pointer transition-colors duration-
|
|
27
|
+
true: `cursor-pointer transition-colors duration-fast outline-none hover:bg-surface-2 ${FOCUS_RING} disabled:pointer-events-none disabled:opacity-40`,
|
|
28
28
|
false: ""
|
|
29
29
|
}
|
|
30
30
|
},
|
package/dist/layout/link.js
CHANGED
|
@@ -38,7 +38,7 @@ import { useRender } from "@base-ui/react/use-render";
|
|
|
38
38
|
* @example
|
|
39
39
|
* <a className={linkVariants({ variant: "underline" })} href="/docs">Docs</a>
|
|
40
40
|
*/
|
|
41
|
-
const linkVariants = cva(`cursor-pointer rounded-control ${CONTROL_FACE} ${LINK_WEIGHT} transition-colors duration-
|
|
41
|
+
const linkVariants = cva(`cursor-pointer rounded-control ${CONTROL_FACE} ${LINK_WEIGHT} transition-colors duration-fast outline-none ${HOVER_INK} ${LINK_FOCUS_RING}`, {
|
|
42
42
|
variants: { variant: {
|
|
43
43
|
plain: `no-underline ${LINK_INK}`,
|
|
44
44
|
underline: `underline ${INLINE_LINK_INK} ${LINK_RULE} ${LINK_OFFSET} ${HOVER_RULE}`
|
|
@@ -62,7 +62,7 @@ function paginationRange(page, pageCount, siblingCount = 1) {
|
|
|
62
62
|
return typeof before === "number" && typeof after === "number" && after - before === 2 ? before + 1 : entry;
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
|
-
const PAGE_BUTTON_CLASSES = `inline-flex h-control-sm min-w-control-sm cursor-pointer items-center justify-center rounded-control border-0 bg-transparent px-(--cue-space-2) ${CONTROL_FACE} font-[number:var(--cue-style-control-weight,inherit)] text-(length:--cue-text-ui) tabular-nums ${GHOST_INK} transition-colors duration-
|
|
65
|
+
const PAGE_BUTTON_CLASSES = `inline-flex h-control-sm min-w-control-sm cursor-pointer items-center justify-center rounded-control border-0 bg-transparent px-(--cue-space-2) ${CONTROL_FACE} font-[number:var(--cue-style-control-weight,inherit)] text-(length:--cue-text-ui) tabular-nums ${GHOST_INK} transition-colors duration-fast outline-none select-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET}`;
|
|
66
66
|
/**
|
|
67
67
|
* The pager: previous, a window of page numbers, next.
|
|
68
68
|
*
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/layout/pane-seam.d.ts
|
|
3
|
+
/** Which way the seam runs: between side-by-side panes, or between stacked ones. */
|
|
4
|
+
type PaneSeamOrientation = "vertical" | "horizontal";
|
|
5
|
+
/** Which side of the seam the pane it resizes sits on. */
|
|
6
|
+
type PaneSeamEdge = "start" | "end";
|
|
7
|
+
interface PaneSeamProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children" | "defaultValue" | "onChange"> {
|
|
8
|
+
/**
|
|
9
|
+
* The name a screen reader lists the seam by: `Resize the inspector`. Required,
|
|
10
|
+
* because a separator nobody can name is one a keyboard user cannot find.
|
|
11
|
+
*/
|
|
12
|
+
label: string;
|
|
13
|
+
/** The pane's size in pixels, when the caller holds it. */
|
|
14
|
+
value?: number;
|
|
15
|
+
/** The size to start from when nothing controls it. Defaults to `min`. */
|
|
16
|
+
defaultValue?: number;
|
|
17
|
+
/** Called with every new size, already clamped to `min` and `max`. */
|
|
18
|
+
onValueChange?: (value: number) => void;
|
|
19
|
+
/** The smallest the pane may become, in pixels. */
|
|
20
|
+
min: number;
|
|
21
|
+
/** The largest the pane may become, in pixels. */
|
|
22
|
+
max: number;
|
|
23
|
+
/** How far one arrow press moves the seam, in pixels. Defaults to 16. */
|
|
24
|
+
step?: number;
|
|
25
|
+
/** How far Shift with an arrow moves it, in pixels. Defaults to 64. */
|
|
26
|
+
largeStep?: number;
|
|
27
|
+
/**
|
|
28
|
+
* `vertical`, the default, divides side-by-side panes and resizes a width;
|
|
29
|
+
* `horizontal` divides stacked panes and resizes a height.
|
|
30
|
+
*/
|
|
31
|
+
orientation?: PaneSeamOrientation;
|
|
32
|
+
/**
|
|
33
|
+
* Where the pane sits: `start`, the default, before the seam (left, or top),
|
|
34
|
+
* or `end`, after it. Moving the seam away from the pane grows it. A vertical
|
|
35
|
+
* seam reads the document's direction, so `start` is the right-hand pane
|
|
36
|
+
* under `dir="rtl"`.
|
|
37
|
+
*/
|
|
38
|
+
edge?: PaneSeamEdge;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* One accessible seam that resizes one pane, with no dependency (cue-ui-55).
|
|
42
|
+
*
|
|
43
|
+
* `Resizable` divides a frame between panel groups on `react-resizable-panels`,
|
|
44
|
+
* an optional peer. Most consoles need one thing less: a side pane whose edge a
|
|
45
|
+
* reader can drag, and move from the keyboard. This is that edge, and nothing
|
|
46
|
+
* else. It owns a number, not a layout: apply `value` to the pane's width (or
|
|
47
|
+
* height) and the seam supplies the rest.
|
|
48
|
+
*
|
|
49
|
+
* The rest is the part a hand-written separator forgets. It is a focusable
|
|
50
|
+
* `role="separator"` that says its size and its bounds (`aria-valuenow`,
|
|
51
|
+
* `-valuemin`, `-valuemax`), so the width is spoken as well as draggable. The
|
|
52
|
+
* arrows move it by `step`, Shift by `largeStep`, and Home and End jump to the
|
|
53
|
+
* bounds. Every value is clamped, so the pane can never squeeze its neighbour
|
|
54
|
+
* out of use.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const [width, setWidth] = React.useState(320);
|
|
58
|
+
* const inspector = { width };
|
|
59
|
+
*
|
|
60
|
+
* <div className="flex">
|
|
61
|
+
* <main className="min-w-0 flex-1">…</main>
|
|
62
|
+
* <PaneSeam
|
|
63
|
+
* label="Resize the inspector"
|
|
64
|
+
* edge="end"
|
|
65
|
+
* min={240}
|
|
66
|
+
* max={640}
|
|
67
|
+
* value={width}
|
|
68
|
+
* onValueChange={setWidth}
|
|
69
|
+
* />
|
|
70
|
+
* <aside style={inspector}>…</aside>
|
|
71
|
+
* </div>
|
|
72
|
+
*/
|
|
73
|
+
declare const PaneSeam: React.ForwardRefExoticComponent<PaneSeamProps & React.RefAttributes<HTMLDivElement>>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { PaneSeam, PaneSeamEdge, PaneSeamOrientation, PaneSeamProps };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { FOCUS_RING_INSET_2 } from "../system/_focus-ring.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region src/layout/pane-seam.tsx
|
|
7
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
8
|
+
/**
|
|
9
|
+
* One accessible seam that resizes one pane, with no dependency (cue-ui-55).
|
|
10
|
+
*
|
|
11
|
+
* `Resizable` divides a frame between panel groups on `react-resizable-panels`,
|
|
12
|
+
* an optional peer. Most consoles need one thing less: a side pane whose edge a
|
|
13
|
+
* reader can drag, and move from the keyboard. This is that edge, and nothing
|
|
14
|
+
* else. It owns a number, not a layout: apply `value` to the pane's width (or
|
|
15
|
+
* height) and the seam supplies the rest.
|
|
16
|
+
*
|
|
17
|
+
* The rest is the part a hand-written separator forgets. It is a focusable
|
|
18
|
+
* `role="separator"` that says its size and its bounds (`aria-valuenow`,
|
|
19
|
+
* `-valuemin`, `-valuemax`), so the width is spoken as well as draggable. The
|
|
20
|
+
* arrows move it by `step`, Shift by `largeStep`, and Home and End jump to the
|
|
21
|
+
* bounds. Every value is clamped, so the pane can never squeeze its neighbour
|
|
22
|
+
* out of use.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const [width, setWidth] = React.useState(320);
|
|
26
|
+
* const inspector = { width };
|
|
27
|
+
*
|
|
28
|
+
* <div className="flex">
|
|
29
|
+
* <main className="min-w-0 flex-1">…</main>
|
|
30
|
+
* <PaneSeam
|
|
31
|
+
* label="Resize the inspector"
|
|
32
|
+
* edge="end"
|
|
33
|
+
* min={240}
|
|
34
|
+
* max={640}
|
|
35
|
+
* value={width}
|
|
36
|
+
* onValueChange={setWidth}
|
|
37
|
+
* />
|
|
38
|
+
* <aside style={inspector}>…</aside>
|
|
39
|
+
* </div>
|
|
40
|
+
*/
|
|
41
|
+
const PaneSeam = React.forwardRef(function PaneSeam({ label, value, defaultValue, onValueChange, min, max, step = 16, largeStep = 64, orientation = "vertical", edge = "start", className, onKeyDown, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, ...elementProps }, ref) {
|
|
42
|
+
const [own, setOwn] = React.useState(() => clamp(defaultValue ?? min, min, max));
|
|
43
|
+
const current = clamp(value ?? own, min, max);
|
|
44
|
+
const drag = React.useRef(null);
|
|
45
|
+
const [dragging, setDragging] = React.useState(false);
|
|
46
|
+
const commit = (next) => {
|
|
47
|
+
const clamped = clamp(Math.round(next), min, max);
|
|
48
|
+
if (clamped === current) return;
|
|
49
|
+
if (value === void 0) setOwn(clamped);
|
|
50
|
+
onValueChange?.(clamped);
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Which physical direction grows the pane: +1 is right (or down), -1 left (or
|
|
54
|
+
* up). Read at the moment of input, from the nearest `dir`, so a seam moved
|
|
55
|
+
* into a right-to-left subtree follows it.
|
|
56
|
+
*/
|
|
57
|
+
const growth = (element) => {
|
|
58
|
+
const away = edge === "start" ? 1 : -1;
|
|
59
|
+
if (orientation === "horizontal") return away;
|
|
60
|
+
return element.closest("[dir]")?.getAttribute("dir") === "rtl" ? -away : away;
|
|
61
|
+
};
|
|
62
|
+
return /* @__PURE__ */ jsx("div", {
|
|
63
|
+
ref,
|
|
64
|
+
role: "separator",
|
|
65
|
+
tabIndex: 0,
|
|
66
|
+
"aria-label": label,
|
|
67
|
+
"aria-orientation": orientation,
|
|
68
|
+
"aria-valuenow": current,
|
|
69
|
+
"aria-valuemin": min,
|
|
70
|
+
"aria-valuemax": max,
|
|
71
|
+
"data-slot": "pane-seam",
|
|
72
|
+
"data-orientation": orientation,
|
|
73
|
+
"data-edge": edge,
|
|
74
|
+
"data-dragging": dragging || void 0,
|
|
75
|
+
className: cn(`relative shrink-0 touch-none bg-border transition-colors duration-fast outline-none select-none hover:bg-accent data-dragging:bg-accent focus-visible:bg-accent ${FOCUS_RING_INSET_2} motion-reduce:transition-none`, orientation === "vertical" ? "min-h-8 w-[0.25rem] cursor-col-resize self-stretch" : "h-[0.25rem] w-full cursor-row-resize", className),
|
|
76
|
+
onKeyDown: (event) => {
|
|
77
|
+
onKeyDown?.(event);
|
|
78
|
+
if (event.defaultPrevented) return;
|
|
79
|
+
const by = event.shiftKey ? largeStep : step;
|
|
80
|
+
const forward = orientation === "vertical" ? "ArrowRight" : "ArrowDown";
|
|
81
|
+
const back = orientation === "vertical" ? "ArrowLeft" : "ArrowUp";
|
|
82
|
+
let next;
|
|
83
|
+
if (event.key === forward) next = current + by * growth(event.currentTarget);
|
|
84
|
+
else if (event.key === back) next = current - by * growth(event.currentTarget);
|
|
85
|
+
else if (event.key === "Home") next = min;
|
|
86
|
+
else if (event.key === "End") next = max;
|
|
87
|
+
if (next === void 0) return;
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
commit(next);
|
|
90
|
+
},
|
|
91
|
+
onPointerDown: (event) => {
|
|
92
|
+
onPointerDown?.(event);
|
|
93
|
+
if (event.defaultPrevented || event.button !== 0) return;
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
96
|
+
drag.current = {
|
|
97
|
+
pointer: event.pointerId,
|
|
98
|
+
from: orientation === "vertical" ? event.clientX : event.clientY,
|
|
99
|
+
size: current,
|
|
100
|
+
grow: growth(event.currentTarget)
|
|
101
|
+
};
|
|
102
|
+
setDragging(true);
|
|
103
|
+
},
|
|
104
|
+
onPointerMove: (event) => {
|
|
105
|
+
onPointerMove?.(event);
|
|
106
|
+
const held = drag.current;
|
|
107
|
+
if (held === null || held.pointer !== event.pointerId) return;
|
|
108
|
+
const at = orientation === "vertical" ? event.clientX : event.clientY;
|
|
109
|
+
commit(held.size + (at - held.from) * held.grow);
|
|
110
|
+
},
|
|
111
|
+
onPointerUp: (event) => {
|
|
112
|
+
onPointerUp?.(event);
|
|
113
|
+
if (drag.current?.pointer !== event.pointerId) return;
|
|
114
|
+
drag.current = null;
|
|
115
|
+
setDragging(false);
|
|
116
|
+
event.currentTarget.releasePointerCapture?.(event.pointerId);
|
|
117
|
+
},
|
|
118
|
+
onPointerCancel: (event) => {
|
|
119
|
+
onPointerCancel?.(event);
|
|
120
|
+
drag.current = null;
|
|
121
|
+
setDragging(false);
|
|
122
|
+
},
|
|
123
|
+
...elementProps
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
//#endregion
|
|
127
|
+
export { PaneSeam };
|
|
@@ -20,7 +20,8 @@ interface ResizableHandleProps extends React.ComponentProps<typeof Separator> {
|
|
|
20
20
|
*
|
|
21
21
|
* `react-resizable-panels` is an **optional peer**: import this from
|
|
22
22
|
* `@cueplusplus/ui/layout/resizable`, never from the core barrel, so an app
|
|
23
|
-
* with no split view never installs one.
|
|
23
|
+
* with no split view never installs one. For one seam beside one pane,
|
|
24
|
+
* `PaneSeam` in the core barrel needs no peer at all.
|
|
24
25
|
*
|
|
25
26
|
* @example
|
|
26
27
|
* <Resizable.Group orientation="horizontal">
|
package/dist/layout/resizable.js
CHANGED
|
@@ -18,7 +18,8 @@ const ResizableOrientationContext = React.createContext("horizontal");
|
|
|
18
18
|
*
|
|
19
19
|
* `react-resizable-panels` is an **optional peer**: import this from
|
|
20
20
|
* `@cueplusplus/ui/layout/resizable`, never from the core barrel, so an app
|
|
21
|
-
* with no split view never installs one.
|
|
21
|
+
* with no split view never installs one. For one seam beside one pane,
|
|
22
|
+
* `PaneSeam` in the core barrel needs no peer at all.
|
|
22
23
|
*
|
|
23
24
|
* @example
|
|
24
25
|
* <Resizable.Group orientation="horizontal">
|
|
@@ -55,7 +56,7 @@ const Resizable = {
|
|
|
55
56
|
return /* @__PURE__ */ jsxs(Separator, {
|
|
56
57
|
elementRef: elementRef ?? ref,
|
|
57
58
|
"data-slot": "resizable-handle",
|
|
58
|
-
className: cn(`relative flex items-center justify-center bg-border transition-colors duration-
|
|
59
|
+
className: cn(`relative flex items-center justify-center bg-border transition-colors duration-fast outline-none data-[separator=hover]:bg-accent data-[separator=active]:bg-accent data-[separator=focus]:bg-accent ${FOCUS_RING_INSET_2} data-[separator=disabled]:opacity-40 motion-reduce:transition-none`, orientation === "vertical" ? "h-[0.25rem] w-full" : "w-[0.25rem] self-stretch", className),
|
|
59
60
|
...elementProps,
|
|
60
61
|
children: [withGrip ? /* @__PURE__ */ jsx("span", {
|
|
61
62
|
"data-slot": "resizable-grip",
|
package/dist/layout/sidebar.js
CHANGED
|
@@ -100,7 +100,7 @@ const SidebarRoot = React.forwardRef(function SidebarRoot({ className, landmark
|
|
|
100
100
|
ref,
|
|
101
101
|
"data-slot": "sidebar",
|
|
102
102
|
"data-collapsed": collapsed ? "" : void 0,
|
|
103
|
-
className: cn("group/sidebar relative flex h-full min-h-0 shrink-0 flex-col border-r border-border bg-surface-1 transition-[width] duration-
|
|
103
|
+
className: cn("group/sidebar relative flex h-full min-h-0 shrink-0 flex-col border-r border-border bg-surface-1 transition-[width] duration-fast ease-out motion-reduce:transition-none", "[counter-reset:cue-rail]", className),
|
|
104
104
|
style: {
|
|
105
105
|
width: collapsed ? collapsedWidth : width,
|
|
106
106
|
...style
|
|
@@ -161,7 +161,7 @@ const Sidebar = {
|
|
|
161
161
|
"data-slot": "sidebar-item",
|
|
162
162
|
"data-active": active ? "" : void 0,
|
|
163
163
|
"aria-current": active ? "page" : void 0,
|
|
164
|
-
className: cn(`relative flex h-control-md w-full cursor-pointer items-center gap-(--cue-space-3) rounded-control border-0 bg-transparent px-(--cue-space-3) text-left font-mono text-(length:--cue-text-ui) no-underline transition-colors duration-
|
|
164
|
+
className: cn(`relative flex h-control-md w-full cursor-pointer items-center gap-(--cue-space-3) rounded-control border-0 bg-transparent px-(--cue-space-3) text-left font-mono text-(length:--cue-text-ui) no-underline transition-colors duration-fast outline-none select-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET} ${NAV_ITEM} ${RAIL_COUNTER} disabled:pointer-events-none disabled:opacity-40`, className),
|
|
165
165
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
166
166
|
Icon === void 0 ? null : /* @__PURE__ */ jsx(Icon, {
|
|
167
167
|
"aria-hidden": "true",
|
|
@@ -193,7 +193,7 @@ const Sidebar = {
|
|
|
193
193
|
onClick?.(event);
|
|
194
194
|
if (!event.defaultPrevented) toggle();
|
|
195
195
|
},
|
|
196
|
-
className: cn(`absolute inset-y-0 right-0 w-[0.25rem] translate-x-1/2 cursor-ew-resize border-0 bg-transparent p-0 transition-colors duration-
|
|
196
|
+
className: cn(`absolute inset-y-0 right-0 w-[0.25rem] translate-x-1/2 cursor-ew-resize border-0 bg-transparent p-0 transition-colors duration-fast outline-none hover:bg-accent ${FOCUS_RING_INSET_2} motion-reduce:transition-none`, className),
|
|
197
197
|
...elementProps
|
|
198
198
|
});
|
|
199
199
|
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/layout/split-pane.d.ts
|
|
3
|
+
/** Whether the panes sit side by side or stack into a drill-in. */
|
|
4
|
+
type SplitPaneLayout = "split" | "stacked";
|
|
5
|
+
interface SplitPaneProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children"> {
|
|
6
|
+
/** The master: a list of rows, a tree, a feed. It owns its own keyboard. */
|
|
7
|
+
list: React.ReactNode;
|
|
8
|
+
/** The detail for whatever the list has selected. */
|
|
9
|
+
detail: React.ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* What the list is, in a word or two: `events`. It names the seam (`Resize
|
|
12
|
+
* the events`), the Back button (`Back to events`) and the fold (`Hide
|
|
13
|
+
* events`), so each control says what it acts on.
|
|
14
|
+
*/
|
|
15
|
+
listLabel: string;
|
|
16
|
+
/** The list's width in pixels, when the caller holds it. */
|
|
17
|
+
listSize?: number;
|
|
18
|
+
/** The list's starting width when nothing controls it. Defaults to 320. */
|
|
19
|
+
defaultListSize?: number;
|
|
20
|
+
/** Called with every new list width, clamped to `minListSize` and `maxListSize`. */
|
|
21
|
+
onListSizeChange?: (size: number) => void;
|
|
22
|
+
/** The narrowest the list may be dragged, in pixels. Defaults to 200. */
|
|
23
|
+
minListSize?: number;
|
|
24
|
+
/** The widest the list may be dragged, in pixels. Defaults to 480. */
|
|
25
|
+
maxListSize?: number;
|
|
26
|
+
/**
|
|
27
|
+
* The frame width, in pixels, below which the panes stack. Unset, they stack
|
|
28
|
+
* once the detail would be narrower than twice the list.
|
|
29
|
+
*/
|
|
30
|
+
stackBelow?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Which pane shows when stacked: the detail when `true`, the list when not.
|
|
33
|
+
* Side by side, both always show.
|
|
34
|
+
*/
|
|
35
|
+
detailOpen?: boolean;
|
|
36
|
+
/** Called by the stacked detail's Back button and by Escape inside it. */
|
|
37
|
+
onBack?: () => void;
|
|
38
|
+
/** Whether the list can fold to a thin tab while side by side. */
|
|
39
|
+
collapsible?: boolean;
|
|
40
|
+
/** Whether the list is folded, when the caller holds it. */
|
|
41
|
+
collapsed?: boolean;
|
|
42
|
+
/** Whether the list starts folded when nothing controls it. */
|
|
43
|
+
defaultCollapsed?: boolean;
|
|
44
|
+
/** Called when the list folds (`true`) or opens (`false`). */
|
|
45
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A list beside its detail, which stacks into a drill-in when narrow (#102).
|
|
49
|
+
*
|
|
50
|
+
* The master-detail opinion on top of {@link PaneSeam}. Side by side while
|
|
51
|
+
* the detail would be at least twice the list, measured on the frame rather
|
|
52
|
+
* than the window, so it works in any column. Below that it stacks the way a
|
|
53
|
+
* phone does: the list, or the detail with a Back button, and Escape inside the
|
|
54
|
+
* detail goes back too. `detailOpen` says which shows; selection and the list's
|
|
55
|
+
* own keyboard stay with the list.
|
|
56
|
+
*
|
|
57
|
+
* The frame measures itself with a `ResizeObserver` and draws side by side
|
|
58
|
+
* until it has, so a server render and a browser without one get the split.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const [selected, setSelected] = React.useState<string | null>(null);
|
|
62
|
+
*
|
|
63
|
+
* <SplitPane
|
|
64
|
+
* listLabel="events"
|
|
65
|
+
* list={<EventFeed events={events} selectedId={selected} onSelect={setSelected} />}
|
|
66
|
+
* detail={selected ? <EventDetail id={selected} /> : <EmptyState title="Pick an event" />}
|
|
67
|
+
* detailOpen={selected !== null}
|
|
68
|
+
* onBack={() => setSelected(null)}
|
|
69
|
+
* collapsible
|
|
70
|
+
* />
|
|
71
|
+
*/
|
|
72
|
+
declare const SplitPane: React.ForwardRefExoticComponent<SplitPaneProps & React.RefAttributes<HTMLDivElement>>;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { SplitPane, SplitPaneLayout, SplitPaneProps };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { FOCUS_RING_INSET_2 } from "../system/_focus-ring.js";
|
|
4
|
+
import { ChevronLeftGlyph, ChevronRightGlyph } from "./_glyphs.js";
|
|
5
|
+
import { PaneSeam } from "./pane-seam.js";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/layout/split-pane.tsx
|
|
9
|
+
/**
|
|
10
|
+
* A list beside its detail, which stacks into a drill-in when narrow (#102).
|
|
11
|
+
*
|
|
12
|
+
* The master-detail opinion on top of {@link PaneSeam}. Side by side while
|
|
13
|
+
* the detail would be at least twice the list, measured on the frame rather
|
|
14
|
+
* than the window, so it works in any column. Below that it stacks the way a
|
|
15
|
+
* phone does: the list, or the detail with a Back button, and Escape inside the
|
|
16
|
+
* detail goes back too. `detailOpen` says which shows; selection and the list's
|
|
17
|
+
* own keyboard stay with the list.
|
|
18
|
+
*
|
|
19
|
+
* The frame measures itself with a `ResizeObserver` and draws side by side
|
|
20
|
+
* until it has, so a server render and a browser without one get the split.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const [selected, setSelected] = React.useState<string | null>(null);
|
|
24
|
+
*
|
|
25
|
+
* <SplitPane
|
|
26
|
+
* listLabel="events"
|
|
27
|
+
* list={<EventFeed events={events} selectedId={selected} onSelect={setSelected} />}
|
|
28
|
+
* detail={selected ? <EventDetail id={selected} /> : <EmptyState title="Pick an event" />}
|
|
29
|
+
* detailOpen={selected !== null}
|
|
30
|
+
* onBack={() => setSelected(null)}
|
|
31
|
+
* collapsible
|
|
32
|
+
* />
|
|
33
|
+
*/
|
|
34
|
+
const SplitPane = React.forwardRef(function SplitPane({ list, detail, listLabel, listSize, defaultListSize = 320, onListSizeChange, minListSize = 200, maxListSize = 480, stackBelow, detailOpen = false, onBack, collapsible = false, collapsed, defaultCollapsed = false, onCollapsedChange, className, ...elementProps }, ref) {
|
|
35
|
+
const node = React.useRef(null);
|
|
36
|
+
const setNode = React.useCallback((element) => {
|
|
37
|
+
node.current = element;
|
|
38
|
+
if (typeof ref === "function") ref(element);
|
|
39
|
+
else if (ref) ref.current = element;
|
|
40
|
+
}, [ref]);
|
|
41
|
+
const [ownSize, setOwnSize] = React.useState(defaultListSize);
|
|
42
|
+
const size = listSize ?? ownSize;
|
|
43
|
+
const [ownCollapsed, setOwnCollapsed] = React.useState(defaultCollapsed);
|
|
44
|
+
const folded = collapsible && (collapsed ?? ownCollapsed);
|
|
45
|
+
const [width, setWidth] = React.useState(null);
|
|
46
|
+
React.useLayoutEffect(() => {
|
|
47
|
+
const element = node.current;
|
|
48
|
+
if (element === null || typeof ResizeObserver === "undefined") return;
|
|
49
|
+
const measure = () => setWidth(element.getBoundingClientRect().width);
|
|
50
|
+
const observer = new ResizeObserver(measure);
|
|
51
|
+
observer.observe(element);
|
|
52
|
+
measure();
|
|
53
|
+
return () => observer.disconnect();
|
|
54
|
+
}, []);
|
|
55
|
+
const threshold = stackBelow ?? size * 3;
|
|
56
|
+
const layout = width !== null && width < threshold ? "stacked" : "split";
|
|
57
|
+
const resize = (next) => {
|
|
58
|
+
if (listSize === void 0) setOwnSize(next);
|
|
59
|
+
onListSizeChange?.(next);
|
|
60
|
+
};
|
|
61
|
+
const fold = (next) => {
|
|
62
|
+
if (collapsed === void 0) setOwnCollapsed(next);
|
|
63
|
+
onCollapsedChange?.(next);
|
|
64
|
+
};
|
|
65
|
+
const quiet = `flex items-center justify-center rounded-control text-fg-muted transition-colors duration-fast outline-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET_2} motion-reduce:transition-none`;
|
|
66
|
+
return /* @__PURE__ */ jsx("div", {
|
|
67
|
+
ref: setNode,
|
|
68
|
+
"data-slot": "split-pane",
|
|
69
|
+
"data-layout": layout,
|
|
70
|
+
"data-detail-open": detailOpen || void 0,
|
|
71
|
+
"data-collapsed": folded || void 0,
|
|
72
|
+
className: cn("flex min-h-0 min-w-0", className),
|
|
73
|
+
...elementProps,
|
|
74
|
+
children: layout === "stacked" ? detailOpen ? /* @__PURE__ */ jsxs("div", {
|
|
75
|
+
"data-slot": "split-pane-detail",
|
|
76
|
+
className: "flex min-h-0 min-w-0 flex-1 flex-col",
|
|
77
|
+
onKeyDown: (event) => {
|
|
78
|
+
if (event.key !== "Escape" || event.defaultPrevented) return;
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
onBack?.();
|
|
81
|
+
},
|
|
82
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
83
|
+
className: "flex shrink-0 items-center border-b border-border px-(--cue-space-2) py-(--cue-space-1)",
|
|
84
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
85
|
+
type: "button",
|
|
86
|
+
onClick: onBack,
|
|
87
|
+
className: cn(quiet, "gap-(--cue-space-1) px-(--cue-space-2) py-(--cue-space-1) text-(length:--cue-text-ui)"),
|
|
88
|
+
children: [/* @__PURE__ */ jsx(ChevronLeftGlyph, { className: "size-3" }), `Back to ${listLabel}`]
|
|
89
|
+
})
|
|
90
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
91
|
+
className: "min-h-0 min-w-0 flex-1",
|
|
92
|
+
children: detail
|
|
93
|
+
})]
|
|
94
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
95
|
+
"data-slot": "split-pane-list",
|
|
96
|
+
className: "min-h-0 min-w-0 flex-1",
|
|
97
|
+
children: list
|
|
98
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [folded ? /* @__PURE__ */ jsxs("button", {
|
|
99
|
+
type: "button",
|
|
100
|
+
"aria-expanded": false,
|
|
101
|
+
onClick: () => fold(false),
|
|
102
|
+
className: cn(quiet, "w-(--cue-space-6) shrink-0 self-stretch rounded-none border-e border-border"),
|
|
103
|
+
children: [/* @__PURE__ */ jsx(ChevronRightGlyph, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
|
|
104
|
+
className: "sr-only",
|
|
105
|
+
children: `Show ${listLabel}`
|
|
106
|
+
})]
|
|
107
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
108
|
+
"data-slot": "split-pane-list",
|
|
109
|
+
className: "relative min-h-0 shrink-0",
|
|
110
|
+
style: { width: size },
|
|
111
|
+
children: [list, collapsible ? /* @__PURE__ */ jsxs("button", {
|
|
112
|
+
type: "button",
|
|
113
|
+
"aria-expanded": true,
|
|
114
|
+
onClick: () => fold(true),
|
|
115
|
+
className: cn(quiet, "absolute end-(--cue-space-1) top-(--cue-space-1) size-(--cue-space-6)"),
|
|
116
|
+
children: [/* @__PURE__ */ jsx(ChevronLeftGlyph, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
|
|
117
|
+
className: "sr-only",
|
|
118
|
+
children: `Hide ${listLabel}`
|
|
119
|
+
})]
|
|
120
|
+
}) : null]
|
|
121
|
+
}), /* @__PURE__ */ jsx(PaneSeam, {
|
|
122
|
+
label: `Resize the ${listLabel}`,
|
|
123
|
+
min: minListSize,
|
|
124
|
+
max: maxListSize,
|
|
125
|
+
value: Math.min(maxListSize, Math.max(minListSize, size)),
|
|
126
|
+
onValueChange: resize
|
|
127
|
+
})] }), /* @__PURE__ */ jsx("div", {
|
|
128
|
+
"data-slot": "split-pane-detail",
|
|
129
|
+
className: "min-h-0 min-w-0 flex-1",
|
|
130
|
+
children: detail
|
|
131
|
+
})] })
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
//#endregion
|
|
135
|
+
export { SplitPane };
|
package/dist/layout/stepper.js
CHANGED
|
@@ -19,7 +19,7 @@ const INDEX_TONES = {
|
|
|
19
19
|
current: "text-accent",
|
|
20
20
|
upcoming: "text-fg-subtle"
|
|
21
21
|
};
|
|
22
|
-
const STEP_BUTTON_CLASSES = `inline-flex cursor-pointer items-center gap-(--cue-space-2) rounded-control border-0 bg-transparent p-0 ${LABEL_FACE} text-[length:inherit] tracking-[inherit] text-inherit ${LABEL_CASE} transition-colors duration-
|
|
22
|
+
const STEP_BUTTON_CLASSES = `inline-flex cursor-pointer items-center gap-(--cue-space-2) rounded-control border-0 bg-transparent p-0 ${LABEL_FACE} text-[length:inherit] tracking-[inherit] text-inherit ${LABEL_CASE} transition-colors duration-fast outline-none hover:text-fg ${FOCUS_RING_OFFSET_2}`;
|
|
23
23
|
/**
|
|
24
24
|
* The step trail: `1 target · 2 preview · 3 apply`.
|
|
25
25
|
*
|