@robr0/design-system 0.7.0 → 0.9.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 +4 -4
- package/components/Accordion/Accordion.js +1 -0
- package/components/AgentPlan/AgentPlan.css +231 -0
- package/components/AgentPlan/AgentPlan.d.ts +41 -0
- package/components/AgentPlan/AgentPlan.js +100 -0
- package/components/AgentStatus/AgentStatus.js +1 -0
- package/components/AiButton/AiButton.js +1 -0
- package/components/AlertDialog/AlertDialog.js +1 -0
- package/components/AnchorNav/AnchorNav.css +63 -0
- package/components/AnchorNav/AnchorNav.d.ts +33 -0
- package/components/AnchorNav/AnchorNav.js +82 -0
- package/components/AppLayout/AppLayout.js +1 -0
- package/components/AppSidebar/AppSidebar.js +1 -0
- package/components/Avatar/Avatar.js +1 -0
- package/components/Button/Button.js +1 -0
- package/components/Card/Card.js +1 -0
- package/components/Carousel/Carousel.js +1 -0
- package/components/ChatThread/ChatThread.js +1 -0
- package/components/Checkbox/Checkbox.js +1 -0
- package/components/CodeBlock/CodeBlock.js +1 -0
- package/components/CodeDiff/CodeDiff.css +175 -0
- package/components/CodeDiff/CodeDiff.d.ts +58 -0
- package/components/CodeDiff/CodeDiff.js +114 -0
- package/components/ColorPicker/ColorPicker.js +1 -0
- package/components/Combobox/Combobox.js +1 -0
- package/components/CommandPalette/CommandPalette.js +1 -0
- package/components/Composer/Composer.js +1 -0
- package/components/ContactCard/ContactCard.js +1 -0
- package/components/ContextMenu/ContextMenu.js +1 -0
- package/components/DataTable/DataTable.css +105 -0
- package/components/DataTable/DataTable.d.ts +81 -0
- package/components/DataTable/DataTable.js +236 -0
- package/components/DateInput/DateInput.js +1 -0
- package/components/DatePicker/DatePicker.js +1 -0
- package/components/Dialog/Dialog.js +1 -0
- package/components/Drawer/Drawer.js +1 -0
- package/components/Dropdown/Dropdown.js +1 -0
- package/components/DropdownMenu/DropdownMenu.css +1 -1
- package/components/DropdownMenu/DropdownMenu.js +1 -0
- package/components/EventCalendar/EventCalendar.css +328 -0
- package/components/EventCalendar/EventCalendar.d.ts +51 -0
- package/components/EventCalendar/EventCalendar.js +214 -0
- package/components/Field/Field.js +1 -0
- package/components/Field/FieldContext.js +1 -0
- package/components/FileInput/FileInput.js +1 -0
- package/components/Input/Input.js +1 -0
- package/components/InterruptCard/InterruptCard.css +11 -21
- package/components/InterruptCard/InterruptCard.d.ts +5 -6
- package/components/MessageCard/MessageCard.css +1 -2
- package/components/ModelPicker/ModelPicker.css +240 -0
- package/components/ModelPicker/ModelPicker.d.ts +60 -0
- package/components/ModelPicker/ModelPicker.js +218 -0
- package/components/NavList/NavList.js +1 -0
- package/components/NotificationCenter/NotificationCenter.css +262 -0
- package/components/NotificationCenter/NotificationCenter.d.ts +72 -0
- package/components/NotificationCenter/NotificationCenter.js +129 -0
- package/components/NumberInput/NumberInput.css +154 -0
- package/components/NumberInput/NumberInput.d.ts +44 -0
- package/components/NumberInput/NumberInput.js +147 -0
- package/components/Pagination/Pagination.js +1 -0
- package/components/PinInput/PinInput.css +92 -0
- package/components/PinInput/PinInput.d.ts +46 -0
- package/components/PinInput/PinInput.js +161 -0
- package/components/Popover/Popover.css +5 -6
- package/components/Popover/Popover.js +1 -0
- package/components/RadioButton/RadioButton.js +1 -0
- package/components/Reasoning/Reasoning.js +1 -0
- package/components/SegmentedControl/SegmentedControl.js +1 -0
- package/components/SelectionCard/SelectionCard.js +1 -0
- package/components/ShaderField/ShaderField.js +1 -0
- package/components/ShaderField/field.glsl.d.ts +11 -5
- package/components/ShaderField/field.glsl.js +4 -3
- package/components/ShaderField/useShaderField.d.ts +19 -2
- package/components/ShaderField/useShaderField.js +18 -3
- package/components/Slider/Slider.js +1 -0
- package/components/Sparkline/Sparkline.css +52 -0
- package/components/Sparkline/Sparkline.d.ts +49 -0
- package/components/Sparkline/Sparkline.js +86 -0
- package/components/Stepper/Stepper.css +207 -0
- package/components/Stepper/Stepper.d.ts +41 -0
- package/components/Stepper/Stepper.js +61 -0
- package/components/Tabs/Tabs.js +1 -0
- package/components/TagInput/TagInput.css +160 -0
- package/components/TagInput/TagInput.d.ts +43 -0
- package/components/TagInput/TagInput.js +137 -0
- package/components/Textarea/Textarea.js +1 -0
- package/components/TimePicker/TimePicker.css +230 -0
- package/components/TimePicker/TimePicker.d.ts +46 -0
- package/components/TimePicker/TimePicker.js +246 -0
- package/components/Timeline/Timeline.css +17 -0
- package/components/Timeline/Timeline.d.ts +2 -0
- package/components/Timeline/Timeline.js +1 -0
- package/components/Toast/Toast.js +1 -0
- package/components/ToggleGroup/ToggleGroup.js +1 -0
- package/components/ToggleSwitch/ToggleSwitch.js +1 -0
- package/components/ToolCall/ToolCall.css +7 -11
- package/components/ToolCall/ToolCall.js +1 -0
- package/components/Tooltip/Tooltip.js +1 -0
- package/components/TreeView/TreeView.css +119 -0
- package/components/TreeView/TreeView.d.ts +45 -0
- package/components/TreeView/TreeView.js +232 -0
- package/components/registry.d.ts +22 -6
- package/components/registry.js +3 -1
- package/components/registry.json +246 -20
- package/components/registry.json.d.ts +246 -20
- package/components/registry.json.js +2 -2
- package/index.d.ts +16 -2
- package/index.js +32 -1
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import "./Stepper.css";
|
|
5
|
+
import "../../fonts/material-symbols.css";
|
|
6
|
+
const Stepper = React.forwardRef(
|
|
7
|
+
({
|
|
8
|
+
steps,
|
|
9
|
+
activeStep,
|
|
10
|
+
onStepClick,
|
|
11
|
+
orientation = "horizontal",
|
|
12
|
+
className = "",
|
|
13
|
+
...rest
|
|
14
|
+
}, ref) => {
|
|
15
|
+
const baseClass = "ds-stepper";
|
|
16
|
+
const classes = [
|
|
17
|
+
baseClass,
|
|
18
|
+
orientation === "vertical" && `${baseClass}--vertical`,
|
|
19
|
+
className
|
|
20
|
+
].filter(Boolean).join(" ");
|
|
21
|
+
return /* @__PURE__ */ jsx("ol", { ...rest, ref, className: classes, children: steps.map((step, index) => {
|
|
22
|
+
const status = index < activeStep ? "complete" : index === activeStep ? "active" : "upcoming";
|
|
23
|
+
const interactive = Boolean(onStepClick) && status !== "upcoming";
|
|
24
|
+
const indicator = /* @__PURE__ */ jsx("span", { className: `${baseClass}__indicator`, "aria-hidden": "true", children: status === "complete" ? /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", children: "check" }) : index + 1 });
|
|
25
|
+
const text = /* @__PURE__ */ jsxs("span", { className: `${baseClass}__text`, children: [
|
|
26
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: step.label }),
|
|
27
|
+
step.description && /* @__PURE__ */ jsx("span", { className: `${baseClass}__description`, children: step.description })
|
|
28
|
+
] });
|
|
29
|
+
return /* @__PURE__ */ jsxs(
|
|
30
|
+
"li",
|
|
31
|
+
{
|
|
32
|
+
className: `${baseClass}__step ${baseClass}__step--${status}`,
|
|
33
|
+
"aria-current": status === "active" ? "step" : void 0,
|
|
34
|
+
children: [
|
|
35
|
+
interactive ? /* @__PURE__ */ jsxs(
|
|
36
|
+
"button",
|
|
37
|
+
{
|
|
38
|
+
type: "button",
|
|
39
|
+
className: `${baseClass}__content`,
|
|
40
|
+
onClick: () => onStepClick?.(index),
|
|
41
|
+
children: [
|
|
42
|
+
indicator,
|
|
43
|
+
text
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
) : /* @__PURE__ */ jsxs("span", { className: `${baseClass}__content`, children: [
|
|
47
|
+
indicator,
|
|
48
|
+
text
|
|
49
|
+
] }),
|
|
50
|
+
index < steps.length - 1 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__connector`, "aria-hidden": "true" })
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
index
|
|
54
|
+
);
|
|
55
|
+
}) });
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
Stepper.displayName = "Stepper";
|
|
59
|
+
export {
|
|
60
|
+
Stepper
|
|
61
|
+
};
|
package/components/Tabs/Tabs.js
CHANGED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
TAG INPUT COMPONENT
|
|
3
|
+
Multi-value text input; committed entries
|
|
4
|
+
held as removable tags inside the field
|
|
5
|
+
============================================ */
|
|
6
|
+
|
|
7
|
+
.ds-taginput {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
gap: var(--gap-sm); /* 8px */
|
|
11
|
+
width: 100%;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* ============================================
|
|
15
|
+
SHELL — the focus-within box matching
|
|
16
|
+
Input's field border and radius
|
|
17
|
+
============================================ */
|
|
18
|
+
|
|
19
|
+
.ds-taginput__shell {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-wrap: wrap;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: var(--gap-xs); /* 4px */
|
|
24
|
+
padding: var(--padding-xs) var(--padding-md); /* 6px 16px */
|
|
25
|
+
border: var(--border-xs) solid var(--color-input-border-primary);
|
|
26
|
+
border-radius: var(--radius-md); /* 12px */
|
|
27
|
+
background-color: var(--color-input-bg-primary);
|
|
28
|
+
cursor: text;
|
|
29
|
+
transition: border-color var(--motion-duration-base) var(--motion-ease-standard);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ds-taginput__shell:hover:not(:focus-within) {
|
|
33
|
+
border-color: var(--color-input-border-hover);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.ds-taginput__shell:focus-within {
|
|
37
|
+
border-color: var(--color-input-border-selected);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ============================================
|
|
41
|
+
TAG — pill holding one committed entry
|
|
42
|
+
============================================ */
|
|
43
|
+
|
|
44
|
+
.ds-taginput__tag {
|
|
45
|
+
display: inline-flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
gap: var(--gap-xxs); /* 2px */
|
|
48
|
+
padding: var(--padding-xxxs) var(--padding-sm); /* 2px 8px */
|
|
49
|
+
border-radius: var(--radius-full);
|
|
50
|
+
background-color: var(--color-bg-container-secondary);
|
|
51
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
52
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
53
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
54
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
55
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
56
|
+
color: var(--color-text-primary);
|
|
57
|
+
max-width: 100%;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ds-taginput__tag-label {
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
text-overflow: ellipsis;
|
|
63
|
+
white-space: nowrap;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* ============================================
|
|
67
|
+
REMOVE — trailing close button on each tag
|
|
68
|
+
============================================ */
|
|
69
|
+
|
|
70
|
+
.ds-taginput__remove {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
margin: 0;
|
|
76
|
+
padding: 0;
|
|
77
|
+
border: none;
|
|
78
|
+
border-radius: var(--radius-full);
|
|
79
|
+
background-color: transparent;
|
|
80
|
+
color: var(--color-icon-primary);
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
transition: color var(--motion-duration-base) var(--motion-ease-standard);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.ds-taginput__remove:not(:disabled):hover {
|
|
86
|
+
color: var(--color-text-primary);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.ds-taginput__remove:focus-visible {
|
|
90
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
91
|
+
outline-offset: 1px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.ds-taginput__remove .material-symbols-rounded {
|
|
95
|
+
--icon-size: var(--icon-size-sm);
|
|
96
|
+
line-height: 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ============================================
|
|
100
|
+
INPUT — borderless draft field sharing the
|
|
101
|
+
shell's line with the tags
|
|
102
|
+
============================================ */
|
|
103
|
+
|
|
104
|
+
.ds-taginput__input {
|
|
105
|
+
flex: 1;
|
|
106
|
+
min-width: 0;
|
|
107
|
+
border: none;
|
|
108
|
+
outline: none;
|
|
109
|
+
background-color: transparent;
|
|
110
|
+
padding: 0;
|
|
111
|
+
font-family: var(--font-paragraph-family);
|
|
112
|
+
font-size: var(--font-paragraph-size);
|
|
113
|
+
font-weight: var(--font-paragraph-weight);
|
|
114
|
+
line-height: var(--font-paragraph-line-height);
|
|
115
|
+
letter-spacing: var(--font-paragraph-letter-spacing);
|
|
116
|
+
color: var(--color-input-text-primary);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ds-taginput__input::placeholder {
|
|
120
|
+
color: var(--color-input-text-placeholder);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ============================================
|
|
124
|
+
ERROR STATE
|
|
125
|
+
============================================ */
|
|
126
|
+
|
|
127
|
+
.ds-taginput--error .ds-taginput__shell,
|
|
128
|
+
.ds-taginput--error .ds-taginput__shell:hover:not(:focus-within),
|
|
129
|
+
.ds-taginput--error .ds-taginput__shell:focus-within {
|
|
130
|
+
border-color: var(--color-status-error-border);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ============================================
|
|
134
|
+
DISABLED STATE
|
|
135
|
+
============================================ */
|
|
136
|
+
|
|
137
|
+
.ds-taginput--disabled .ds-taginput__shell {
|
|
138
|
+
opacity: 0.4;
|
|
139
|
+
cursor: not-allowed;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.ds-taginput--disabled .ds-taginput__input,
|
|
143
|
+
.ds-taginput--disabled .ds-taginput__remove {
|
|
144
|
+
cursor: not-allowed;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* ============================================
|
|
148
|
+
SIZE — COMPACT
|
|
149
|
+
============================================ */
|
|
150
|
+
|
|
151
|
+
.ds-taginput--compact .ds-taginput__shell {
|
|
152
|
+
gap: var(--gap-xxs); /* 2px */
|
|
153
|
+
padding: var(--padding-xxs) var(--padding-sm-md); /* 4px 12px */
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.ds-taginput--compact .ds-taginput__input {
|
|
157
|
+
font-size: var(--font-paragraph-sm-size);
|
|
158
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
159
|
+
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
160
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Props owned by TagInput itself — everything else falls through to the inner
|
|
4
|
+
* <input>. The native `value`/`defaultValue` are excluded from the interface:
|
|
5
|
+
* the draft text is component-managed, and the committed entries live in
|
|
6
|
+
* `values`/`defaultValues` instead.
|
|
7
|
+
*/
|
|
8
|
+
type TagInputOwnProps = {
|
|
9
|
+
/** Field label text */
|
|
10
|
+
label?: string;
|
|
11
|
+
/** Committed tags (controlled) — pair with `onValuesChange` */
|
|
12
|
+
values?: string[];
|
|
13
|
+
/** Initial tags when uncontrolled */
|
|
14
|
+
defaultValues?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* Convenience callback receiving the full tag array whenever a tag is added
|
|
17
|
+
* or removed. The native `onChange` still fires for draft-text edits, which
|
|
18
|
+
* keeps the standard React event signature for form libraries.
|
|
19
|
+
*/
|
|
20
|
+
onValuesChange?: (values: string[]) => void;
|
|
21
|
+
/** Maximum number of tags — at the limit the input stops accepting new ones */
|
|
22
|
+
maxTags?: number;
|
|
23
|
+
/** Component size (not the native character-width `size` attribute) */
|
|
24
|
+
size?: 'default' | 'compact';
|
|
25
|
+
/** Error state — shows error styling and message */
|
|
26
|
+
error?: boolean;
|
|
27
|
+
/** Helper or error message displayed below the control */
|
|
28
|
+
helperText?: string;
|
|
29
|
+
/** Additional CSS classes — applied to the wrapper, not the <input> */
|
|
30
|
+
className?: string;
|
|
31
|
+
};
|
|
32
|
+
export interface TagInputProps extends TagInputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof TagInputOwnProps | 'type' | 'value' | 'defaultValue'> {
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Multi-value text input holding committed entries as removable tags.
|
|
36
|
+
*
|
|
37
|
+
* Enter or comma commits the trimmed draft as a tag (duplicates and empty
|
|
38
|
+
* drafts are ignored), Backspace on an empty draft removes the last tag, and
|
|
39
|
+
* each tag carries its own labelled remove button. Forwards a ref to the inner
|
|
40
|
+
* `<input>` and spreads unrecognised props onto it.
|
|
41
|
+
*/
|
|
42
|
+
export declare const TagInput: React.ForwardRefExoticComponent<TagInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useRef } from "react";
|
|
4
|
+
import { Field } from "../Field/Field.js";
|
|
5
|
+
import "./TagInput.css";
|
|
6
|
+
import "../../fonts/material-symbols.css";
|
|
7
|
+
const TagInput = React.forwardRef(
|
|
8
|
+
({
|
|
9
|
+
label,
|
|
10
|
+
values,
|
|
11
|
+
defaultValues,
|
|
12
|
+
onValuesChange,
|
|
13
|
+
maxTags,
|
|
14
|
+
size = "default",
|
|
15
|
+
error = false,
|
|
16
|
+
helperText,
|
|
17
|
+
className = "",
|
|
18
|
+
placeholder = "",
|
|
19
|
+
disabled = false,
|
|
20
|
+
required = false,
|
|
21
|
+
onChange,
|
|
22
|
+
onKeyDown,
|
|
23
|
+
name,
|
|
24
|
+
id,
|
|
25
|
+
...rest
|
|
26
|
+
}, ref) => {
|
|
27
|
+
const baseClass = "ds-taginput";
|
|
28
|
+
const isControlled = values !== void 0;
|
|
29
|
+
const [internalValues, setInternalValues] = useState(defaultValues ?? []);
|
|
30
|
+
const [draft, setDraft] = useState("");
|
|
31
|
+
const inputRef = useRef(null);
|
|
32
|
+
const tags = isControlled ? values : internalValues;
|
|
33
|
+
const atLimit = maxTags !== void 0 && tags.length >= maxTags;
|
|
34
|
+
const classes = [
|
|
35
|
+
baseClass,
|
|
36
|
+
size === "compact" ? `${baseClass}--compact` : "",
|
|
37
|
+
error ? `${baseClass}--error` : "",
|
|
38
|
+
disabled ? `${baseClass}--disabled` : "",
|
|
39
|
+
className
|
|
40
|
+
].filter(Boolean).join(" ");
|
|
41
|
+
const setRef = (node) => {
|
|
42
|
+
inputRef.current = node;
|
|
43
|
+
if (typeof ref === "function") ref(node);
|
|
44
|
+
else if (ref) ref.current = node;
|
|
45
|
+
};
|
|
46
|
+
const updateValues = (next) => {
|
|
47
|
+
if (!isControlled) setInternalValues(next);
|
|
48
|
+
onValuesChange?.(next);
|
|
49
|
+
};
|
|
50
|
+
const commitDraft = () => {
|
|
51
|
+
const tag = draft.trim();
|
|
52
|
+
if (!tag || atLimit) return;
|
|
53
|
+
if (!tags.includes(tag)) updateValues([...tags, tag]);
|
|
54
|
+
setDraft("");
|
|
55
|
+
};
|
|
56
|
+
const removeTag = (index) => {
|
|
57
|
+
if (disabled) return;
|
|
58
|
+
updateValues(tags.filter((_, i) => i !== index));
|
|
59
|
+
};
|
|
60
|
+
const handleChange = (e) => {
|
|
61
|
+
onChange?.(e);
|
|
62
|
+
setDraft(e.target.value);
|
|
63
|
+
};
|
|
64
|
+
const handleKeyDown = (e) => {
|
|
65
|
+
onKeyDown?.(e);
|
|
66
|
+
if (e.defaultPrevented) return;
|
|
67
|
+
if (e.key === "Enter" && draft.trim() !== "") {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
commitDraft();
|
|
70
|
+
} else if (e.key === ",") {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
commitDraft();
|
|
73
|
+
} else if (e.key === "Backspace" && draft === "" && tags.length > 0) {
|
|
74
|
+
removeTag(tags.length - 1);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const handleShellClick = (e) => {
|
|
78
|
+
if (disabled) return;
|
|
79
|
+
if (e.target.closest(`.${baseClass}__remove`)) return;
|
|
80
|
+
inputRef.current?.focus();
|
|
81
|
+
};
|
|
82
|
+
const inputId = id || name || label?.toLowerCase().replace(/\s+/g, "-");
|
|
83
|
+
const describedBy = helperText ? `${inputId}-helper` : rest["aria-describedby"];
|
|
84
|
+
return /* @__PURE__ */ jsx(
|
|
85
|
+
Field,
|
|
86
|
+
{
|
|
87
|
+
className: classes,
|
|
88
|
+
label,
|
|
89
|
+
helperText,
|
|
90
|
+
error,
|
|
91
|
+
required,
|
|
92
|
+
disabled,
|
|
93
|
+
size,
|
|
94
|
+
id: inputId,
|
|
95
|
+
children: /* @__PURE__ */ jsxs("div", { className: `${baseClass}__shell`, onClick: handleShellClick, children: [
|
|
96
|
+
tags.map((tag, index) => /* @__PURE__ */ jsxs("span", { className: `${baseClass}__tag`, children: [
|
|
97
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__tag-label`, children: tag }),
|
|
98
|
+
/* @__PURE__ */ jsx(
|
|
99
|
+
"button",
|
|
100
|
+
{
|
|
101
|
+
type: "button",
|
|
102
|
+
className: `${baseClass}__remove`,
|
|
103
|
+
onClick: () => removeTag(index),
|
|
104
|
+
disabled,
|
|
105
|
+
"aria-label": `Remove ${tag}`,
|
|
106
|
+
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
] }, tag)),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
"input",
|
|
112
|
+
{
|
|
113
|
+
...rest,
|
|
114
|
+
ref: setRef,
|
|
115
|
+
className: `${baseClass}__input`,
|
|
116
|
+
type: "text",
|
|
117
|
+
id: inputId,
|
|
118
|
+
name,
|
|
119
|
+
value: draft,
|
|
120
|
+
placeholder: atLimit ? "" : placeholder,
|
|
121
|
+
disabled,
|
|
122
|
+
required,
|
|
123
|
+
"aria-invalid": error,
|
|
124
|
+
"aria-describedby": describedBy,
|
|
125
|
+
onChange: handleChange,
|
|
126
|
+
onKeyDown: handleKeyDown
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
] })
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
TagInput.displayName = "TagInput";
|
|
135
|
+
export {
|
|
136
|
+
TagInput
|
|
137
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
TIMEPICKER COMPONENT
|
|
3
|
+
Time-of-day field with a dropdown listbox of
|
|
4
|
+
generated times. Mirrors Dropdown's anatomy;
|
|
5
|
+
the trigger is a real <button> styled as an
|
|
6
|
+
input.
|
|
7
|
+
============================================ */
|
|
8
|
+
|
|
9
|
+
/* ============================================
|
|
10
|
+
BASE
|
|
11
|
+
============================================ */
|
|
12
|
+
|
|
13
|
+
.ds-timepicker {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: var(--gap-sm); /* 8px */
|
|
17
|
+
width: 100%;
|
|
18
|
+
position: relative;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* ============================================
|
|
22
|
+
TRIGGER
|
|
23
|
+
============================================ */
|
|
24
|
+
|
|
25
|
+
.ds-timepicker__trigger {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: var(--gap-sm); /* 8px */
|
|
29
|
+
width: 100%;
|
|
30
|
+
padding: var(--padding-sm) var(--padding-md); /* 8px 16px */
|
|
31
|
+
border: var(--border-xs) solid var(--color-input-border-primary);
|
|
32
|
+
border-radius: var(--radius-md); /* 12px — input shape, not the button pill */
|
|
33
|
+
background-color: var(--color-input-bg-primary);
|
|
34
|
+
text-align: left;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
transition: border-color var(--motion-duration-base) var(--motion-ease-standard);
|
|
37
|
+
outline: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ds-timepicker__trigger:hover:not(:focus-visible):not(:disabled) {
|
|
41
|
+
border-color: var(--color-input-border-hover);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ds-timepicker__trigger:focus-visible {
|
|
45
|
+
border-color: var(--color-input-border-selected);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ds-timepicker--open .ds-timepicker__trigger {
|
|
49
|
+
border-color: var(--color-input-border-selected);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ============================================
|
|
53
|
+
CLOCK ICON
|
|
54
|
+
============================================ */
|
|
55
|
+
|
|
56
|
+
.ds-timepicker__icon {
|
|
57
|
+
--icon-size: var(--icon-size-sm);
|
|
58
|
+
color: var(--color-icon-primary);
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* ============================================
|
|
63
|
+
VALUE / PLACEHOLDER
|
|
64
|
+
============================================ */
|
|
65
|
+
|
|
66
|
+
.ds-timepicker__value {
|
|
67
|
+
font-family: var(--font-paragraph-em-family);
|
|
68
|
+
font-size: var(--font-paragraph-em-size);
|
|
69
|
+
font-weight: var(--font-paragraph-em-weight);
|
|
70
|
+
line-height: var(--font-paragraph-em-line-height);
|
|
71
|
+
letter-spacing: var(--font-paragraph-em-letter-spacing);
|
|
72
|
+
color: var(--color-input-text-primary);
|
|
73
|
+
flex: 1;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.ds-timepicker__value--placeholder {
|
|
80
|
+
color: var(--color-input-text-placeholder);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* ============================================
|
|
84
|
+
CHEVRON
|
|
85
|
+
============================================ */
|
|
86
|
+
|
|
87
|
+
.ds-timepicker__chevron {
|
|
88
|
+
--icon-size: var(--icon-size-md);
|
|
89
|
+
color: var(--color-icon-primary);
|
|
90
|
+
transition: transform var(--motion-duration-base) var(--motion-ease-standard);
|
|
91
|
+
flex-shrink: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.ds-timepicker--open .ds-timepicker__chevron {
|
|
95
|
+
transform: rotate(180deg);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ============================================
|
|
99
|
+
MENU
|
|
100
|
+
============================================ */
|
|
101
|
+
|
|
102
|
+
.ds-timepicker__menu {
|
|
103
|
+
position: absolute;
|
|
104
|
+
top: 100%;
|
|
105
|
+
left: 0;
|
|
106
|
+
right: 0;
|
|
107
|
+
margin-top: var(--gap-xxs); /* 2px */
|
|
108
|
+
padding: var(--padding-xxs); /* inset gap on all sides */
|
|
109
|
+
border: var(--border-xs) solid var(--color-input-border-primary);
|
|
110
|
+
border-radius: var(--radius-md); /* 12px */
|
|
111
|
+
background-color: var(--color-bg-page-primary);
|
|
112
|
+
list-style: none;
|
|
113
|
+
max-height: 240px;
|
|
114
|
+
overflow-y: auto;
|
|
115
|
+
z-index: 10;
|
|
116
|
+
box-shadow: var(--shadow-floating);
|
|
117
|
+
|
|
118
|
+
/* Animation */
|
|
119
|
+
animation: ds-timepicker-appear var(--motion-duration-fast) var(--motion-ease-entrance);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@keyframes ds-timepicker-appear {
|
|
123
|
+
from {
|
|
124
|
+
opacity: 0;
|
|
125
|
+
transform: scale(0.96);
|
|
126
|
+
}
|
|
127
|
+
to {
|
|
128
|
+
opacity: 1;
|
|
129
|
+
transform: scale(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ============================================
|
|
134
|
+
OPTION
|
|
135
|
+
============================================ */
|
|
136
|
+
|
|
137
|
+
.ds-timepicker__option {
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
justify-content: space-between;
|
|
141
|
+
padding: var(--padding-sm) var(--padding-sm-md); /* 8px 12px */
|
|
142
|
+
border-radius: var(--radius-sm);
|
|
143
|
+
font-family: var(--font-paragraph-em-family);
|
|
144
|
+
font-size: var(--font-paragraph-em-size);
|
|
145
|
+
font-weight: var(--font-paragraph-em-weight);
|
|
146
|
+
line-height: var(--font-paragraph-em-line-height);
|
|
147
|
+
letter-spacing: var(--font-paragraph-em-letter-spacing);
|
|
148
|
+
font-variant-numeric: tabular-nums;
|
|
149
|
+
color: var(--color-text-primary);
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.ds-timepicker__option:hover {
|
|
155
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.ds-timepicker__option--focused {
|
|
159
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.ds-timepicker__option--selected {
|
|
163
|
+
color: var(--color-action-primary-text-tertiary);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* ============================================
|
|
167
|
+
CHECK ICON
|
|
168
|
+
============================================ */
|
|
169
|
+
|
|
170
|
+
.ds-timepicker__check {
|
|
171
|
+
--icon-size: var(--icon-size-md);
|
|
172
|
+
color: var(--color-action-primary-bg);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* ============================================
|
|
176
|
+
ERROR STATE
|
|
177
|
+
============================================ */
|
|
178
|
+
|
|
179
|
+
.ds-timepicker--error .ds-timepicker__trigger {
|
|
180
|
+
border-color: var(--color-status-error-border);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* ============================================
|
|
184
|
+
DISABLED STATE
|
|
185
|
+
============================================ */
|
|
186
|
+
|
|
187
|
+
.ds-timepicker--disabled .ds-timepicker__trigger {
|
|
188
|
+
background-color: var(--color-input-bg-disabled);
|
|
189
|
+
border-color: var(--color-input-border-disabled);
|
|
190
|
+
opacity: 0.4;
|
|
191
|
+
cursor: not-allowed;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.ds-timepicker--disabled .ds-timepicker__value {
|
|
195
|
+
color: var(--color-input-text-disabled);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.ds-timepicker--disabled .ds-timepicker__icon,
|
|
199
|
+
.ds-timepicker--disabled .ds-timepicker__chevron {
|
|
200
|
+
color: var(--color-input-text-disabled);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ============================================
|
|
204
|
+
SIZE — COMPACT
|
|
205
|
+
============================================ */
|
|
206
|
+
|
|
207
|
+
.ds-timepicker--compact .ds-timepicker__trigger {
|
|
208
|
+
padding: var(--padding-xs) var(--padding-sm-md); /* 6px 12px */
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ds-timepicker--compact .ds-timepicker__value {
|
|
212
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
213
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
214
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.ds-timepicker--compact .ds-timepicker__chevron {
|
|
218
|
+
--icon-size: var(--icon-size-sm);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.ds-timepicker--compact .ds-timepicker__option {
|
|
222
|
+
padding: var(--padding-xs) var(--padding-sm); /* 6px 8px */
|
|
223
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
224
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
225
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.ds-timepicker--compact .ds-timepicker__check {
|
|
229
|
+
--icon-size: var(--icon-size-sm);
|
|
230
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by TimePicker itself — everything else falls through to the trigger button. */
|
|
3
|
+
type TimePickerOwnProps = {
|
|
4
|
+
/** Label text rendered above the trigger */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Placeholder shown when no time is selected */
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
/** Currently selected time as a 24-hour "HH:MM" string (controlled) */
|
|
9
|
+
value?: string;
|
|
10
|
+
/** Initially selected time as a 24-hour "HH:MM" string (uncontrolled) */
|
|
11
|
+
defaultValue?: string;
|
|
12
|
+
/** Earliest generated option, as a 24-hour "HH:MM" string */
|
|
13
|
+
minTime?: string;
|
|
14
|
+
/** Latest generated option, as a 24-hour "HH:MM" string */
|
|
15
|
+
maxTime?: string;
|
|
16
|
+
/** Minutes between generated options */
|
|
17
|
+
stepMinutes?: number;
|
|
18
|
+
/** Display format for the trigger and options — the value stays a 24-hour "HH:MM" string either way */
|
|
19
|
+
hourFormat?: '12' | '24';
|
|
20
|
+
/** Component size */
|
|
21
|
+
size?: 'default' | 'compact';
|
|
22
|
+
/** Whether the picker is disabled */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/** Whether the field is required */
|
|
25
|
+
required?: boolean;
|
|
26
|
+
/** Error state */
|
|
27
|
+
error?: boolean;
|
|
28
|
+
/** Helper or error message */
|
|
29
|
+
helperText?: string;
|
|
30
|
+
/** Called with the newly selected 24-hour "HH:MM" value */
|
|
31
|
+
onValueChange?: (value: string) => void;
|
|
32
|
+
/** Additional CSS classes */
|
|
33
|
+
className?: string;
|
|
34
|
+
};
|
|
35
|
+
export interface TimePickerProps extends TimePickerOwnProps, Omit<React.ComponentPropsWithoutRef<'button'>, keyof TimePickerOwnProps | 'type'> {
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Time-of-day form field. A button-shaped trigger opens a scrollable
|
|
39
|
+
* `role="listbox"` of times generated from `minTime`/`maxTime`/`stepMinutes`,
|
|
40
|
+
* with full keyboard navigation. The value is always a 24-hour "HH:MM"
|
|
41
|
+
* string; `hourFormat` only changes the display.
|
|
42
|
+
*
|
|
43
|
+
* Forwards a ref to the trigger button and spreads unrecognised props onto it.
|
|
44
|
+
*/
|
|
45
|
+
export declare const TimePicker: React.ForwardRefExoticComponent<TimePickerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
46
|
+
export {};
|