@robr0/design-system 0.3.0 → 0.5.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 +30 -27
- package/components/AgentStatus/AgentStatus.css +496 -0
- package/components/AgentStatus/AgentStatus.d.ts +41 -0
- package/components/AgentStatus/AgentStatus.js +85 -0
- package/components/AgentStatus/AgentStatusPatterns.d.ts +17 -0
- package/components/AgentStatus/AgentStatusPatterns.js +20 -0
- package/components/AiButton/AiButton.css +163 -0
- package/components/AiButton/AiButton.d.ts +36 -0
- package/components/AiButton/AiButton.js +58 -0
- package/components/AlertDialog/AlertDialog.css +5 -5
- package/components/Button/Button.d.ts +14 -2
- package/components/ButtonGroup/ButtonGroup.js +2 -9
- package/components/ChatHeader/ChatHeader.css +35 -0
- package/components/ChatHeader/ChatHeader.d.ts +30 -0
- package/components/ChatHeader/ChatHeader.js +17 -0
- package/components/ChatMarker/ChatMarker.css +52 -0
- package/components/ChatMarker/ChatMarker.d.ts +22 -0
- package/components/ChatMarker/ChatMarker.js +18 -0
- package/components/ChatMessage/ChatMessage.css +304 -0
- package/components/ChatMessage/ChatMessage.d.ts +68 -0
- package/components/ChatMessage/ChatMessage.js +69 -0
- package/components/ChatThread/ChatThread.css +171 -0
- package/components/ChatThread/ChatThread.d.ts +41 -0
- package/components/ChatThread/ChatThread.js +174 -0
- package/components/Checkbox/Checkbox.d.ts +17 -3
- package/components/Chip/Chip.css +27 -0
- package/components/Chip/Chip.d.ts +2 -2
- package/components/CircularButton/CircularButton.d.ts +31 -10
- package/components/CircularButton/CircularButton.js +54 -50
- package/components/CodeBlock/CodeBlock.css +2 -2
- package/components/Combobox/Combobox.d.ts +10 -2
- package/components/Composer/Composer.css +164 -0
- package/components/Composer/Composer.d.ts +67 -0
- package/components/Composer/Composer.js +120 -0
- package/components/ContextMenu/ContextMenu.css +0 -4
- package/components/DateInput/DateInput.d.ts +5 -1
- package/components/Dialog/Dialog.css +5 -5
- package/components/DocumentChip/DocumentChip.css +181 -0
- package/components/DocumentChip/DocumentChip.d.ts +41 -0
- package/components/DocumentChip/DocumentChip.js +78 -0
- package/components/Dropdown/Dropdown.css +2 -4
- package/components/Dropdown/Dropdown.d.ts +10 -2
- package/components/DropdownMenu/DropdownMenu.css +2 -4
- package/components/FileInput/FileInput.d.ts +5 -1
- package/components/Input/Input.d.ts +5 -1
- package/components/InterruptCard/InterruptCard.css +164 -0
- package/components/InterruptCard/InterruptCard.d.ts +55 -0
- package/components/InterruptCard/InterruptCard.js +57 -0
- package/components/LinkList/LinkList.d.ts +1 -0
- package/components/MessageActions/MessageActions.css +76 -0
- package/components/MessageActions/MessageActions.d.ts +38 -0
- package/components/MessageActions/MessageActions.js +33 -0
- package/components/MessageCard/MessageCard.css +112 -0
- package/components/MessageCard/MessageCard.d.ts +35 -0
- package/components/MessageCard/MessageCard.js +28 -0
- package/components/NavList/NavList.css +155 -0
- package/components/NavList/NavList.d.ts +56 -0
- package/components/NavList/NavList.js +99 -0
- package/components/PromptSuggestions/PromptSuggestions.css +87 -0
- package/components/PromptSuggestions/PromptSuggestions.d.ts +51 -0
- package/components/PromptSuggestions/PromptSuggestions.js +34 -0
- package/components/Prose/Prose.css +252 -0
- package/components/Prose/Prose.d.ts +21 -0
- package/components/Prose/Prose.js +14 -0
- package/components/RadioButton/RadioButton.d.ts +17 -3
- package/components/Reasoning/Reasoning.css +276 -0
- package/components/Reasoning/Reasoning.d.ts +55 -0
- package/components/Reasoning/Reasoning.js +98 -0
- package/components/SelectionCard/SelectionCard.d.ts +5 -1
- package/components/Slider/Slider.d.ts +5 -1
- package/components/SourceChip/SourceChip.css +102 -0
- package/components/SourceChip/SourceChip.d.ts +32 -0
- package/components/SourceChip/SourceChip.js +31 -0
- package/components/Stat/Stat.css +2 -2
- package/components/Textarea/Textarea.d.ts +5 -1
- package/components/Timeline/Timeline.d.ts +2 -0
- package/components/ToggleGroup/ToggleGroup.d.ts +10 -2
- package/components/ToggleSwitch/ToggleSwitch.d.ts +10 -2
- package/components/ToolCall/ToolCall.css +213 -0
- package/components/ToolCall/ToolCall.d.ts +43 -0
- package/components/ToolCall/ToolCall.js +89 -0
- package/components/registry.json +145 -8
- package/components/registry.json.d.ts +145 -8
- package/components/registry.json.js +2 -2
- package/index.d.ts +18 -0
- package/index.js +36 -0
- package/package.json +8 -4
- package/tokens/registry.json +13 -0
- package/tokens/registry.json.d.ts +13 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +13 -0
- package/tokens/tokens-light.css +42 -0
- package/tokens/tokens-motion.css +3 -1
- package/tokens/tokens-typography.css +24 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
NAV LIST COMPONENT
|
|
3
|
+
Vertical navigation link list for drawers and
|
|
4
|
+
side menus. Rows are tertiary Buttons — their
|
|
5
|
+
shape, sizing, and states live in Button.css;
|
|
6
|
+
this file owns only layout, the chevron
|
|
7
|
+
toggle, and the expand animation.
|
|
8
|
+
============================================ */
|
|
9
|
+
|
|
10
|
+
.ds-nav-list {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
gap: var(--gap-xxs);
|
|
14
|
+
width: 100%;
|
|
15
|
+
margin: 0;
|
|
16
|
+
padding: 0;
|
|
17
|
+
list-style: none;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Every row reserves the toggle's 40px height, so leaf rows (compact
|
|
21
|
+
Buttons, ~32px) and rows carrying a 40px chevron toggle sit on the same
|
|
22
|
+
vertical rhythm instead of producing uneven gaps down the list. */
|
|
23
|
+
.ds-nav-list__item {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
min-height: 40px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* ============================================
|
|
31
|
+
ROWS
|
|
32
|
+
Full-width, left-aligned Buttons; long labels
|
|
33
|
+
truncate with an ellipsis.
|
|
34
|
+
============================================ */
|
|
35
|
+
|
|
36
|
+
.ds-nav-list .ds-button {
|
|
37
|
+
width: 100%;
|
|
38
|
+
max-width: 100%;
|
|
39
|
+
justify-content: flex-start;
|
|
40
|
+
text-align: left;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ds-nav-list .ds-button__text {
|
|
44
|
+
min-width: 0;
|
|
45
|
+
flex: 0 1 auto;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
text-overflow: ellipsis;
|
|
48
|
+
white-space: nowrap;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* A row with a toggle shares its width with the chevron button. */
|
|
52
|
+
.ds-nav-list__row {
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: var(--gap-xxs);
|
|
56
|
+
width: 100%;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.ds-nav-list__row .ds-button {
|
|
60
|
+
width: auto;
|
|
61
|
+
flex: 1 1 auto;
|
|
62
|
+
min-width: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ============================================
|
|
66
|
+
TOGGLE
|
|
67
|
+
Mirrors CircularButton's 40px tertiary recipe.
|
|
68
|
+
============================================ */
|
|
69
|
+
|
|
70
|
+
.ds-nav-list__toggle {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
width: 40px;
|
|
76
|
+
height: 40px;
|
|
77
|
+
padding: 0;
|
|
78
|
+
border: none;
|
|
79
|
+
border-radius: var(--radius-full);
|
|
80
|
+
background-color: var(--color-action-passive-bg);
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
transition: background-color var(--motion-duration-base) var(--motion-ease-standard);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.ds-nav-list__toggle:hover {
|
|
86
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.ds-nav-list__toggle:active {
|
|
90
|
+
background-color: var(--color-action-passive-bg-active);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.ds-nav-list__toggle:focus-visible {
|
|
94
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
95
|
+
outline-offset: -2px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.ds-nav-list__chevron {
|
|
99
|
+
--icon-size: var(--icon-size-sm);
|
|
100
|
+
color: var(--color-icon-primary);
|
|
101
|
+
line-height: 1;
|
|
102
|
+
transition: transform var(--motion-duration-slow) var(--motion-ease-standard);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.ds-nav-list__toggle[aria-expanded='true'] .ds-nav-list__chevron {
|
|
106
|
+
transform: rotate(180deg);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ============================================
|
|
110
|
+
PANEL — collapsible sublist wrapper
|
|
111
|
+
Grid-rows height animation; reduced motion is
|
|
112
|
+
handled by the motion-token contract.
|
|
113
|
+
============================================ */
|
|
114
|
+
|
|
115
|
+
.ds-nav-list__panel {
|
|
116
|
+
display: grid;
|
|
117
|
+
grid-template-rows: 0fr;
|
|
118
|
+
margin-top: 0;
|
|
119
|
+
transition:
|
|
120
|
+
grid-template-rows var(--motion-duration-slow) var(--motion-ease-emphasized),
|
|
121
|
+
margin-top var(--motion-duration-slow) var(--motion-ease-emphasized);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Open panels take the list's item gap above them, so the first child sits
|
|
125
|
+
the same distance from its parent row as any two siblings do. */
|
|
126
|
+
.ds-nav-list__panel--open {
|
|
127
|
+
grid-template-rows: 1fr;
|
|
128
|
+
margin-top: var(--gap-xxs);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ============================================
|
|
132
|
+
SUBLIST — one indent step per nesting level
|
|
133
|
+
============================================ */
|
|
134
|
+
|
|
135
|
+
.ds-nav-list__sublist {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
gap: var(--gap-xxs);
|
|
139
|
+
overflow: hidden;
|
|
140
|
+
min-height: 0;
|
|
141
|
+
margin: 0;
|
|
142
|
+
padding: 0;
|
|
143
|
+
padding-left: var(--padding-xl);
|
|
144
|
+
list-style: none;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.ds-nav-list__sublist .ds-nav-list__sublist {
|
|
148
|
+
padding-left: var(--gap-lg);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Permanent (non-collapsible) groups render their sublist directly under the
|
|
152
|
+
row, with no panel — give it the same item gap the panel carries when open. */
|
|
153
|
+
.ds-nav-list__item > .ds-nav-list__sublist {
|
|
154
|
+
margin-top: var(--gap-xxs);
|
|
155
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface NavListItem {
|
|
3
|
+
/** Row text. */
|
|
4
|
+
label: string;
|
|
5
|
+
/** Link destination — every row is a real link. */
|
|
6
|
+
href: string;
|
|
7
|
+
/** Stable identity for expansion state and aria wiring. Defaults to `href`. */
|
|
8
|
+
id?: string;
|
|
9
|
+
/** Child rows, indented one level. Nesting is supported three levels deep. */
|
|
10
|
+
items?: NavListItem[];
|
|
11
|
+
/**
|
|
12
|
+
* When false, an item with children renders no toggle and its children stay
|
|
13
|
+
* visible — a permanent group rather than a collapsible section.
|
|
14
|
+
*/
|
|
15
|
+
collapsible?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Forces the current-page treatment on this row regardless of the exact
|
|
18
|
+
* match against `currentHref` — for prefix-matched sections that should
|
|
19
|
+
* stay current on their child routes.
|
|
20
|
+
*/
|
|
21
|
+
current?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Props owned by NavList itself — everything else falls through to the list element. */
|
|
24
|
+
type NavListOwnProps = {
|
|
25
|
+
/** Navigation tree to render. */
|
|
26
|
+
items: NavListItem[];
|
|
27
|
+
/** href of the current page — the exactly matching row gets `aria-current="page"` and the active treatment. */
|
|
28
|
+
currentHref?: string;
|
|
29
|
+
/** Controlled list of expanded item ids. */
|
|
30
|
+
expandedIds?: string[];
|
|
31
|
+
/** Initially expanded item ids (uncontrolled). */
|
|
32
|
+
defaultExpandedIds?: string[];
|
|
33
|
+
/** Fires with the full new list of expanded ids whenever a toggle is pressed, in both controlled and uncontrolled modes. */
|
|
34
|
+
onExpandedChange?: (expandedIds: string[]) => void;
|
|
35
|
+
/** Allow several sections expanded at once. Default false — expanding one collapses the rest. */
|
|
36
|
+
multiple?: boolean;
|
|
37
|
+
/** Fires when any link is clicked, alongside native navigation — e.g. to close the drawer that contains the list. */
|
|
38
|
+
onNavigate?: (item: NavListItem, event: React.MouseEvent<HTMLElement>) => void;
|
|
39
|
+
/** Additional CSS classes */
|
|
40
|
+
className?: string;
|
|
41
|
+
};
|
|
42
|
+
export interface NavListProps extends NavListOwnProps, Omit<React.ComponentPropsWithoutRef<'ul'>, keyof NavListOwnProps> {
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Vertical list of navigation links for drawers and side menus.
|
|
46
|
+
*
|
|
47
|
+
* Every row is a real link, rendered as a tertiary Button so shape, sizing,
|
|
48
|
+
* and interaction states stay identical to every other button in the system.
|
|
49
|
+
* An item with children gets a separate chevron toggle — tapping the label
|
|
50
|
+
* navigates, tapping the chevron expands — and collapsed sublists are `inert`,
|
|
51
|
+
* so they leave the tab order and the accessibility tree. Expansion is
|
|
52
|
+
* uncontrolled by default (`defaultExpandedIds`) or controlled via
|
|
53
|
+
* `expandedIds` + `onExpandedChange`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const NavList: React.ForwardRefExoticComponent<NavListProps & React.RefAttributes<HTMLUListElement>>;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useId, useState } from "react";
|
|
3
|
+
import { Button } from "../Button/Button.js";
|
|
4
|
+
import "./NavList.css";
|
|
5
|
+
import "../../fonts/material-symbols.css";
|
|
6
|
+
const MAX_DEPTH = 3;
|
|
7
|
+
const NavList = React.forwardRef(
|
|
8
|
+
({
|
|
9
|
+
items,
|
|
10
|
+
currentHref,
|
|
11
|
+
expandedIds,
|
|
12
|
+
defaultExpandedIds,
|
|
13
|
+
onExpandedChange,
|
|
14
|
+
multiple = false,
|
|
15
|
+
onNavigate,
|
|
16
|
+
className = "",
|
|
17
|
+
...rest
|
|
18
|
+
}, ref) => {
|
|
19
|
+
const baseClass = "ds-nav-list";
|
|
20
|
+
const uid = useId();
|
|
21
|
+
const [internalExpanded, setInternalExpanded] = useState(
|
|
22
|
+
defaultExpandedIds ?? []
|
|
23
|
+
);
|
|
24
|
+
const expanded = expandedIds ?? internalExpanded;
|
|
25
|
+
const itemId = (item) => item.id ?? item.href;
|
|
26
|
+
const toggle = (id) => {
|
|
27
|
+
const next = expanded.includes(id) ? expanded.filter((entry) => entry !== id) : multiple ? [...expanded, id] : [id];
|
|
28
|
+
if (expandedIds === void 0) setInternalExpanded(next);
|
|
29
|
+
onExpandedChange?.(next);
|
|
30
|
+
};
|
|
31
|
+
const renderItems = (level, levelItems) => levelItems.map((item) => {
|
|
32
|
+
const id = itemId(item);
|
|
33
|
+
const isCurrent = item.current ?? (currentHref !== void 0 && item.href === currentHref);
|
|
34
|
+
const children = level < MAX_DEPTH ? item.items : void 0;
|
|
35
|
+
const collapsible = item.collapsible ?? true;
|
|
36
|
+
const isOpen = expanded.includes(id);
|
|
37
|
+
const panelId = `${uid}-${id}`;
|
|
38
|
+
const link = /* @__PURE__ */ jsx(
|
|
39
|
+
Button,
|
|
40
|
+
{
|
|
41
|
+
variant: "tertiary",
|
|
42
|
+
size: level === 1 ? "default" : "compact",
|
|
43
|
+
label: item.label,
|
|
44
|
+
href: item.href,
|
|
45
|
+
ariaCurrent: isCurrent,
|
|
46
|
+
className: isCurrent ? "ds-button--active" : void 0,
|
|
47
|
+
onClick: (event) => onNavigate?.(item, event)
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
if (!children || children.length === 0) {
|
|
51
|
+
return /* @__PURE__ */ jsx("li", { className: `${baseClass}__item`, children: link }, id);
|
|
52
|
+
}
|
|
53
|
+
if (!collapsible) {
|
|
54
|
+
return /* @__PURE__ */ jsxs("li", { className: `${baseClass}__item`, children: [
|
|
55
|
+
link,
|
|
56
|
+
/* @__PURE__ */ jsx("ul", { className: `${baseClass}__sublist`, children: renderItems(level + 1, children) })
|
|
57
|
+
] }, id);
|
|
58
|
+
}
|
|
59
|
+
return /* @__PURE__ */ jsxs("li", { className: `${baseClass}__item`, children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__row`, children: [
|
|
61
|
+
link,
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
"button",
|
|
64
|
+
{
|
|
65
|
+
type: "button",
|
|
66
|
+
className: `${baseClass}__toggle`,
|
|
67
|
+
"aria-expanded": isOpen,
|
|
68
|
+
"aria-controls": panelId,
|
|
69
|
+
"aria-label": `${isOpen ? "Collapse" : "Expand"} ${item.label}`,
|
|
70
|
+
onClick: () => toggle(id),
|
|
71
|
+
children: /* @__PURE__ */ jsx(
|
|
72
|
+
"span",
|
|
73
|
+
{
|
|
74
|
+
className: `${baseClass}__chevron material-symbols-rounded`,
|
|
75
|
+
"aria-hidden": "true",
|
|
76
|
+
children: "expand_more"
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
"div",
|
|
84
|
+
{
|
|
85
|
+
id: panelId,
|
|
86
|
+
className: `${baseClass}__panel ${isOpen ? `${baseClass}__panel--open` : ""}`,
|
|
87
|
+
inert: !isOpen,
|
|
88
|
+
children: /* @__PURE__ */ jsx("ul", { className: `${baseClass}__sublist`, children: renderItems(level + 1, children) })
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
] }, id);
|
|
92
|
+
});
|
|
93
|
+
return /* @__PURE__ */ jsx("ul", { ...rest, ref, className: `${baseClass} ${className}`.trim(), children: renderItems(1, items) });
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
NavList.displayName = "NavList";
|
|
97
|
+
export {
|
|
98
|
+
NavList
|
|
99
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
PROMPT SUGGESTIONS COMPONENT
|
|
3
|
+
Horizontal row of tappable prompt chips.
|
|
4
|
+
Scrolls with pure-CSS edge fades by default;
|
|
5
|
+
wraps for empty-state hero placements.
|
|
6
|
+
============================================ */
|
|
7
|
+
|
|
8
|
+
.ds-prompt-suggestions {
|
|
9
|
+
display: flex;
|
|
10
|
+
gap: var(--gap-sm);
|
|
11
|
+
/* Breathing room so chip focus rings aren't clipped by the scroll box */
|
|
12
|
+
padding-block: var(--padding-xxxs);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* ============================================
|
|
16
|
+
SIZE
|
|
17
|
+
The pill scale itself is Chip's — the row only
|
|
18
|
+
picks which size to ask for. What belongs here
|
|
19
|
+
is the gutter between pills, which opens up a
|
|
20
|
+
step at the larger size.
|
|
21
|
+
============================================ */
|
|
22
|
+
|
|
23
|
+
.ds-prompt-suggestions--default {
|
|
24
|
+
gap: var(--gap-sm-md);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ds-prompt-suggestions--compact {
|
|
28
|
+
/* Base gap applies. */
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* ============================================
|
|
32
|
+
SCROLL — one line, overflow hinted by fades
|
|
33
|
+
============================================ */
|
|
34
|
+
|
|
35
|
+
/* Edge fades — scroll layout only, which is why the layout is named on the
|
|
36
|
+
element rather than inferred: content near either edge fades out, hinting
|
|
37
|
+
there is more to scroll. A CSS mask, not a scroll listener.
|
|
38
|
+
ds-allow(color): mask alpha keywords (black/transparent), not palette colours */
|
|
39
|
+
.ds-prompt-suggestions--scroll {
|
|
40
|
+
overflow-x: auto;
|
|
41
|
+
-webkit-mask-image: linear-gradient(
|
|
42
|
+
to right,
|
|
43
|
+
transparent,
|
|
44
|
+
black var(--gap-lg),
|
|
45
|
+
black calc(100% - var(--gap-lg)),
|
|
46
|
+
transparent
|
|
47
|
+
);
|
|
48
|
+
mask-image: linear-gradient(
|
|
49
|
+
to right,
|
|
50
|
+
transparent,
|
|
51
|
+
black var(--gap-lg),
|
|
52
|
+
black calc(100% - var(--gap-lg)),
|
|
53
|
+
transparent
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* ============================================
|
|
58
|
+
WRAP — multi-line hero variant, no fades
|
|
59
|
+
============================================ */
|
|
60
|
+
|
|
61
|
+
.ds-prompt-suggestions--wrap {
|
|
62
|
+
flex-wrap: wrap;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ============================================
|
|
66
|
+
STACK — one suggestion per line
|
|
67
|
+
For narrow columns, where a wrapped row breaks
|
|
68
|
+
wherever the labels happen to run out of room
|
|
69
|
+
and the ragged right edge reads as an accident.
|
|
70
|
+
Pills still hug their labels: stretching them
|
|
71
|
+
to a shared width would make a set of prompts
|
|
72
|
+
look like a set of buttons.
|
|
73
|
+
============================================ */
|
|
74
|
+
|
|
75
|
+
.ds-prompt-suggestions--stack {
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
align-items: flex-start;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* ============================================
|
|
81
|
+
ITEMS
|
|
82
|
+
============================================ */
|
|
83
|
+
|
|
84
|
+
.ds-prompt-suggestions__item {
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-shrink: 0;
|
|
87
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** One tappable suggestion in the row. */
|
|
3
|
+
export interface PromptSuggestion {
|
|
4
|
+
/** Stable identifier passed to `onValueChange`. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** The suggestion text shown in the chip. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Optional leading Material Symbol name. */
|
|
9
|
+
icon?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Props owned by PromptSuggestions itself — everything else falls through to the root element. */
|
|
12
|
+
type PromptSuggestionsOwnProps = {
|
|
13
|
+
/** The suggestions to render, in order. */
|
|
14
|
+
suggestions: PromptSuggestion[];
|
|
15
|
+
/** Fires with the tapped suggestion's `id`. */
|
|
16
|
+
onValueChange?: (id: string) => void;
|
|
17
|
+
/**
|
|
18
|
+
* How the suggestions are arranged. `scroll` is one line that scrolls
|
|
19
|
+
* sideways behind edge fades. `wrap` runs them across as many lines as
|
|
20
|
+
* they need, for empty-state hero placements. `stack` gives each one its
|
|
21
|
+
* own line, for narrow columns where a wrapped row breaks unevenly and
|
|
22
|
+
* the ragged right edge reads as an accident.
|
|
23
|
+
*/
|
|
24
|
+
layout?: 'scroll' | 'wrap' | 'stack';
|
|
25
|
+
/**
|
|
26
|
+
* Legacy alias for `layout="wrap"`; ignored when `layout` is set.
|
|
27
|
+
* @deprecated Use `layout` instead, which also covers `stack`.
|
|
28
|
+
*/
|
|
29
|
+
wrap?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Row scale. `default` sits at the body-paragraph scale, so a suggestion
|
|
32
|
+
* reads at the same weight as the messages it will become; `compact` is
|
|
33
|
+
* the quieter row for placements alongside a live conversation.
|
|
34
|
+
*/
|
|
35
|
+
size?: 'default' | 'compact';
|
|
36
|
+
/** Accessible name for the list. */
|
|
37
|
+
ariaLabel?: string;
|
|
38
|
+
/** Additional CSS classes */
|
|
39
|
+
className?: string;
|
|
40
|
+
};
|
|
41
|
+
export interface PromptSuggestionsProps extends PromptSuggestionsOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof PromptSuggestionsOwnProps> {
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* PromptSuggestions is a horizontal row of tappable prompts for starting or
|
|
45
|
+
* steering a conversation. Each suggestion renders as a clickable Chip, so
|
|
46
|
+
* one component owns the pill look; the row scrolls horizontally with pure
|
|
47
|
+
* CSS edge fades — no scroll listeners, no measurement — or wraps for
|
|
48
|
+
* empty-state hero placements.
|
|
49
|
+
*/
|
|
50
|
+
export declare const PromptSuggestions: React.ForwardRefExoticComponent<PromptSuggestionsProps & React.RefAttributes<HTMLDivElement>>;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Chip } from "../Chip/Chip.js";
|
|
4
|
+
import "./PromptSuggestions.css";
|
|
5
|
+
const PromptSuggestions = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
suggestions,
|
|
8
|
+
onValueChange,
|
|
9
|
+
layout,
|
|
10
|
+
wrap = false,
|
|
11
|
+
size = "default",
|
|
12
|
+
ariaLabel = "Suggested prompts",
|
|
13
|
+
className = "",
|
|
14
|
+
...rest
|
|
15
|
+
}, ref) => {
|
|
16
|
+
const baseClass = "ds-prompt-suggestions";
|
|
17
|
+
const arrangement = layout ?? (wrap ? "wrap" : "scroll");
|
|
18
|
+
const chipSize = size === "compact" ? "default" : "large";
|
|
19
|
+
const classes = [baseClass, `${baseClass}--${size}`, `${baseClass}--${arrangement}`, className].filter(Boolean).join(" ");
|
|
20
|
+
return /* @__PURE__ */ jsx("div", { ...rest, ref, className: classes, role: "list", "aria-label": ariaLabel, children: suggestions.map((suggestion) => /* @__PURE__ */ jsx("div", { role: "listitem", className: `${baseClass}__item`, children: /* @__PURE__ */ jsx(
|
|
21
|
+
Chip,
|
|
22
|
+
{
|
|
23
|
+
label: suggestion.label,
|
|
24
|
+
icon: suggestion.icon,
|
|
25
|
+
size: chipSize,
|
|
26
|
+
onClick: () => onValueChange?.(suggestion.id)
|
|
27
|
+
}
|
|
28
|
+
) }, suggestion.id)) });
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
PromptSuggestions.displayName = "PromptSuggestions";
|
|
32
|
+
export {
|
|
33
|
+
PromptSuggestions
|
|
34
|
+
};
|