@robr0/design-system 0.2.0 → 0.4.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 (99) hide show
  1. package/README.md +33 -28
  2. package/components/Accordion/Accordion.js +0 -1
  3. package/components/AgentStatus/AgentStatus.css +436 -0
  4. package/components/AgentStatus/AgentStatus.d.ts +37 -0
  5. package/components/AgentStatus/AgentStatus.js +53 -0
  6. package/components/AgentStatus/AgentStatusPatterns.d.ts +17 -0
  7. package/components/AgentStatus/AgentStatusPatterns.js +20 -0
  8. package/components/AlertDialog/AlertDialog.css +5 -5
  9. package/components/Avatar/Avatar.js +1 -0
  10. package/components/Breadcrumb/Breadcrumb.d.ts +7 -1
  11. package/components/Breadcrumb/Breadcrumb.js +3 -2
  12. package/components/Button/Button.css +18 -0
  13. package/components/Button/Button.d.ts +20 -2
  14. package/components/Button/Button.js +13 -3
  15. package/components/ButtonGroup/ButtonGroup.js +2 -9
  16. package/components/Card/Card.css +6 -0
  17. package/components/Checkbox/Checkbox.d.ts +17 -3
  18. package/components/CircularButton/CircularButton.css +11 -0
  19. package/components/CircularButton/CircularButton.d.ts +15 -3
  20. package/components/CircularButton/CircularButton.js +19 -6
  21. package/components/CodeBlock/CodeBlock.js +10 -1
  22. package/components/ColorPicker/ColorPicker.css +278 -0
  23. package/components/ColorPicker/ColorPicker.d.ts +50 -0
  24. package/components/ColorPicker/ColorPicker.js +338 -0
  25. package/components/Combobox/Combobox.css +0 -36
  26. package/components/Combobox/Combobox.d.ts +10 -2
  27. package/components/Combobox/Combobox.js +90 -82
  28. package/components/CommandPalette/CommandPalette.css +1 -15
  29. package/components/CommandPalette/CommandPalette.js +6 -5
  30. package/components/ContextMenu/ContextMenu.css +258 -0
  31. package/components/ContextMenu/ContextMenu.d.ts +26 -0
  32. package/components/ContextMenu/ContextMenu.js +350 -0
  33. package/components/DateInput/DateInput.css +0 -36
  34. package/components/DateInput/DateInput.d.ts +5 -1
  35. package/components/DateInput/DateInput.js +37 -33
  36. package/components/DatePicker/DatePicker.js +45 -34
  37. package/components/Dialog/Dialog.css +5 -5
  38. package/components/Dialog/Dialog.js +5 -4
  39. package/components/Drawer/Drawer.js +7 -2
  40. package/components/Dropdown/Dropdown.css +2 -40
  41. package/components/Dropdown/Dropdown.d.ts +10 -2
  42. package/components/Dropdown/Dropdown.js +119 -109
  43. package/components/DropdownMenu/DropdownMenu.css +2 -4
  44. package/components/DropdownMenu/DropdownMenu.js +10 -7
  45. package/components/Field/Field.css +80 -0
  46. package/components/Field/Field.d.ts +50 -0
  47. package/components/Field/Field.js +58 -0
  48. package/components/Field/FieldContext.d.ts +42 -0
  49. package/components/Field/FieldContext.js +7 -0
  50. package/components/FileInput/FileInput.css +0 -36
  51. package/components/FileInput/FileInput.d.ts +5 -1
  52. package/components/FileInput/FileInput.js +111 -103
  53. package/components/Input/Input.css +0 -43
  54. package/components/Input/Input.d.ts +5 -1
  55. package/components/Input/Input.js +51 -46
  56. package/components/Kbd/Kbd.css +28 -0
  57. package/components/Kbd/Kbd.d.ts +19 -0
  58. package/components/Kbd/Kbd.js +14 -0
  59. package/components/LinkList/LinkList.d.ts +1 -0
  60. package/components/Popover/Popover.d.ts +0 -6
  61. package/components/Popover/Popover.js +22 -16
  62. package/components/ProgressBar/ProgressBar.js +1 -1
  63. package/components/RadioButton/RadioButton.d.ts +17 -3
  64. package/components/Reasoning/Reasoning.css +152 -0
  65. package/components/Reasoning/Reasoning.d.ts +36 -0
  66. package/components/Reasoning/Reasoning.js +69 -0
  67. package/components/SelectionCard/SelectionCard.css +6 -2
  68. package/components/SelectionCard/SelectionCard.d.ts +5 -1
  69. package/components/Skeleton/Skeleton.css +2 -0
  70. package/components/Slider/Slider.d.ts +5 -1
  71. package/components/Spinner/Spinner.css +11 -0
  72. package/components/Spinner/Spinner.d.ts +2 -2
  73. package/components/Swatch/Swatch.css +52 -0
  74. package/components/Swatch/Swatch.d.ts +34 -0
  75. package/components/Swatch/Swatch.js +44 -0
  76. package/components/Table/Table.css +19 -0
  77. package/components/Table/Table.js +1 -1
  78. package/components/Textarea/Textarea.css +0 -43
  79. package/components/Textarea/Textarea.d.ts +5 -1
  80. package/components/Textarea/Textarea.js +37 -35
  81. package/components/Timeline/Timeline.d.ts +2 -0
  82. package/components/ToggleGroup/ToggleGroup.d.ts +10 -2
  83. package/components/ToggleSwitch/ToggleSwitch.css +7 -2
  84. package/components/ToggleSwitch/ToggleSwitch.d.ts +10 -2
  85. package/components/ToolCall/ToolCall.css +213 -0
  86. package/components/ToolCall/ToolCall.d.ts +43 -0
  87. package/components/ToolCall/ToolCall.js +89 -0
  88. package/components/registry.d.ts +47 -10
  89. package/components/registry.js +5 -1
  90. package/components/registry.json +523 -56
  91. package/components/registry.json.d.ts +523 -56
  92. package/components/registry.json.js +4 -1
  93. package/index.d.ts +12 -1
  94. package/index.js +24 -1
  95. package/package.json +8 -4
  96. package/tokens/registry.json +1 -0
  97. package/tokens/registry.json.d.ts +1 -0
  98. package/tokens/registry.json.js +1 -1
  99. package/tokens/tokens-motion.css +3 -1
