panelui-web 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/card/index.d.ts +107 -0
- package/dist/components/card/index.js +88 -0
- package/dist/components/command-dock/index.d.ts +23 -2
- package/dist/components/command-dock/index.js +22 -2
- package/dist/components/day-grid/index.d.ts +45 -1
- package/dist/components/day-grid/index.js +40 -1
- package/dist/components/feedback/index.d.ts +50 -1
- package/dist/components/feedback/index.js +43 -1
- package/dist/components/flow/index.d.ts +61 -3
- package/dist/components/flow/index.js +65 -9
- package/dist/components/frame/index.d.ts +191 -0
- package/dist/components/frame/index.js +186 -0
- package/dist/components/kpi/index.d.ts +3 -1
- package/dist/components/kpi/index.js +6 -3
- package/dist/components/lane-board/index.d.ts +9 -1
- package/dist/components/lane-board/index.js +8 -1
- package/dist/components/planet/index.d.ts +24 -1
- package/dist/components/planet/index.js +27 -6
- package/dist/components/range-meter/index.d.ts +30 -2
- package/dist/components/range-meter/index.js +27 -2
- package/dist/components/sheet/index.d.ts +148 -0
- package/dist/components/sheet/index.js +330 -0
- package/dist/components/stat/index.d.ts +106 -0
- package/dist/components/stat/index.js +72 -0
- package/dist/components/tilt-card/index.d.ts +23 -2
- package/dist/components/tilt-card/index.js +19 -2
- package/dist/index.d.ts +12 -8
- package/dist/index.js +12 -8
- package/package.json +1 -1
|
@@ -6,7 +6,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
6
6
|
* ```tsx
|
|
7
7
|
* <Flow>
|
|
8
8
|
* <Flow.Background variant="dots" />
|
|
9
|
-
* <Flow.Node id="source" position={{ x: 24, y: 40 }}
|
|
9
|
+
* <Flow.Node id="source" position={{ x: 24, y: 40 }}>
|
|
10
|
+
* <Flow.NodeContent>
|
|
11
|
+
* <Flow.NodeTitle>Source</Flow.NodeTitle>
|
|
12
|
+
* <Flow.NodeDescription>main</Flow.NodeDescription>
|
|
13
|
+
* </Flow.NodeContent>
|
|
14
|
+
* </Flow.Node>
|
|
10
15
|
* <Flow.Node id="build" position={{ x: 24, y: 200 }}>…</Flow.Node>
|
|
11
16
|
* <Flow.Edge from="source" to="build" dashed animated arrow />
|
|
12
17
|
* <Flow.Controls />
|
|
@@ -50,6 +55,15 @@ const flowVariants = tv({
|
|
|
50
55
|
node: 'absolute left-0 top-0 z-10 cursor-grab rounded-2xl border bg-card text-card-foreground shadow-sm outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-ring active:cursor-grabbing',
|
|
51
56
|
group: 'absolute left-0 top-0 cursor-grab rounded-3xl border border-dashed bg-muted/40 p-3 pt-8 outline-none focus-visible:ring-2 focus-visible:ring-ring active:cursor-grabbing',
|
|
52
57
|
groupLabel: 'absolute start-4 top-2.5 text-xs font-medium uppercase tracking-wider text-muted-foreground',
|
|
58
|
+
// What goes inside a node. The padding is on the parts, so a header can
|
|
59
|
+
// draw its rule from edge to edge.
|
|
60
|
+
nodeHeader: 'flex items-center justify-between gap-3 border-b px-4 py-2.5',
|
|
61
|
+
// Inside a group item the item already has its padding.
|
|
62
|
+
nodeContent: 'px-4 py-3 in-data-[slot=flow-group-item]:p-0',
|
|
63
|
+
nodeTitle: 'text-sm font-medium',
|
|
64
|
+
nodeDescription: 'text-sm text-muted-foreground in-data-[slot=flow-group-item]:text-xs',
|
|
65
|
+
nodeBadge: 'rounded-full bg-[color-mix(in_oklab,var(--pu-color)_14%,transparent)] px-2 py-0.5 text-xs font-medium text-[var(--pu-color)]',
|
|
66
|
+
groupItem: 'flex items-center gap-3 rounded-xl border bg-card px-3 py-2.5 [&_svg]:size-4 [&_svg]:text-muted-foreground',
|
|
53
67
|
handle: 'absolute z-20 size-3 -translate-x-1/2 -translate-y-1/2 cursor-crosshair rounded-full border-2 border-background bg-muted-foreground outline-none transition-[scale,background-color] hover:scale-150 hover:bg-foreground focus-visible:ring-2 focus-visible:ring-ring',
|
|
54
68
|
edge: 'pointer-events-none absolute left-0 top-0 size-px overflow-visible text-[var(--pu-color)]',
|
|
55
69
|
controls: 'absolute flex flex-col divide-y divide-border overflow-hidden rounded-xl border bg-card text-card-foreground shadow-sm',
|
|
@@ -58,13 +72,16 @@ const flowVariants = tv({
|
|
|
58
72
|
},
|
|
59
73
|
variants: {
|
|
60
74
|
color: {
|
|
61
|
-
primary: { edge: '[--pu-color:var(--primary)]' },
|
|
62
|
-
info: { edge: '[--pu-color:var(--info)]' },
|
|
63
|
-
success: { edge: '[--pu-color:var(--success)]' },
|
|
64
|
-
warning: { edge: '[--pu-color:var(--warning)]' },
|
|
65
|
-
destructive: { edge: '[--pu-color:var(--destructive)]' },
|
|
75
|
+
primary: { edge: '[--pu-color:var(--primary)]', nodeBadge: '[--pu-color:var(--primary)]' },
|
|
76
|
+
info: { edge: '[--pu-color:var(--info)]', nodeBadge: '[--pu-color:var(--info)]' },
|
|
77
|
+
success: { edge: '[--pu-color:var(--success)]', nodeBadge: '[--pu-color:var(--success)]' },
|
|
78
|
+
warning: { edge: '[--pu-color:var(--warning)]', nodeBadge: '[--pu-color:var(--warning)]' },
|
|
79
|
+
destructive: { edge: '[--pu-color:var(--destructive)]', nodeBadge: '[--pu-color:var(--destructive)]' },
|
|
66
80
|
// The colour of a hairline that is meant to be seen but not looked at.
|
|
67
|
-
muted: {
|
|
81
|
+
muted: {
|
|
82
|
+
edge: '[--pu-color:var(--muted-foreground)]',
|
|
83
|
+
nodeBadge: 'bg-muted text-muted-foreground',
|
|
84
|
+
},
|
|
68
85
|
},
|
|
69
86
|
selected: { true: { node: 'z-20 ring-2 ring-ring' } },
|
|
70
87
|
position: {
|
|
@@ -474,7 +491,7 @@ function useMovable({ id, position, draggable, onPress, onDelete, moveStep, }) {
|
|
|
474
491
|
},
|
|
475
492
|
};
|
|
476
493
|
}
|
|
477
|
-
/** A box on the graph.
|
|
494
|
+
/** A box on the graph. Fill it with the node parts below, or with anything of your own. */
|
|
478
495
|
function FlowNode({ id, position, draggable = true, selected = false, onPress, onDelete, moveStep, className, style, children, ...props }) {
|
|
479
496
|
const movable = useMovable({ id, position, draggable, onPress, onDelete, moveStep });
|
|
480
497
|
return (_jsx(NodeContext.Provider, { value: id, children: _jsx("div", { ref: movable.element, "data-flow-node": id, tabIndex: 0, "aria-roledescription": "Node", "aria-selected": selected || undefined, className: flowVariants({ selected }).node({ className }), style: { ...movable.style, ...style }, ...movable.handlers, ...props, children: children }) }));
|
|
@@ -489,6 +506,39 @@ function FlowGroup({ id, position, draggable = true, selected: _selected, onPres
|
|
|
489
506
|
return (_jsx(NodeContext.Provider, { value: id, children: _jsxs("div", { ref: movable.element, "data-flow-node": id, tabIndex: 0, "aria-roledescription": "Group", className: flowVariants().group({ className }), style: { ...movable.style, ...style }, ...movable.handlers, ...props, children: [label !== undefined ? _jsx("span", { className: flowVariants().groupLabel(), children: label }) : null, children] }) }));
|
|
490
507
|
}
|
|
491
508
|
FlowGroup.displayName = 'Flow.Group';
|
|
509
|
+
/** A strip across the top of a node, ruled off from the rest: a step number and a state. */
|
|
510
|
+
function FlowNodeHeader({ className, ...props }) {
|
|
511
|
+
return _jsx("div", { "data-slot": "flow-node-header", className: flowVariants().nodeHeader({ className }), ...props });
|
|
512
|
+
}
|
|
513
|
+
FlowNodeHeader.displayName = 'Flow.NodeHeader';
|
|
514
|
+
/** The body of a node, with the node's padding: a title over a description. */
|
|
515
|
+
function FlowNodeContent({ className, ...props }) {
|
|
516
|
+
return _jsx("div", { "data-slot": "flow-node-content", className: flowVariants().nodeContent({ className }), ...props });
|
|
517
|
+
}
|
|
518
|
+
FlowNodeContent.displayName = 'Flow.NodeContent';
|
|
519
|
+
/** What the node is. */
|
|
520
|
+
function FlowNodeTitle({ className, ...props }) {
|
|
521
|
+
return _jsx("p", { "data-slot": "flow-node-title", className: flowVariants().nodeTitle({ className }), ...props });
|
|
522
|
+
}
|
|
523
|
+
FlowNodeTitle.displayName = 'Flow.NodeTitle';
|
|
524
|
+
/** A quieter line about the node. Smaller inside a `Flow.GroupItem`. */
|
|
525
|
+
function FlowNodeDescription({ className, ...props }) {
|
|
526
|
+
return (_jsx("p", { "data-slot": "flow-node-description", className: flowVariants().nodeDescription({ className }), ...props }));
|
|
527
|
+
}
|
|
528
|
+
FlowNodeDescription.displayName = 'Flow.NodeDescription';
|
|
529
|
+
/** A word in a pill, for a node's state: done, now, waiting. */
|
|
530
|
+
function FlowNodeBadge({ color = 'muted', className, ...props }) {
|
|
531
|
+
return (_jsx("span", { "data-slot": "flow-node-badge", className: flowVariants({ color }).nodeBadge({ className }), ...props }));
|
|
532
|
+
}
|
|
533
|
+
FlowNodeBadge.displayName = 'Flow.NodeBadge';
|
|
534
|
+
/**
|
|
535
|
+
* One thing inside a `Flow.Group` that is not a node of its own: an icon
|
|
536
|
+
* beside a `Flow.NodeContent`. It moves with the group and edges cannot join it.
|
|
537
|
+
*/
|
|
538
|
+
function FlowGroupItem({ className, ...props }) {
|
|
539
|
+
return _jsx("div", { "data-slot": "flow-group-item", className: flowVariants().groupItem({ className }), ...props });
|
|
540
|
+
}
|
|
541
|
+
FlowGroupItem.displayName = 'Flow.GroupItem';
|
|
492
542
|
const SIDE_STYLE = {
|
|
493
543
|
top: (offset) => ({ left: `${offset * 100}%`, top: '0%' }),
|
|
494
544
|
bottom: (offset) => ({ left: `${offset * 100}%`, top: '100%' }),
|
|
@@ -589,9 +639,15 @@ export const Flow = Object.assign(FlowRoot, {
|
|
|
589
639
|
Handle: FlowHandle,
|
|
590
640
|
Edge: FlowEdge,
|
|
591
641
|
Group: FlowGroup,
|
|
642
|
+
GroupItem: FlowGroupItem,
|
|
643
|
+
NodeHeader: FlowNodeHeader,
|
|
644
|
+
NodeContent: FlowNodeContent,
|
|
645
|
+
NodeTitle: FlowNodeTitle,
|
|
646
|
+
NodeDescription: FlowNodeDescription,
|
|
647
|
+
NodeBadge: FlowNodeBadge,
|
|
592
648
|
Controls: FlowControls,
|
|
593
649
|
MiniMap: FlowMiniMap,
|
|
594
650
|
});
|
|
595
651
|
/* By name as well, for a Server Component, which cannot dot into a client
|
|
596
652
|
module. */
|
|
597
|
-
export { FlowBackground, FlowNode, FlowHandle, FlowEdge, FlowGroup, FlowControls, FlowMiniMap };
|
|
653
|
+
export { FlowBackground, FlowNode, FlowHandle, FlowEdge, FlowGroup, FlowGroupItem, FlowNodeHeader, FlowNodeContent, FlowNodeTitle, FlowNodeDescription, FlowNodeBadge, FlowControls, FlowMiniMap, };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame — a widget shell: a panel of rows sitting in a tray, with the tray's
|
|
3
|
+
* one exposed strip along the top carrying the title.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <Frame>
|
|
7
|
+
* <Frame.Header>
|
|
8
|
+
* <Frame.Title>Deploys</Frame.Title>
|
|
9
|
+
* <Frame.Action>Last 24 hours</Frame.Action>
|
|
10
|
+
* </Frame.Header>
|
|
11
|
+
* <Frame.Panel>
|
|
12
|
+
* <Frame.Row>
|
|
13
|
+
* <Frame.Media><RocketIcon /></Frame.Media>
|
|
14
|
+
* <Frame.Content>
|
|
15
|
+
* <Frame.Title>main · a41c2e9</Frame.Title>
|
|
16
|
+
* <Frame.Description>Production · 2 minutes ago</Frame.Description>
|
|
17
|
+
* </Frame.Content>
|
|
18
|
+
* <Frame.Actions><Badge>Ready</Badge></Frame.Actions>
|
|
19
|
+
* </Frame.Row>
|
|
20
|
+
* </Frame.Panel>
|
|
21
|
+
* </Frame>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ## Two surfaces, one exposed edge
|
|
25
|
+
*
|
|
26
|
+
* The panel sits flush against the shell's left, right and bottom, so the
|
|
27
|
+
* shell reads as something the panel is set into rather than as a border
|
|
28
|
+
* round it — and the strip left at the top is the header, which is why the
|
|
29
|
+
* header needs no rule under it and no background of its own. The panel's
|
|
30
|
+
* bottom corners are not drawn at all: the shell clips them to its own radius.
|
|
31
|
+
*
|
|
32
|
+
* ## Four ways to nest them
|
|
33
|
+
*
|
|
34
|
+
* `default` is the tray. `band` holds a bordered card in a thin muted band,
|
|
35
|
+
* with the band's top strip naming what is inside — the frame for a widget
|
|
36
|
+
* with one thing to show, a figure or a chart, rather than a list of rows.
|
|
37
|
+
* `inset` floats the panel clear of the shell on all four sides, at the radius
|
|
38
|
+
* that keeps the two curves concentric, and the band left round it is a recess
|
|
39
|
+
* — darker than the panel in a light theme and a dark one alike — that
|
|
40
|
+
* `Frame.Footer` puts its actions in. `plain` drops the shell, for a frame
|
|
41
|
+
* inside something that already draws an edge, where a second one just inside
|
|
42
|
+
* it reads as a double line.
|
|
43
|
+
*
|
|
44
|
+
* ## Rows divide themselves
|
|
45
|
+
*
|
|
46
|
+
* The panel draws the hairline between its children, so no row has to know
|
|
47
|
+
* whether it is first. Under `band` it draws none: a figure and its chart are
|
|
48
|
+
* one thing, not two rows. A row with `href` is a link and one with `onClick`
|
|
49
|
+
* is a button, each with the hover and focus a pressable row should have, and
|
|
50
|
+
* `chevron` marks one that leads somewhere.
|
|
51
|
+
*
|
|
52
|
+
* ## It renders on the server
|
|
53
|
+
*
|
|
54
|
+
* The variant reaches the parts through a data attribute and CSS rather than
|
|
55
|
+
* through context, so a frame has no client JavaScript of its own.
|
|
56
|
+
*/
|
|
57
|
+
import type { ComponentProps, MouseEventHandler, ReactNode } from 'react';
|
|
58
|
+
export type FrameVariant = 'default' | 'band' | 'inset' | 'plain';
|
|
59
|
+
export interface FrameProps extends ComponentProps<'div'> {
|
|
60
|
+
/**
|
|
61
|
+
* `default` sets the panel in a tray. `band` holds a bordered card in a
|
|
62
|
+
* thin muted band, for a widget showing one figure or chart, and its panel
|
|
63
|
+
* draws no rule between its children. `inset` floats
|
|
64
|
+
* the panel in a recessed band and gives `Frame.Footer` somewhere to sit.
|
|
65
|
+
* `plain` drops the shell, for a frame inside a container that already draws
|
|
66
|
+
* its own edge.
|
|
67
|
+
*/
|
|
68
|
+
variant?: FrameVariant;
|
|
69
|
+
}
|
|
70
|
+
declare function FrameRoot({ variant, className, ...props }: FrameProps): import("react").JSX.Element;
|
|
71
|
+
declare namespace FrameRoot {
|
|
72
|
+
var displayName: string;
|
|
73
|
+
}
|
|
74
|
+
export interface FrameHeaderProps extends ComponentProps<'div'> {
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The strip of shell above the panel: a title, an optional description under
|
|
78
|
+
* it, and `Frame.Action` on the trailing side.
|
|
79
|
+
*/
|
|
80
|
+
declare function FrameHeader({ className, ...props }: FrameHeaderProps): import("react").JSX.Element;
|
|
81
|
+
declare namespace FrameHeader {
|
|
82
|
+
var displayName: string;
|
|
83
|
+
}
|
|
84
|
+
export interface FrameTitleProps extends ComponentProps<'h3'> {
|
|
85
|
+
/** The heading level in a header. Default `h3`; a row title is always a `p`. */
|
|
86
|
+
as?: 'h2' | 'h3' | 'h4' | 'p';
|
|
87
|
+
}
|
|
88
|
+
/** A caption in the header; the subject line inside `Frame.Content`. */
|
|
89
|
+
declare function FrameTitle({ as: Tag, className, ...props }: FrameTitleProps): import("react").JSX.Element;
|
|
90
|
+
declare namespace FrameTitle {
|
|
91
|
+
var displayName: string;
|
|
92
|
+
}
|
|
93
|
+
export interface FrameDescriptionProps extends ComponentProps<'p'> {
|
|
94
|
+
}
|
|
95
|
+
/** A line under a title, in the header or in a row. */
|
|
96
|
+
declare function FrameDescription({ className, ...props }: FrameDescriptionProps): import("react").JSX.Element;
|
|
97
|
+
declare namespace FrameDescription {
|
|
98
|
+
var displayName: string;
|
|
99
|
+
}
|
|
100
|
+
export interface FrameActionProps extends ComponentProps<'div'> {
|
|
101
|
+
}
|
|
102
|
+
/** The header's trailing side: a short status, a link, a small button. */
|
|
103
|
+
declare function FrameAction({ className, ...props }: FrameActionProps): import("react").JSX.Element;
|
|
104
|
+
declare namespace FrameAction {
|
|
105
|
+
var displayName: string;
|
|
106
|
+
}
|
|
107
|
+
export interface FramePanelProps extends ComponentProps<'div'> {
|
|
108
|
+
}
|
|
109
|
+
/** The card the rows sit on. It draws the hairline between its children, except under `band`. */
|
|
110
|
+
declare function FramePanel({ className, ...props }: FramePanelProps): import("react").JSX.Element;
|
|
111
|
+
declare namespace FramePanel {
|
|
112
|
+
var displayName: string;
|
|
113
|
+
}
|
|
114
|
+
export interface FrameRowProps extends Omit<ComponentProps<'div'>, 'onClick'> {
|
|
115
|
+
/** Make the row a link to this address. */
|
|
116
|
+
href?: string;
|
|
117
|
+
/** Where a link row opens, as on an `<a>`. */
|
|
118
|
+
target?: string;
|
|
119
|
+
/** The relationship of a link row's target, as on an `<a>`. */
|
|
120
|
+
rel?: string;
|
|
121
|
+
/** Make the row a button that calls this. Ignored when `href` is set. */
|
|
122
|
+
onClick?: MouseEventHandler<HTMLElement>;
|
|
123
|
+
/** A trailing chevron, for a row that leads somewhere. */
|
|
124
|
+
chevron?: boolean;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* One line of the panel: `Frame.Media`, `Frame.Content` and `Frame.Actions`
|
|
128
|
+
* side by side. With `href` it is a link and with `onClick` a button.
|
|
129
|
+
*/
|
|
130
|
+
declare function FrameRow({ className, children, chevron, href, target, rel, ...props }: FrameRowProps): import("react").JSX.Element;
|
|
131
|
+
declare namespace FrameRow {
|
|
132
|
+
var displayName: string;
|
|
133
|
+
}
|
|
134
|
+
export interface FrameMediaProps extends ComponentProps<'div'> {
|
|
135
|
+
}
|
|
136
|
+
/** A row's leading icon, avatar or picture. Holds its size. */
|
|
137
|
+
declare function FrameMedia({ className, ...props }: FrameMediaProps): import("react").JSX.Element;
|
|
138
|
+
declare namespace FrameMedia {
|
|
139
|
+
var displayName: string;
|
|
140
|
+
}
|
|
141
|
+
export interface FrameContentProps extends ComponentProps<'div'> {
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The flexible middle of a row, usually a title over a description. It takes
|
|
145
|
+
* what the media and actions leave and may shrink past its content, which is
|
|
146
|
+
* what keeps a long line from pushing the rest of the row off the edge.
|
|
147
|
+
*/
|
|
148
|
+
declare function FrameContent({ className, ...props }: FrameContentProps): import("react").JSX.Element;
|
|
149
|
+
declare namespace FrameContent {
|
|
150
|
+
var displayName: string;
|
|
151
|
+
}
|
|
152
|
+
export interface FrameActionsProps extends ComponentProps<'div'> {
|
|
153
|
+
}
|
|
154
|
+
/** A row's trailing side: a badge, a value, a switch, a small button. Holds its size. */
|
|
155
|
+
declare function FrameActions({ className, ...props }: FrameActionsProps): import("react").JSX.Element;
|
|
156
|
+
declare namespace FrameActions {
|
|
157
|
+
var displayName: string;
|
|
158
|
+
}
|
|
159
|
+
export interface FrameSectionProps extends Omit<ComponentProps<'section'>, 'title'> {
|
|
160
|
+
/** The heading over the section's rows. */
|
|
161
|
+
title?: ReactNode;
|
|
162
|
+
}
|
|
163
|
+
/** A labelled group of rows, for a panel holding more than one. */
|
|
164
|
+
declare function FrameSection({ title, className, children, ...props }: FrameSectionProps): import("react").JSX.Element;
|
|
165
|
+
declare namespace FrameSection {
|
|
166
|
+
var displayName: string;
|
|
167
|
+
}
|
|
168
|
+
export interface FrameFooterProps extends ComponentProps<'div'> {
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The strip below the panel. Under `inset` its children become equal pills in
|
|
172
|
+
* the recessed band, the shape a row of decisions takes.
|
|
173
|
+
*/
|
|
174
|
+
declare function FrameFooter({ className, ...props }: FrameFooterProps): import("react").JSX.Element;
|
|
175
|
+
declare namespace FrameFooter {
|
|
176
|
+
var displayName: string;
|
|
177
|
+
}
|
|
178
|
+
export declare const Frame: typeof FrameRoot & {
|
|
179
|
+
Header: typeof FrameHeader;
|
|
180
|
+
Title: typeof FrameTitle;
|
|
181
|
+
Description: typeof FrameDescription;
|
|
182
|
+
Action: typeof FrameAction;
|
|
183
|
+
Panel: typeof FramePanel;
|
|
184
|
+
Row: typeof FrameRow;
|
|
185
|
+
Media: typeof FrameMedia;
|
|
186
|
+
Content: typeof FrameContent;
|
|
187
|
+
Actions: typeof FrameActions;
|
|
188
|
+
Section: typeof FrameSection;
|
|
189
|
+
Footer: typeof FrameFooter;
|
|
190
|
+
};
|
|
191
|
+
export { FrameHeader, FrameTitle, FrameDescription, FrameAction, FramePanel, FrameRow, FrameMedia, FrameContent, FrameActions, FrameSection, FrameFooter, };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { tv } from 'tailwind-variants';
|
|
3
|
+
/*
|
|
4
|
+
* `inset` geometry. The panel's radius is the shell's less the shell's padding,
|
|
5
|
+
* so the two curves are concentric — a panel radius that ignored the band
|
|
6
|
+
* would leave a crescent of shell thicker at the corners than along the sides.
|
|
7
|
+
*/
|
|
8
|
+
const frameVariants = tv({
|
|
9
|
+
slots: {
|
|
10
|
+
root: 'group/frame flex flex-col text-foreground',
|
|
11
|
+
header: [
|
|
12
|
+
'grid grid-cols-[minmax(0,1fr)] items-center gap-x-3 px-4 pt-2.5 pb-3',
|
|
13
|
+
'has-data-[slot=frame-action]:grid-cols-[minmax(0,1fr)_auto]',
|
|
14
|
+
// The action spans two rows, so with no description the second is empty
|
|
15
|
+
// and a gap under the title would only add height.
|
|
16
|
+
'has-data-[slot=frame-description]:gap-y-0.5',
|
|
17
|
+
'group-data-[variant=inset]/frame:px-3 group-data-[variant=inset]/frame:pt-1 group-data-[variant=inset]/frame:pb-2.5',
|
|
18
|
+
'group-data-[variant=plain]/frame:px-1',
|
|
19
|
+
'group-data-[variant=band]/frame:px-3.5 group-data-[variant=band]/frame:py-2.5',
|
|
20
|
+
],
|
|
21
|
+
/*
|
|
22
|
+
* One part in two places. In the header it labels the tray, so it stays
|
|
23
|
+
* quiet; inside a row's content it is the row's subject, so it takes the
|
|
24
|
+
* foreground and the weight, and truncates rather than pushing the row's
|
|
25
|
+
* trailing slot out of view.
|
|
26
|
+
*/
|
|
27
|
+
title: [
|
|
28
|
+
'text-sm font-medium text-muted-foreground',
|
|
29
|
+
'in-data-[slot=frame-content]:truncate in-data-[slot=frame-content]:text-foreground',
|
|
30
|
+
// A band names one thing rather than captioning a list, so its title reads first.
|
|
31
|
+
'group-data-[variant=band]/frame:text-foreground',
|
|
32
|
+
],
|
|
33
|
+
description: 'text-sm text-muted-foreground in-data-[slot=frame-content]:truncate',
|
|
34
|
+
action: 'col-start-2 row-span-2 row-start-1 text-sm text-muted-foreground',
|
|
35
|
+
// The panel's shape follows the root's variant, read from its data attribute.
|
|
36
|
+
panel: [
|
|
37
|
+
'flex flex-col divide-y overflow-hidden bg-card text-card-foreground',
|
|
38
|
+
// A band holds one figure, not a list of rows, so nothing in it is ruled off.
|
|
39
|
+
'group-data-[variant=band]/frame:divide-y-0',
|
|
40
|
+
'group-data-[variant=default]/frame:rounded-t-2xl group-data-[variant=default]/frame:border-t',
|
|
41
|
+
'group-data-[variant=plain]/frame:rounded-3xl group-data-[variant=plain]/frame:border group-data-[variant=plain]/frame:shadow-xs',
|
|
42
|
+
'group-data-[variant=inset]/frame:rounded-[30px] group-data-[variant=inset]/frame:bg-popover group-data-[variant=inset]/frame:text-popover-foreground group-data-[variant=inset]/frame:shadow-xs',
|
|
43
|
+
// 18px is the band's 24 less its 6px of padding: one corner, two curves.
|
|
44
|
+
'group-data-[variant=band]/frame:rounded-[1.125rem] group-data-[variant=band]/frame:border group-data-[variant=band]/frame:shadow-xs',
|
|
45
|
+
],
|
|
46
|
+
row: 'flex w-full items-center gap-3 px-4 py-3 text-start',
|
|
47
|
+
rowPressable: [
|
|
48
|
+
'cursor-pointer outline-none transition-colors hover:bg-muted/60',
|
|
49
|
+
'focus-visible:bg-muted/60 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset',
|
|
50
|
+
],
|
|
51
|
+
chevron: 'size-4 shrink-0 text-muted-foreground/70 rtl:-scale-x-100',
|
|
52
|
+
media: 'flex shrink-0 items-center justify-center',
|
|
53
|
+
content: 'flex min-w-0 flex-1 flex-col gap-0.5',
|
|
54
|
+
actions: 'flex shrink-0 items-center gap-1.5',
|
|
55
|
+
section: 'flex flex-col divide-y',
|
|
56
|
+
sectionTitle: 'bg-muted/40 px-4 pt-2.5 pb-1.5 text-xs font-medium uppercase tracking-wider text-muted-foreground',
|
|
57
|
+
footer: [
|
|
58
|
+
'flex items-center gap-2 px-4 pt-3 pb-3.5',
|
|
59
|
+
// Under `inset` the band is a row of equal decisions: equal widths, full pills.
|
|
60
|
+
'group-data-[variant=inset]/frame:gap-3.5 group-data-[variant=inset]/frame:px-[18px] group-data-[variant=inset]/frame:pt-4 group-data-[variant=inset]/frame:pb-2.5',
|
|
61
|
+
'group-data-[variant=inset]/frame:*:h-11 group-data-[variant=inset]/frame:*:flex-1 group-data-[variant=inset]/frame:*:rounded-full',
|
|
62
|
+
'group-data-[variant=plain]/frame:px-1',
|
|
63
|
+
'group-data-[variant=band]/frame:px-3.5 group-data-[variant=band]/frame:pt-2.5 group-data-[variant=band]/frame:pb-1',
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
variants: {
|
|
67
|
+
variant: {
|
|
68
|
+
default: {
|
|
69
|
+
// `overflow-hidden` is what gives the panel the shell's bottom corners.
|
|
70
|
+
root: 'overflow-hidden rounded-3xl border bg-muted',
|
|
71
|
+
},
|
|
72
|
+
plain: {},
|
|
73
|
+
band: {
|
|
74
|
+
root: 'rounded-3xl bg-muted p-1.5',
|
|
75
|
+
},
|
|
76
|
+
inset: {
|
|
77
|
+
// The band is the popover surface with black mixed in, so it is the
|
|
78
|
+
// darker of the two in either theme, which a step on a grey scale
|
|
79
|
+
// cannot promise: those run darker one way and lighter the other.
|
|
80
|
+
root: [
|
|
81
|
+
'rounded-[38px] p-2 ring-1 ring-foreground/[0.06]',
|
|
82
|
+
'bg-[color-mix(in_oklab,var(--popover),black_5%)] dark:bg-[color-mix(in_oklab,var(--popover),black_14%)]',
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
defaultVariants: { variant: 'default' },
|
|
88
|
+
});
|
|
89
|
+
function FrameRoot({ variant = 'default', className, ...props }) {
|
|
90
|
+
const slots = frameVariants({ variant });
|
|
91
|
+
return _jsx("div", { "data-slot": "frame", "data-variant": variant, className: slots.root({ className }), ...props });
|
|
92
|
+
}
|
|
93
|
+
FrameRoot.displayName = 'Frame';
|
|
94
|
+
/**
|
|
95
|
+
* The strip of shell above the panel: a title, an optional description under
|
|
96
|
+
* it, and `Frame.Action` on the trailing side.
|
|
97
|
+
*/
|
|
98
|
+
function FrameHeader({ className, ...props }) {
|
|
99
|
+
return _jsx("div", { "data-slot": "frame-header", className: frameVariants().header({ className }), ...props });
|
|
100
|
+
}
|
|
101
|
+
FrameHeader.displayName = 'Frame.Header';
|
|
102
|
+
/** A caption in the header; the subject line inside `Frame.Content`. */
|
|
103
|
+
function FrameTitle({ as: Tag = 'h3', className, ...props }) {
|
|
104
|
+
return _jsx(Tag, { "data-slot": "frame-title", className: frameVariants().title({ className }), ...props });
|
|
105
|
+
}
|
|
106
|
+
FrameTitle.displayName = 'Frame.Title';
|
|
107
|
+
/** A line under a title, in the header or in a row. */
|
|
108
|
+
function FrameDescription({ className, ...props }) {
|
|
109
|
+
return (_jsx("p", { "data-slot": "frame-description", className: frameVariants().description({ className }), ...props }));
|
|
110
|
+
}
|
|
111
|
+
FrameDescription.displayName = 'Frame.Description';
|
|
112
|
+
/** The header's trailing side: a short status, a link, a small button. */
|
|
113
|
+
function FrameAction({ className, ...props }) {
|
|
114
|
+
return _jsx("div", { "data-slot": "frame-action", className: frameVariants().action({ className }), ...props });
|
|
115
|
+
}
|
|
116
|
+
FrameAction.displayName = 'Frame.Action';
|
|
117
|
+
/** The card the rows sit on. It draws the hairline between its children, except under `band`. */
|
|
118
|
+
function FramePanel({ className, ...props }) {
|
|
119
|
+
return _jsx("div", { "data-slot": "frame-panel", className: frameVariants().panel({ className }), ...props });
|
|
120
|
+
}
|
|
121
|
+
FramePanel.displayName = 'Frame.Panel';
|
|
122
|
+
const Chevron = ({ className }) => (_jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": true, className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "m9 6 6 6-6 6" }) }));
|
|
123
|
+
/**
|
|
124
|
+
* One line of the panel: `Frame.Media`, `Frame.Content` and `Frame.Actions`
|
|
125
|
+
* side by side. With `href` it is a link and with `onClick` a button.
|
|
126
|
+
*/
|
|
127
|
+
function FrameRow({ className, children, chevron = false, href, target, rel, ...props }) {
|
|
128
|
+
const slots = frameVariants();
|
|
129
|
+
const inside = (_jsxs(_Fragment, { children: [children, chevron ? _jsx(Chevron, { className: slots.chevron() }) : null] }));
|
|
130
|
+
if (href !== undefined) {
|
|
131
|
+
return (_jsx("a", { "data-slot": "frame-row", href: href, target: target, rel: rel, className: slots.row({ className: [slots.rowPressable(), className] }), ...props, children: inside }));
|
|
132
|
+
}
|
|
133
|
+
// `onClick` stays in `props`, so it reaches whichever element is rendered.
|
|
134
|
+
if (props.onClick !== undefined) {
|
|
135
|
+
return (_jsx("button", { type: "button", "data-slot": "frame-row", className: slots.row({ className: [slots.rowPressable(), className] }), ...props, children: inside }));
|
|
136
|
+
}
|
|
137
|
+
return (_jsx("div", { "data-slot": "frame-row", className: slots.row({ className }), ...props, children: inside }));
|
|
138
|
+
}
|
|
139
|
+
FrameRow.displayName = 'Frame.Row';
|
|
140
|
+
/** A row's leading icon, avatar or picture. Holds its size. */
|
|
141
|
+
function FrameMedia({ className, ...props }) {
|
|
142
|
+
return _jsx("div", { "data-slot": "frame-media", className: frameVariants().media({ className }), ...props });
|
|
143
|
+
}
|
|
144
|
+
FrameMedia.displayName = 'Frame.Media';
|
|
145
|
+
/**
|
|
146
|
+
* The flexible middle of a row, usually a title over a description. It takes
|
|
147
|
+
* what the media and actions leave and may shrink past its content, which is
|
|
148
|
+
* what keeps a long line from pushing the rest of the row off the edge.
|
|
149
|
+
*/
|
|
150
|
+
function FrameContent({ className, ...props }) {
|
|
151
|
+
return _jsx("div", { "data-slot": "frame-content", className: frameVariants().content({ className }), ...props });
|
|
152
|
+
}
|
|
153
|
+
FrameContent.displayName = 'Frame.Content';
|
|
154
|
+
/** A row's trailing side: a badge, a value, a switch, a small button. Holds its size. */
|
|
155
|
+
function FrameActions({ className, ...props }) {
|
|
156
|
+
return _jsx("div", { "data-slot": "frame-actions", className: frameVariants().actions({ className }), ...props });
|
|
157
|
+
}
|
|
158
|
+
FrameActions.displayName = 'Frame.Actions';
|
|
159
|
+
/** A labelled group of rows, for a panel holding more than one. */
|
|
160
|
+
function FrameSection({ title, className, children, ...props }) {
|
|
161
|
+
const slots = frameVariants();
|
|
162
|
+
return (_jsxs("section", { "data-slot": "frame-section", className: slots.section({ className }), ...props, children: [title !== undefined ? _jsx("h4", { className: slots.sectionTitle(), children: title }) : null, children] }));
|
|
163
|
+
}
|
|
164
|
+
FrameSection.displayName = 'Frame.Section';
|
|
165
|
+
/**
|
|
166
|
+
* The strip below the panel. Under `inset` its children become equal pills in
|
|
167
|
+
* the recessed band, the shape a row of decisions takes.
|
|
168
|
+
*/
|
|
169
|
+
function FrameFooter({ className, ...props }) {
|
|
170
|
+
return _jsx("div", { "data-slot": "frame-footer", className: frameVariants().footer({ className }), ...props });
|
|
171
|
+
}
|
|
172
|
+
FrameFooter.displayName = 'Frame.Footer';
|
|
173
|
+
export const Frame = Object.assign(FrameRoot, {
|
|
174
|
+
Header: FrameHeader,
|
|
175
|
+
Title: FrameTitle,
|
|
176
|
+
Description: FrameDescription,
|
|
177
|
+
Action: FrameAction,
|
|
178
|
+
Panel: FramePanel,
|
|
179
|
+
Row: FrameRow,
|
|
180
|
+
Media: FrameMedia,
|
|
181
|
+
Content: FrameContent,
|
|
182
|
+
Actions: FrameActions,
|
|
183
|
+
Section: FrameSection,
|
|
184
|
+
Footer: FrameFooter,
|
|
185
|
+
});
|
|
186
|
+
export { FrameHeader, FrameTitle, FrameDescription, FrameAction, FramePanel, FrameRow, FrameMedia, FrameContent, FrameActions, FrameSection, FrameFooter, };
|
|
@@ -187,9 +187,11 @@ declare namespace KpiSeparator {
|
|
|
187
187
|
export interface KpiGroupProps extends ComponentProps<'div'> {
|
|
188
188
|
/** Side by side with upright rules, or stacked with level ones. */
|
|
189
189
|
orientation?: 'horizontal' | 'vertical';
|
|
190
|
+
/** Draws the group as a card of its own, for a group not already inside one. */
|
|
191
|
+
surface?: boolean;
|
|
190
192
|
}
|
|
191
193
|
/** Several metrics as one panel, with a rule between each and the next. */
|
|
192
|
-
declare function KpiGroup({ orientation, className, ...props }: KpiGroupProps): import("react").JSX.Element;
|
|
194
|
+
declare function KpiGroup({ orientation, surface, className, ...props }: KpiGroupProps): import("react").JSX.Element;
|
|
193
195
|
declare namespace KpiGroup {
|
|
194
196
|
var displayName: string;
|
|
195
197
|
}
|
|
@@ -25,7 +25,10 @@ const kpiVariants = tv({
|
|
|
25
25
|
},
|
|
26
26
|
variants: {
|
|
27
27
|
surface: {
|
|
28
|
-
true: {
|
|
28
|
+
true: {
|
|
29
|
+
root: 'rounded-3xl border bg-card p-5 text-card-foreground',
|
|
30
|
+
group: 'rounded-3xl border bg-card p-5 text-card-foreground',
|
|
31
|
+
},
|
|
29
32
|
},
|
|
30
33
|
color: {
|
|
31
34
|
primary: { chart: '[--pu-color:var(--primary)]', progress: '[--pu-color:var(--primary)]' },
|
|
@@ -221,8 +224,8 @@ function KpiSeparator({ className, ...props }) {
|
|
|
221
224
|
}
|
|
222
225
|
KpiSeparator.displayName = 'Kpi.Separator';
|
|
223
226
|
/** Several metrics as one panel, with a rule between each and the next. */
|
|
224
|
-
function KpiGroup({ orientation = 'horizontal', className, ...props }) {
|
|
225
|
-
return _jsx("div", { className: kpiVariants({ orientation }).group({ className }), ...props });
|
|
227
|
+
function KpiGroup({ orientation = 'horizontal', surface = false, className, ...props }) {
|
|
228
|
+
return _jsx("div", { className: kpiVariants({ orientation, surface }).group({ className }), ...props });
|
|
226
229
|
}
|
|
227
230
|
KpiGroup.displayName = 'Kpi.Group';
|
|
228
231
|
export const Kpi = Object.assign(KpiRoot, {
|
|
@@ -114,6 +114,13 @@ declare function LaneBoardMeta({ className, ...props }: LaneBoardMetaProps): imp
|
|
|
114
114
|
declare namespace LaneBoardMeta {
|
|
115
115
|
var displayName: string;
|
|
116
116
|
}
|
|
117
|
+
export interface LaneBoardAvatarProps extends ComponentProps<'span'> {
|
|
118
|
+
}
|
|
119
|
+
/** Who a card belongs to, as initials in a small circle. */
|
|
120
|
+
declare function LaneBoardAvatar({ className, ...props }: LaneBoardAvatarProps): import("react").JSX.Element;
|
|
121
|
+
declare namespace LaneBoardAvatar {
|
|
122
|
+
var displayName: string;
|
|
123
|
+
}
|
|
117
124
|
export declare const LaneBoard: typeof LaneBoardRoot & {
|
|
118
125
|
Title: typeof LaneBoardTitle;
|
|
119
126
|
Code: typeof LaneBoardCode;
|
|
@@ -121,5 +128,6 @@ export declare const LaneBoard: typeof LaneBoardRoot & {
|
|
|
121
128
|
Steps: typeof LaneBoardSteps;
|
|
122
129
|
Tag: typeof LaneBoardTag;
|
|
123
130
|
Meta: typeof LaneBoardMeta;
|
|
131
|
+
Avatar: typeof LaneBoardAvatar;
|
|
124
132
|
};
|
|
125
|
-
export { LaneBoardTitle, LaneBoardCode, LaneBoardRow, LaneBoardSteps, LaneBoardTag, LaneBoardMeta };
|
|
133
|
+
export { LaneBoardTitle, LaneBoardCode, LaneBoardRow, LaneBoardSteps, LaneBoardTag, LaneBoardMeta, LaneBoardAvatar, };
|
|
@@ -89,6 +89,7 @@ const laneBoardVariants = tv({
|
|
|
89
89
|
step: 'h-1 w-4 rounded-full',
|
|
90
90
|
tag: 'inline-flex items-center rounded-full border border-[color-mix(in_oklab,var(--pu-color)_35%,var(--card))] bg-[color-mix(in_oklab,var(--pu-color)_14%,transparent)] px-2 py-0.5 text-xs font-medium text-[var(--pu-color)]',
|
|
91
91
|
meta: 'font-mono text-sm tabular-nums text-foreground',
|
|
92
|
+
avatar: 'flex size-6 shrink-0 items-center justify-center rounded-full bg-muted text-[0.625rem] font-semibold text-muted-foreground',
|
|
92
93
|
},
|
|
93
94
|
variants: {
|
|
94
95
|
color: {
|
|
@@ -678,6 +679,11 @@ function LaneBoardMeta({ className, ...props }) {
|
|
|
678
679
|
return _jsx("span", { className: laneBoardVariants().meta({ className }), ...props });
|
|
679
680
|
}
|
|
680
681
|
LaneBoardMeta.displayName = 'LaneBoard.Meta';
|
|
682
|
+
/** Who a card belongs to, as initials in a small circle. */
|
|
683
|
+
function LaneBoardAvatar({ className, ...props }) {
|
|
684
|
+
return _jsx("span", { "data-slot": "lane-board-avatar", className: laneBoardVariants().avatar({ className }), ...props });
|
|
685
|
+
}
|
|
686
|
+
LaneBoardAvatar.displayName = 'LaneBoard.Avatar';
|
|
681
687
|
export const LaneBoard = Object.assign(LaneBoardRoot, {
|
|
682
688
|
Title: LaneBoardTitle,
|
|
683
689
|
Code: LaneBoardCode,
|
|
@@ -685,7 +691,8 @@ export const LaneBoard = Object.assign(LaneBoardRoot, {
|
|
|
685
691
|
Steps: LaneBoardSteps,
|
|
686
692
|
Tag: LaneBoardTag,
|
|
687
693
|
Meta: LaneBoardMeta,
|
|
694
|
+
Avatar: LaneBoardAvatar,
|
|
688
695
|
});
|
|
689
696
|
/* By name as well, for a Server Component, which cannot dot into a client
|
|
690
697
|
module. */
|
|
691
|
-
export { LaneBoardTitle, LaneBoardCode, LaneBoardRow, LaneBoardSteps, LaneBoardTag, LaneBoardMeta };
|
|
698
|
+
export { LaneBoardTitle, LaneBoardCode, LaneBoardRow, LaneBoardSteps, LaneBoardTag, LaneBoardMeta, LaneBoardAvatar, };
|
|
@@ -124,9 +124,32 @@ declare function PlanetControls({ step, className, children, ...props }: PlanetC
|
|
|
124
124
|
declare namespace PlanetControls {
|
|
125
125
|
var displayName: string;
|
|
126
126
|
}
|
|
127
|
+
export interface PlanetListProps extends ComponentProps<'ul'> {
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The places on the globe as a list, in two columns, for picking one by name.
|
|
131
|
+
* Put it beside or under the globe and turn the globe to whatever is picked.
|
|
132
|
+
*/
|
|
133
|
+
declare function PlanetList({ className, ...props }: PlanetListProps): import("react").JSX.Element;
|
|
134
|
+
declare namespace PlanetList {
|
|
135
|
+
var displayName: string;
|
|
136
|
+
}
|
|
137
|
+
export interface PlanetListItemProps extends Omit<ComponentProps<'button'>, 'color'> {
|
|
138
|
+
/** The colour of the dot before the name. Match it to the place's marker. */
|
|
139
|
+
color?: PlanetMarkerColor;
|
|
140
|
+
/** Draws the row as the one that has been picked. */
|
|
141
|
+
selected?: boolean;
|
|
142
|
+
}
|
|
143
|
+
/** One place in `Planet.List`: a dot in its marker's colour, and its name. A button. */
|
|
144
|
+
declare function PlanetListItem({ color, selected, className, children, ...props }: PlanetListItemProps): import("react").JSX.Element;
|
|
145
|
+
declare namespace PlanetListItem {
|
|
146
|
+
var displayName: string;
|
|
147
|
+
}
|
|
127
148
|
export declare const Planet: import("react").ForwardRefExoticComponent<Omit<PlanetProps, "ref"> & import("react").RefAttributes<PlanetHandle>> & {
|
|
128
149
|
Globe: typeof PlanetGlobe;
|
|
129
150
|
Marker: typeof PlanetMarker;
|
|
130
151
|
Controls: typeof PlanetControls;
|
|
152
|
+
List: typeof PlanetList;
|
|
153
|
+
ListItem: typeof PlanetListItem;
|
|
131
154
|
};
|
|
132
|
-
export { PlanetGlobe, PlanetMarker, PlanetControls };
|
|
155
|
+
export { PlanetGlobe, PlanetMarker, PlanetControls, PlanetList, PlanetListItem };
|