@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.
Files changed (109) hide show
  1. package/README.md +4 -4
  2. package/components/Accordion/Accordion.js +1 -0
  3. package/components/AgentPlan/AgentPlan.css +231 -0
  4. package/components/AgentPlan/AgentPlan.d.ts +41 -0
  5. package/components/AgentPlan/AgentPlan.js +100 -0
  6. package/components/AgentStatus/AgentStatus.js +1 -0
  7. package/components/AiButton/AiButton.js +1 -0
  8. package/components/AlertDialog/AlertDialog.js +1 -0
  9. package/components/AnchorNav/AnchorNav.css +63 -0
  10. package/components/AnchorNav/AnchorNav.d.ts +33 -0
  11. package/components/AnchorNav/AnchorNav.js +82 -0
  12. package/components/AppLayout/AppLayout.js +1 -0
  13. package/components/AppSidebar/AppSidebar.js +1 -0
  14. package/components/Avatar/Avatar.js +1 -0
  15. package/components/Button/Button.js +1 -0
  16. package/components/Card/Card.js +1 -0
  17. package/components/Carousel/Carousel.js +1 -0
  18. package/components/ChatThread/ChatThread.js +1 -0
  19. package/components/Checkbox/Checkbox.js +1 -0
  20. package/components/CodeBlock/CodeBlock.js +1 -0
  21. package/components/CodeDiff/CodeDiff.css +175 -0
  22. package/components/CodeDiff/CodeDiff.d.ts +58 -0
  23. package/components/CodeDiff/CodeDiff.js +114 -0
  24. package/components/ColorPicker/ColorPicker.js +1 -0
  25. package/components/Combobox/Combobox.js +1 -0
  26. package/components/CommandPalette/CommandPalette.js +1 -0
  27. package/components/Composer/Composer.js +1 -0
  28. package/components/ContactCard/ContactCard.js +1 -0
  29. package/components/ContextMenu/ContextMenu.js +1 -0
  30. package/components/DataTable/DataTable.css +105 -0
  31. package/components/DataTable/DataTable.d.ts +81 -0
  32. package/components/DataTable/DataTable.js +236 -0
  33. package/components/DateInput/DateInput.js +1 -0
  34. package/components/DatePicker/DatePicker.js +1 -0
  35. package/components/Dialog/Dialog.js +1 -0
  36. package/components/Drawer/Drawer.js +1 -0
  37. package/components/Dropdown/Dropdown.js +1 -0
  38. package/components/DropdownMenu/DropdownMenu.css +1 -1
  39. package/components/DropdownMenu/DropdownMenu.js +1 -0
  40. package/components/EventCalendar/EventCalendar.css +328 -0
  41. package/components/EventCalendar/EventCalendar.d.ts +51 -0
  42. package/components/EventCalendar/EventCalendar.js +214 -0
  43. package/components/Field/Field.js +1 -0
  44. package/components/Field/FieldContext.js +1 -0
  45. package/components/FileInput/FileInput.js +1 -0
  46. package/components/Input/Input.js +1 -0
  47. package/components/InterruptCard/InterruptCard.css +11 -21
  48. package/components/InterruptCard/InterruptCard.d.ts +5 -6
  49. package/components/MessageCard/MessageCard.css +1 -2
  50. package/components/ModelPicker/ModelPicker.css +240 -0
  51. package/components/ModelPicker/ModelPicker.d.ts +60 -0
  52. package/components/ModelPicker/ModelPicker.js +218 -0
  53. package/components/NavList/NavList.js +1 -0
  54. package/components/NotificationCenter/NotificationCenter.css +262 -0
  55. package/components/NotificationCenter/NotificationCenter.d.ts +72 -0
  56. package/components/NotificationCenter/NotificationCenter.js +129 -0
  57. package/components/NumberInput/NumberInput.css +154 -0
  58. package/components/NumberInput/NumberInput.d.ts +44 -0
  59. package/components/NumberInput/NumberInput.js +147 -0
  60. package/components/Pagination/Pagination.js +1 -0
  61. package/components/PinInput/PinInput.css +92 -0
  62. package/components/PinInput/PinInput.d.ts +46 -0
  63. package/components/PinInput/PinInput.js +161 -0
  64. package/components/Popover/Popover.css +5 -6
  65. package/components/Popover/Popover.js +1 -0
  66. package/components/RadioButton/RadioButton.js +1 -0
  67. package/components/Reasoning/Reasoning.js +1 -0
  68. package/components/SegmentedControl/SegmentedControl.js +1 -0
  69. package/components/SelectionCard/SelectionCard.js +1 -0
  70. package/components/ShaderField/ShaderField.js +1 -0
  71. package/components/ShaderField/field.glsl.d.ts +11 -5
  72. package/components/ShaderField/field.glsl.js +4 -3
  73. package/components/ShaderField/useShaderField.d.ts +19 -2
  74. package/components/ShaderField/useShaderField.js +18 -3
  75. package/components/Slider/Slider.js +1 -0
  76. package/components/Sparkline/Sparkline.css +52 -0
  77. package/components/Sparkline/Sparkline.d.ts +49 -0
  78. package/components/Sparkline/Sparkline.js +86 -0
  79. package/components/Stepper/Stepper.css +207 -0
  80. package/components/Stepper/Stepper.d.ts +41 -0
  81. package/components/Stepper/Stepper.js +61 -0
  82. package/components/Tabs/Tabs.js +1 -0
  83. package/components/TagInput/TagInput.css +160 -0
  84. package/components/TagInput/TagInput.d.ts +43 -0
  85. package/components/TagInput/TagInput.js +137 -0
  86. package/components/Textarea/Textarea.js +1 -0
  87. package/components/TimePicker/TimePicker.css +230 -0
  88. package/components/TimePicker/TimePicker.d.ts +46 -0
  89. package/components/TimePicker/TimePicker.js +246 -0
  90. package/components/Timeline/Timeline.css +17 -0
  91. package/components/Timeline/Timeline.d.ts +2 -0
  92. package/components/Timeline/Timeline.js +1 -0
  93. package/components/Toast/Toast.js +1 -0
  94. package/components/ToggleGroup/ToggleGroup.js +1 -0
  95. package/components/ToggleSwitch/ToggleSwitch.js +1 -0
  96. package/components/ToolCall/ToolCall.css +7 -11
  97. package/components/ToolCall/ToolCall.js +1 -0
  98. package/components/Tooltip/Tooltip.js +1 -0
  99. package/components/TreeView/TreeView.css +119 -0
  100. package/components/TreeView/TreeView.d.ts +45 -0
  101. package/components/TreeView/TreeView.js +232 -0
  102. package/components/registry.d.ts +22 -6
  103. package/components/registry.js +3 -1
  104. package/components/registry.json +246 -20
  105. package/components/registry.json.d.ts +246 -20
  106. package/components/registry.json.js +2 -2
  107. package/index.d.ts +16 -2
  108. package/index.js +32 -1
  109. 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
+ };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import "react";
3
4
  import "./Tabs.css";
@@ -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
+ };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import { Field } from "../Field/Field.js";
@@ -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 {};