@@ -16,19 +16,6 @@
16
16
  LABEL
17
17
  ============================================ */
18
18
 
19
- .ds-combobox__label {
20
- font-family: var(--font-paragraph-em-family);
21
- font-size: var(--font-paragraph-em-size);
22
- font-weight: var(--font-paragraph-em-weight);
23
- line-height: var(--font-paragraph-em-line-height);
24
- letter-spacing: var(--font-paragraph-em-letter-spacing);
25
- color: var(--color-text-primary);
26
- }
27
-
28
- .ds-combobox__required {
29
- color: var(--color-core-accent-coral);
30
- }
31
-
32
19
  /* ============================================
33
20
  CONTROL
34
21
  ============================================ */
@@ -324,16 +311,6 @@
324
311
  HELPER TEXT
325
312
  ============================================ */
326
313
 
327
- .ds-combobox__helper {
328
- font-family: var(--font-paragraph-sm-family);
329
- font-size: var(--font-paragraph-sm-size);
330
- font-weight: var(--font-paragraph-sm-weight);
331
- line-height: var(--font-paragraph-sm-line-height);
332
- letter-spacing: var(--font-paragraph-sm-letter-spacing);
333
- color: var(--color-text-tertiary);
334
- margin: 0;
335
- }
336
-
337
314
  /* ============================================
338
315
  ERROR STATE
339
316
  ============================================ */
@@ -342,18 +319,10 @@
342
319
  border-color: var(--color-status-error-border);
343
320
  }
344
321
 
345
- .ds-combobox--error .ds-combobox__helper {
346
- color: var(--color-status-error-border);
347
- }
348
-
349
322
  /* ============================================
350
323
  DISABLED STATE
351
324
  ============================================ */
352
325
 
353
- .ds-combobox--disabled .ds-combobox__label {
354
- color: var(--color-input-text-disabled);
355
- }
356
-
357
326
  .ds-combobox--disabled .ds-combobox__control {
358
327
  background-color: var(--color-input-bg-disabled);
359
328
  border-color: var(--color-input-border-disabled);
@@ -378,11 +347,6 @@
378
347
  padding: var(--padding-xs) var(--padding-sm-md); /* 6px 12px */
379
348
  }
380
349
 
381
- .ds-combobox--compact .ds-combobox__label {
382
- font-size: var(--font-paragraph-sm-size);
383
- line-height: var(--font-paragraph-sm-line-height);
384
- }
385
-
386
350
  .ds-combobox--compact .ds-combobox__input,
387
351
  .ds-combobox--compact .ds-combobox__option-label {
388
352
  font-size: var(--font-paragraph-sm-em-size);
@@ -59,9 +59,17 @@ type ComboboxOwnProps = {
59
59
  manualFiltering?: boolean;
60
60
  /** Additional CSS classes */
61
61
  className?: string;
62
- /** @deprecated Use `onValueChange` instead. */
62
+ /**
63
+ * Legacy change handler, kept for backwards compatibility.
64
+ *
65
+ * @deprecated Use `onValueChange` instead.
66
+ */
63
67
  onChange?: (value: string | string[]) => void;
64
- /** @deprecated Pass the native `aria-label` attribute instead. */
68
+ /**
69
+ * Legacy accessible-name prop.
70
+ *
71
+ * @deprecated Pass the native `aria-label` attribute instead.
72
+ */
65
73
  ariaLabel?: string;
66
74
  /**
67
75
  * Used only as a fallback for deriving the element id (`id || name || generated`).
@@ -1,5 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import React, { useState, useRef, useId, useMemo, useCallback, useEffect } from "react";
3
+ import { Field } from "../Field/Field.js";
3
4
  import "./Combobox.css";
4
5
  import "../../fonts/material-symbols.css";
5
6
  const Combobox = React.forwardRef(({
@@ -223,97 +224,104 @@ const Combobox = React.forwardRef(({
223
224
  );
224
225
  };
225
226
  const hasSelection = selectedValues.length > 0;
226
- return /* @__PURE__ */ jsxs("div", { ...rest, className: classes, ref: setRootRef, children: [
227
- label && /* @__PURE__ */ jsxs("label", { className: `${baseClass}__label`, htmlFor: inputId, children: [
227
+ return /* @__PURE__ */ jsxs(
228
+ Field,
229
+ {
230
+ ...rest,
231
+ className: classes,
232
+ ref: setRootRef,
228
233
  label,
229
- required && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__required`, "aria-hidden": "true", children: [
230
- " ",
231
- "*"
232
- ] })
233
- ] }),
234
- /* @__PURE__ */ jsxs("div", { className: `${baseClass}__control`, onClick: () => inputRef.current?.focus(), children: [
235
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__search-icon material-symbols-rounded`, "aria-hidden": "true", children: "search" }),
236
- /* @__PURE__ */ jsxs("div", { className: `${baseClass}__field`, children: [
237
- multiple && selectedOptions.map((option) => /* @__PURE__ */ jsxs("span", { className: `${baseClass}__chip`, children: [
238
- option.label,
239
- /* @__PURE__ */ jsx(
234
+ helperText,
235
+ error,
236
+ required,
237
+ disabled,
238
+ size,
239
+ id: inputId,
240
+ children: [
241
+ /* @__PURE__ */ jsxs("div", { className: `${baseClass}__control`, onClick: () => inputRef.current?.focus(), children: [
242
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__search-icon material-symbols-rounded`, "aria-hidden": "true", children: "search" }),
243
+ /* @__PURE__ */ jsxs("div", { className: `${baseClass}__field`, children: [
244
+ multiple && selectedOptions.map((option) => /* @__PURE__ */ jsxs("span", { className: `${baseClass}__chip`, children: [
245
+ option.label,
246
+ /* @__PURE__ */ jsx(
247
+ "button",
248
+ {
249
+ type: "button",
250
+ className: `${baseClass}__chip-remove`,
251
+ onClick: (e) => {
252
+ e.stopPropagation();
253
+ handleRemove(option.value);
254
+ },
255
+ disabled,
256
+ "aria-label": `Remove ${option.label}`,
257
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
258
+ }
259
+ )
260
+ ] }, option.value)),
261
+ /* @__PURE__ */ jsx(
262
+ "input",
263
+ {
264
+ ref: inputRef,
265
+ id: inputId,
266
+ name,
267
+ type: "text",
268
+ className: `${baseClass}__input`,
269
+ role: "combobox",
270
+ autoComplete: "off",
271
+ "aria-expanded": isOpen,
272
+ "aria-controls": listboxId,
273
+ "aria-autocomplete": "list",
274
+ "aria-activedescendant": activeOptionId,
275
+ "aria-label": ariaLabel || rest["aria-label"] || (!label ? placeholder : void 0),
276
+ "aria-describedby": helperText ? `${inputId}-helper` : void 0,
277
+ "aria-invalid": error || void 0,
278
+ "aria-required": required || void 0,
279
+ placeholder: multiple && hasSelection ? "" : placeholder,
280
+ value: inputValue,
281
+ disabled,
282
+ onChange: (e) => handleQueryChange(e.target.value),
283
+ onFocus: openMenu,
284
+ onKeyDown: handleKeyDown
285
+ }
286
+ )
287
+ ] }),
288
+ clearable && hasSelection && !disabled && /* @__PURE__ */ jsx(
240
289
  "button",
241
290
  {
242
291
  type: "button",
243
- className: `${baseClass}__chip-remove`,
292
+ className: `${baseClass}__clear`,
244
293
  onClick: (e) => {
245
294
  e.stopPropagation();
246
- handleRemove(option.value);
295
+ handleClear();
247
296
  },
248
- disabled,
249
- "aria-label": `Remove ${option.label}`,
297
+ "aria-label": "Clear selection",
250
298
  children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
251
299
  }
252
- )
253
- ] }, option.value)),
254
- /* @__PURE__ */ jsx(
255
- "input",
256
- {
257
- ref: inputRef,
258
- id: inputId,
259
- name,
260
- type: "text",
261
- className: `${baseClass}__input`,
262
- role: "combobox",
263
- autoComplete: "off",
264
- "aria-expanded": isOpen,
265
- "aria-controls": listboxId,
266
- "aria-autocomplete": "list",
267
- "aria-activedescendant": activeOptionId,
268
- "aria-label": ariaLabel || rest["aria-label"] || (!label ? placeholder : void 0),
269
- "aria-describedby": helperText ? `${inputId}-helper` : void 0,
270
- "aria-invalid": error || void 0,
271
- "aria-required": required || void 0,
272
- placeholder: multiple && hasSelection ? "" : placeholder,
273
- value: inputValue,
274
- disabled,
275
- onChange: (e) => handleQueryChange(e.target.value),
276
- onFocus: openMenu,
277
- onKeyDown: handleKeyDown
278
- }
279
- )
280
- ] }),
281
- clearable && hasSelection && !disabled && /* @__PURE__ */ jsx(
282
- "button",
283
- {
284
- type: "button",
285
- className: `${baseClass}__clear`,
286
- onClick: (e) => {
287
- e.stopPropagation();
288
- handleClear();
289
- },
290
- "aria-label": "Clear selection",
291
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
292
- }
293
- ),
294
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__chevron material-symbols-rounded`, "aria-hidden": "true", children: "expand_more" })
295
- ] }),
296
- isOpen && /* @__PURE__ */ jsxs("ul", { className: `${baseClass}__menu`, role: "listbox", id: listboxId, ref: listRef, children: [
297
- loading && /* @__PURE__ */ jsx("li", { className: `${baseClass}__status`, role: "presentation", children: "Loading…" }),
298
- !loading && filteredOptions.length === 0 && /* @__PURE__ */ jsx("li", { className: `${baseClass}__status`, role: "presentation", children: emptyMessage }),
299
- !loading && (isGrouped ? (() => {
300
- let flatIndex = 0;
301
- return filteredGroups.map((group) => /* @__PURE__ */ jsxs("li", { className: `${baseClass}__group`, role: "none", children: [
302
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__group-label`, role: "presentation", children: group.label }),
303
- /* @__PURE__ */ jsx(
304
- "ul",
305
- {
306
- className: `${baseClass}__group-list`,
307
- role: "group",
308
- "aria-label": group.label,
309
- children: group.options.map((option) => renderOption(option, flatIndex++))
310
- }
311
- )
312
- ] }, group.label));
313
- })() : filteredOptions.map((option, index) => renderOption(option, index)))
314
- ] }),
315
- helperText && /* @__PURE__ */ jsx("p", { className: `${baseClass}__helper`, id: `${inputId}-helper`, children: helperText })
316
- ] });
300
+ ),
301
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__chevron material-symbols-rounded`, "aria-hidden": "true", children: "expand_more" })
302
+ ] }),
303
+ isOpen && /* @__PURE__ */ jsxs("ul", { className: `${baseClass}__menu`, role: "listbox", id: listboxId, ref: listRef, children: [
304
+ loading && /* @__PURE__ */ jsx("li", { className: `${baseClass}__status`, role: "presentation", children: "Loading…" }),
305
+ !loading && filteredOptions.length === 0 && /* @__PURE__ */ jsx("li", { className: `${baseClass}__status`, role: "presentation", children: emptyMessage }),
306
+ !loading && (isGrouped ? (() => {
307
+ let flatIndex = 0;
308
+ return filteredGroups.map((group) => /* @__PURE__ */ jsxs("li", { className: `${baseClass}__group`, role: "none", children: [
309
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__group-label`, role: "presentation", children: group.label }),
310
+ /* @__PURE__ */ jsx(
311
+ "ul",
312
+ {
313
+ className: `${baseClass}__group-list`,
314
+ role: "group",
315
+ "aria-label": group.label,
316
+ children: group.options.map((option) => renderOption(option, flatIndex++))
317
+ }
318
+ )
319
+ ] }, group.label));
320
+ })() : filteredOptions.map((option, index) => renderOption(option, index)))
321
+ ] })
322
+ ]
323
+ }
324
+ );
317
325
  });
318
326
  Combobox.displayName = "Combobox";
319
327
  export {
@@ -241,21 +241,7 @@
241
241
  flex-shrink: 0;
242
242
  }
243
243
 
244
- .ds-command-palette__key {
245
- display: inline-flex;
246
- align-items: center;
247
- justify-content: center;
248
- min-width: 20px;
249
- padding: var(--padding-xxxs) var(--padding-xxs);
250
- border: var(--border-xs) solid var(--color-bg-container-border);
251
- border-radius: var(--radius-xs);
252
- background-color: var(--color-bg-container-primary);
253
- font-family: var(--font-paragraph-sm-family);
254
- font-size: var(--font-paragraph-sm-size);
255
- font-weight: var(--font-paragraph-sm-em-weight);
256
- line-height: var(--font-paragraph-sm-line-height);
257
- color: var(--color-text-tertiary);
258
- }
244
+ /* Keycaps render through the Kbd component (compact size) */
259
245
 
260
246
  /* ============================================
261
247
  FOOTER HINTS
@@ -1,6 +1,7 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useState, useRef, useId, useSyncExternalStore, useCallback, useMemo, useEffect } from "react";
3
3
  import ReactDOM from "react-dom";
4
+ import { Kbd } from "../Kbd/Kbd.js";
4
5
  import "./CommandPalette.css";
5
6
  import "../../fonts/material-symbols.css";
6
7
  const emptySubscribe = () => () => {
@@ -225,7 +226,7 @@ const CommandPalette = ({
225
226
  /* @__PURE__ */ jsx("span", { className: `${baseClass}__command-label`, children: command.label }),
226
227
  command.description && /* @__PURE__ */ jsx("span", { className: `${baseClass}__command-description`, children: command.description })
227
228
  ] }),
228
- command.shortcut && command.shortcut.length > 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__shortcut`, children: command.shortcut.map((key, i) => /* @__PURE__ */ jsx("kbd", { className: `${baseClass}__key`, children: key }, i)) })
229
+ command.shortcut && command.shortcut.length > 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__shortcut`, children: command.shortcut.map((key, i) => /* @__PURE__ */ jsx(Kbd, { size: "compact", children: key }, i)) })
229
230
  ]
230
231
  },
231
232
  command.id
@@ -238,16 +239,16 @@ const CommandPalette = ({
238
239
  ] }),
239
240
  !hideFooter && /* @__PURE__ */ jsxs("div", { className: `${baseClass}__footer`, children: [
240
241
  /* @__PURE__ */ jsxs("span", { className: `${baseClass}__hint`, children: [
241
- /* @__PURE__ */ jsx("kbd", { className: `${baseClass}__key`, children: "↑" }),
242
- /* @__PURE__ */ jsx("kbd", { className: `${baseClass}__key`, children: "↓" }),
242
+ /* @__PURE__ */ jsx(Kbd, { size: "compact", children: "↑" }),
243
+ /* @__PURE__ */ jsx(Kbd, { size: "compact", children: "↓" }),
243
244
  "to navigate"
244
245
  ] }),
245
246
  /* @__PURE__ */ jsxs("span", { className: `${baseClass}__hint`, children: [
246
- /* @__PURE__ */ jsx("kbd", { className: `${baseClass}__key`, children: "↵" }),
247
+ /* @__PURE__ */ jsx(Kbd, { size: "compact", children: "↵" }),
247
248
  "to select"
248
249
  ] }),
249
250
  /* @__PURE__ */ jsxs("span", { className: `${baseClass}__hint`, children: [
250
- /* @__PURE__ */ jsx("kbd", { className: `${baseClass}__key`, children: "esc" }),
251
+ /* @__PURE__ */ jsx(Kbd, { size: "compact", children: "esc" }),
251
252
  "to close"
252
253
  ] })
253
254
  ] })
@@ -0,0 +1,258 @@
1
+ /* ============================================
2
+ CONTEXT MENU COMPONENT
3
+ Right-click menu anchored at the pointer.
4
+ Panel and item styling mirror DropdownMenu.
5
+ ============================================ */
6
+
7
+ /* Base — a plain block wrapper around the right-clickable area
8
+ (a real box, so the keyboard-invocation fallback can measure it) */
9
+ .ds-context-menu {
10
+ display: block;
11
+ }
12
+
13
+ /* ============================================
14
+ PANEL
15
+ Fixed-positioned at the pointer; left/top are
16
+ set inline from the contextmenu event coords.
17
+ ============================================ */
18
+
19
+ .ds-context-menu__panel {
20
+ position: fixed;
21
+ z-index: 50;
22
+ min-width: 200px;
23
+
24
+ /* Appearance — same recipe as DropdownMenu */
25
+ background: var(--color-bg-page-primary);
26
+ border: var(--border-xs) solid var(--color-input-border-primary);
27
+ border-radius: var(--radius-md);
28
+ box-shadow: var(--shadow-floating);
29
+
30
+ /* Inset gap */
31
+ padding: var(--padding-xxs);
32
+
33
+ /* Reset list */
34
+ list-style: none;
35
+ margin: 0;
36
+
37
+ outline: none;
38
+
39
+ /* Animation */
40
+ animation: ds-context-menu-appear var(--motion-duration-fast) var(--motion-ease-entrance);
41
+ }
42
+
43
+ /* Compact items: paragraph-sm-em, tighter padding, radius-sm */
44
+ .ds-context-menu__panel--compact .ds-context-menu__item {
45
+ padding: var(--padding-xs) var(--padding-sm-md);
46
+ border-radius: var(--radius-sm);
47
+ font-family: var(--font-paragraph-sm-em-family);
48
+ font-size: var(--font-paragraph-sm-em-size);
49
+ font-weight: var(--font-paragraph-sm-em-weight);
50
+ line-height: var(--font-paragraph-sm-em-line-height);
51
+ letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
52
+ }
53
+
54
+ .ds-context-menu__panel--compact .ds-context-menu__item-icon {
55
+ --icon-size: var(--icon-size-sm);
56
+ }
57
+
58
+ .ds-context-menu__panel--compact .ds-context-menu__submenu-icon {
59
+ --icon-size: var(--icon-size-sm);
60
+ }
61
+
62
+ .ds-context-menu__panel--compact .ds-context-menu__group-label {
63
+ min-height: auto;
64
+ padding: var(--padding-xs) var(--padding-sm-md);
65
+ font-size: var(--font-paragraph-sm-em-size);
66
+ line-height: var(--font-paragraph-sm-em-line-height);
67
+ letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
68
+ }
69
+
70
+ .ds-context-menu__panel--compact .ds-context-menu__separator {
71
+ margin: var(--padding-xxs) var(--padding-sm-md);
72
+ }
73
+
74
+ /* ============================================
75
+ SUB-MENU PANEL
76
+ Positioned relative to its parent item.
77
+ ============================================ */
78
+
79
+ .ds-context-menu__submenu {
80
+ position: absolute;
81
+ left: 100%;
82
+ top: 0;
83
+ margin-left: var(--gap-xxs);
84
+ }
85
+
86
+ /* ============================================
87
+ MENU ITEM
88
+ ============================================ */
89
+
90
+ .ds-context-menu__item {
91
+ display: flex;
92
+ align-items: center;
93
+ justify-content: space-between;
94
+ gap: var(--gap-md);
95
+ padding: var(--padding-sm) var(--padding-md);
96
+ border-radius: var(--radius-md);
97
+ cursor: pointer;
98
+ position: relative;
99
+ white-space: nowrap;
100
+ list-style: none;
101
+
102
+ font-family: var(--font-paragraph-em-family);
103
+ font-size: var(--font-paragraph-em-size);
104
+ font-weight: var(--font-paragraph-em-weight);
105
+ line-height: var(--font-paragraph-em-line-height);
106
+ letter-spacing: var(--font-paragraph-em-letter-spacing);
107
+ color: var(--color-action-passive-text);
108
+
109
+ transition: background-color var(--motion-duration-fast) var(--motion-ease-standard), color var(--motion-duration-fast) var(--motion-ease-standard);
110
+ }
111
+
112
+ .ds-context-menu__item:hover,
113
+ .ds-context-menu__item--focused {
114
+ background-color: var(--color-action-passive-bg-hover);
115
+ }
116
+
117
+ /* States — disabled */
118
+ .ds-context-menu__item--disabled {
119
+ opacity: 0.4;
120
+ cursor: not-allowed;
121
+ }
122
+
123
+ .ds-context-menu__item--disabled:hover,
124
+ .ds-context-menu__item--disabled.ds-context-menu__item--focused {
125
+ background-color: transparent;
126
+ }
127
+
128
+ /* ============================================
129
+ ITEM LABEL (text + optional icon)
130
+ ============================================ */
131
+
132
+ .ds-context-menu__item-label {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: var(--gap-sm);
136
+ line-height: 1;
137
+ }
138
+
139
+ .ds-context-menu__item-icon {
140
+ --icon-size: var(--icon-size-md);
141
+ color: var(--color-icon-secondary);
142
+ flex-shrink: 0;
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ line-height: 1;
147
+ }
148
+
149
+ /* ============================================
150
+ KEYBOARD SHORTCUT
151
+ ============================================ */
152
+
153
+ .ds-context-menu__shortcut {
154
+ font-family: var(--font-paragraph-sm-family);
155
+ font-size: var(--font-paragraph-sm-size);
156
+ font-weight: var(--font-paragraph-sm-weight);
157
+ line-height: var(--font-paragraph-sm-line-height);
158
+ letter-spacing: var(--font-paragraph-sm-letter-spacing);
159
+ color: var(--color-text-tertiary);
160
+ margin-left: auto;
161
+ flex-shrink: 0;
162
+ }
163
+
164
+ /* ============================================
165
+ SUB-MENU INDICATOR
166
+ ============================================ */
167
+
168
+ .ds-context-menu__submenu-icon {
169
+ --icon-size: var(--icon-size-md);
170
+ color: var(--color-text-tertiary);
171
+ margin-left: auto;
172
+ flex-shrink: 0;
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: center;
176
+ line-height: 1;
177
+ }
178
+
179
+ .ds-context-menu__item--has-submenu {
180
+ padding-right: var(--padding-sm-md);
181
+ }
182
+
183
+ /* ============================================
184
+ SEPARATOR
185
+ ============================================ */
186
+
187
+ .ds-context-menu__separator {
188
+ height: 1px;
189
+ background: var(--color-input-border-primary);
190
+ margin: var(--padding-xxs) var(--padding-md);
191
+ list-style: none;
192
+ }
193
+
194
+ /* ============================================
195
+ GROUP
196
+ ============================================ */
197
+
198
+ .ds-context-menu__group {
199
+ list-style: none;
200
+ }
201
+
202
+ .ds-context-menu__group-label {
203
+ display: flex;
204
+ align-items: center;
205
+ min-height: 40px;
206
+ font-family: var(--font-paragraph-em-family);
207
+ font-size: var(--font-paragraph-em-size);
208
+ font-weight: var(--font-paragraph-em-weight);
209
+ line-height: var(--font-paragraph-em-line-height);
210
+ letter-spacing: var(--font-paragraph-em-letter-spacing);
211
+ color: var(--color-text-tertiary);
212
+ padding-left: var(--padding-md);
213
+ padding-right: var(--padding-md);
214
+ user-select: none;
215
+ }
216
+
217
+ .ds-context-menu__group-list {
218
+ list-style: none;
219
+ margin: 0;
220
+ padding: 0;
221
+ }
222
+
223
+ /* ============================================
224
+ DESTRUCTIVE ITEM
225
+ ============================================ */
226
+
227
+ .ds-context-menu__item--destructive {
228
+ color: var(--color-core-accent-coral);
229
+ }
230
+
231
+ .ds-context-menu__item--destructive .ds-context-menu__item-icon {
232
+ color: var(--color-core-accent-coral);
233
+ }
234
+
235
+ .ds-context-menu__item--destructive .ds-context-menu__shortcut {
236
+ color: var(--color-core-accent-coral);
237
+ opacity: 0.7;
238
+ }
239
+
240
+ .ds-context-menu__item--destructive:hover,
241
+ .ds-context-menu__item--destructive.ds-context-menu__item--focused {
242
+ background-color: var(--color-status-error-bg);
243
+ }
244
+
245
+ /* ============================================
246
+ APPEAR ANIMATION
247
+ ============================================ */
248
+
249
+ @keyframes ds-context-menu-appear {
250
+ from {
251
+ opacity: 0;
252
+ transform: scale(0.96);
253
+ }
254
+ to {
255
+ opacity: 1;
256
+ transform: scale(1);
257
+ }
258
+ }
@@ -0,0 +1,26 @@
1
+ import { default as React } from 'react';
2
+ import { DropdownMenuEntry } from '../DropdownMenu/DropdownMenu';
3
+ /** Props owned by ContextMenu itself — everything else falls through to the wrapper div. */
4
+ type ContextMenuOwnProps = {
5
+ /** Menu entries — the same model as DropdownMenu (items, groups, separators) */
6
+ items: DropdownMenuEntry[];
7
+ /** Component size */
8
+ size?: 'default' | 'compact';
9
+ /** Accessible name for the menu */
10
+ ariaLabel?: string;
11
+ /** Additional CSS classes */
12
+ className?: string;
13
+ /** The right-clickable area the menu is attached to */
14
+ children: React.ReactNode;
15
+ };
16
+ export interface ContextMenuProps extends ContextMenuOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof ContextMenuOwnProps> {
17
+ }
18
+ /**
19
+ * ContextMenu opens a floating menu at the pointer position when the wrapped
20
+ * area is right-clicked (or the keyboard ContextMenu / Shift+F10 key is used).
21
+ * Entries use the same model as DropdownMenu — items, groups, separators,
22
+ * icons, shortcut hints, destructive and disabled states, and one level of
23
+ * sub-menus.
24
+ */
25
+ export declare const ContextMenu: React.ForwardRefExoticComponent<ContextMenuProps & React.RefAttributes<HTMLDivElement>>;
26
+ export {};