@drivenets/design-system 0.9.0 → 0.10.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 (95) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.cjs +1076 -600
  3. package/dist/index.d.cts +1988 -528
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +1991 -531
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1083 -609
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +16 -17
  11. package/src/components/ds-breadcrumb/ds-breadcrumb.types.ts +40 -6
  12. package/src/components/ds-button/ds-button.types.ts +13 -2
  13. package/src/components/ds-button-v3/ds-button-v3.module.scss +108 -109
  14. package/src/components/ds-button-v3/ds-button-v3.tsx +2 -2
  15. package/src/components/ds-button-v3/ds-button-v3.types.ts +19 -3
  16. package/src/components/ds-button-v3/index.ts +5 -1
  17. package/src/components/ds-card/ds-card.types.ts +3 -1
  18. package/src/components/ds-checkbox/ds-checkbox.module.scss +106 -123
  19. package/src/components/ds-checkbox/ds-checkbox.tsx +24 -35
  20. package/src/components/ds-checkbox/ds-checkbox.types.ts +12 -11
  21. package/src/components/ds-checkbox-group/ds-checkbox-group.module.scss +13 -0
  22. package/src/components/ds-checkbox-group/ds-checkbox-group.tsx +20 -0
  23. package/src/components/ds-checkbox-group/ds-checkbox-group.types.ts +8 -0
  24. package/src/components/ds-checkbox-group/hooks/index.ts +6 -0
  25. package/src/components/ds-checkbox-group/hooks/use-checkbox-select-all.ts +65 -0
  26. package/src/components/ds-checkbox-group/index.ts +3 -0
  27. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.types.ts +26 -0
  28. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.types.ts +38 -0
  29. package/src/components/ds-comment-bubble/ds-comment-bubble.types.ts +78 -0
  30. package/src/components/ds-comment-card/ds-comment-card.module.scss +2 -2
  31. package/src/components/ds-comment-card/ds-comment-card.types.ts +114 -0
  32. package/src/components/ds-comment-indicator/ds-comment-indicator.types.ts +26 -0
  33. package/src/components/ds-comments-drawer/comments-filters.types.ts +33 -0
  34. package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.types.ts +37 -1
  35. package/src/components/ds-comments-drawer/ds-comments-drawer.types.ts +64 -0
  36. package/src/components/ds-date-input/ds-date-input.types.ts +23 -0
  37. package/src/components/ds-date-picker/ds-date-picker.tsx +3 -0
  38. package/src/components/ds-date-picker/ds-date-picker.types.ts +20 -0
  39. package/src/components/ds-date-range-picker/ds-date-range-picker.types.ts +28 -0
  40. package/src/components/ds-dialog/ds-dialog.types.ts +10 -1
  41. package/src/components/ds-dropdown-menu/ds-dropdown-menu.types.ts +24 -0
  42. package/src/components/ds-file-upload/components/file-upload/file-upload.types.ts +1 -0
  43. package/src/components/ds-file-upload/ds-file-upload-api.types.ts +55 -0
  44. package/src/components/ds-form-control/ds-form-control.types.ts +3 -0
  45. package/src/components/ds-grid/ds-grid.tsx +29 -12
  46. package/src/components/ds-grid/ds-grid.types.ts +16 -13
  47. package/src/components/ds-grid/index.ts +10 -2
  48. package/src/components/ds-icon/custom-icons/index.ts +2 -0
  49. package/src/components/ds-icon/custom-icons/special-leading-icon.tsx +11 -0
  50. package/src/components/ds-icon/ds-icon.tsx +10 -4
  51. package/src/components/ds-icon/ds-icon.types.ts +30 -0
  52. package/src/components/ds-key-value-pair/ds-key-value-pair.types.ts +12 -2
  53. package/src/components/ds-modal/ds-modal.types.ts +16 -0
  54. package/src/components/ds-panel/ds-panel.types.ts +39 -0
  55. package/src/components/ds-radio-group/ds-radio-group.module.scss +58 -56
  56. package/src/components/ds-radio-group/ds-radio-group.tsx +35 -28
  57. package/src/components/ds-radio-group/ds-radio-group.types.ts +38 -7
  58. package/src/components/ds-segment-group/ds-segment-group.types.ts +17 -0
  59. package/src/components/ds-select/ds-select.types.ts +32 -0
  60. package/src/components/ds-split-button/ds-split-button.module.scss +7 -7
  61. package/src/components/ds-split-button/ds-split-button.types.ts +28 -0
  62. package/src/components/ds-stack/ds-stack.types.ts +23 -0
  63. package/src/components/ds-stepper/ds-stepper.types.ts +34 -1
  64. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +27 -0
  65. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.tsx +7 -4
  66. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.types.ts +6 -0
  67. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.types.ts +10 -0
  68. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.types.ts +11 -0
  69. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.types.ts +27 -0
  70. package/src/components/ds-table/filters/types/filter-adapter.types.ts +6 -0
  71. package/src/components/ds-tag/ds-tag.types.ts +6 -0
  72. package/src/components/ds-tag-filter/ds-tag-filter.types.ts +16 -0
  73. package/src/components/ds-text-input/ds-text-input.types.ts +3 -0
  74. package/src/components/ds-time-picker/components/time-item/time-item.types.ts +19 -0
  75. package/src/components/ds-time-picker/components/time-scroller/time-scroller.types.ts +29 -0
  76. package/src/components/ds-time-picker/ds-time-picker.module.scss +7 -0
  77. package/src/components/ds-time-picker/ds-time-picker.tsx +8 -1
  78. package/src/components/ds-time-picker/ds-time-picker.types.ts +80 -0
  79. package/src/components/ds-tooltip/ds-tooltip.types.ts +6 -0
  80. package/src/components/ds-tree/ds-tree.types.ts +86 -4
  81. package/src/components/ds-typography/ds-typography.config.ts +46 -27
  82. package/src/components/ds-typography/ds-typography.tsx +22 -7
  83. package/src/components/ds-typography/ds-typography.types.ts +15 -4
  84. package/src/components/ds-workspace/ds-workspace.module.scss +55 -0
  85. package/src/components/ds-workspace/ds-workspace.tsx +37 -0
  86. package/src/components/ds-workspace/ds-workspace.types.ts +17 -0
  87. package/src/components/ds-workspace/index.ts +8 -0
  88. package/src/index.ts +2 -0
  89. package/src/styles/_grid.scss +7 -3
  90. package/src/styles/_mixin.scss +2 -2
  91. package/src/styles/_root_new.scss +449 -0
  92. package/src/styles/_scrollbars.scss +6 -1
  93. package/src/styles/shared/_input.scss +0 -2
  94. package/src/styles/styles.scss +1 -0
  95. package/src/styles/_root_updated.scss +0 -593
@@ -94,6 +94,7 @@ export interface FileUploadProps {
94
94
  disabled?: boolean;
95
95
  /**
96
96
  * Whether the file upload layout is compact (inline)
97
+ * @default false
97
98
  */
98
99
  compact?: boolean;
99
100
  }
@@ -5,23 +5,65 @@ export type UploadFileStatus = 'pending' | 'uploading' | 'interrupted' | 'comple
5
5
  export type FileMetadata = Record<string, unknown>;
6
6
 
7
7
  export interface UploadedFile extends File {
8
+ /**
9
+ * Stable identifier assigned by the uploader. Used to correlate progress
10
+ * updates, retries, and deletions with this specific file.
11
+ */
8
12
  id: string;
13
+ /**
14
+ * Current upload progress as a number between `0` and `1`.
15
+ */
9
16
  progress: number;
17
+ /**
18
+ * Current lifecycle state of the upload.
19
+ */
10
20
  status: UploadFileStatus;
21
+ /**
22
+ * Validation or upload errors associated with this file. Present when
23
+ * `status` is `error` or when the file fails client-side validation.
24
+ */
11
25
  errors?: FileError[];
26
+ /**
27
+ * Native `File` the user selected. Kept alongside so consumers can access
28
+ * the original binary even after `UploadedFile` has been enriched.
29
+ */
12
30
  originalFile: File;
13
31
  }
14
32
 
15
33
  export interface FileUploadOptions {
34
+ /**
35
+ * The file to upload.
36
+ */
16
37
  file: File;
38
+ /**
39
+ * Stable identifier for this upload. Must match the `id` on the corresponding
40
+ * `UploadedFile` so progress and cancellation are routed correctly.
41
+ */
17
42
  fileId: string;
43
+ /**
44
+ * Optional custom metadata forwarded by the consumer (e.g., tags, parent ids).
45
+ */
18
46
  metadata?: FileMetadata;
47
+ /**
48
+ * Called by the adapter with upload progress updates as a number between `0`
49
+ * and `1`.
50
+ */
19
51
  onProgress?: (progress: number) => void;
52
+ /**
53
+ * AbortSignal that fires when the consumer cancels the upload. Adapters should
54
+ * observe this and abort any in-flight network request.
55
+ */
20
56
  signal?: AbortSignal;
21
57
  }
22
58
 
23
59
  export interface FileUploadResult {
60
+ /**
61
+ * URL of the successfully uploaded file, returned by the backend.
62
+ */
24
63
  url: string;
64
+ /**
65
+ * Optional metadata returned by the backend alongside the URL.
66
+ */
25
67
  metadata?: FileMetadata;
26
68
  }
27
69
 
@@ -33,7 +75,20 @@ export interface FileUploadResult {
33
75
  * - reject with FatalFileUploadError for permanent failures
34
76
  */
35
77
  export interface FileUploadAdapter {
78
+ /**
79
+ * Uploads a single file. Resolve with `FileUploadResult` on success; reject
80
+ * with `RetryableFileUploadError` for transient failures (will be retried) or
81
+ * `FatalFileUploadError` for permanent failures (surfaces to the user).
82
+ */
36
83
  upload: (options: FileUploadOptions) => FileUploadResult | Promise<FileUploadResult>;
84
+ /**
85
+ * Optional hook called when the user cancels an in-flight upload. Implementations
86
+ * should abort any pending network work for the given `fileId`.
87
+ */
37
88
  cancel?: (fileId: string) => void | Promise<void>;
89
+ /**
90
+ * Optional hook called when the user removes a previously uploaded file. Typically
91
+ * issues a delete request to the backend so stored files can be cleaned up.
92
+ */
38
93
  delete?: (fileId: string) => void | Promise<void>;
39
94
  }
@@ -22,6 +22,9 @@ export interface DsFormControlProps extends React.PropsWithChildren {
22
22
  */
23
23
  required?: boolean;
24
24
 
25
+ /**
26
+ * Optional render slots for customizing parts of the control.
27
+ */
25
28
  slots?: {
26
29
  /**
27
30
  * Adornment to display at the end of the label
@@ -1,11 +1,17 @@
1
- import type React from 'react';
1
+ import type { CSSProperties } from 'react';
2
2
  import classNames from 'classnames';
3
- import type { DsGridItemProps, DsGridProps } from './ds-grid.types';
4
3
 
5
- /**
6
- * Design system Grid component
7
- */
8
- export const DsGrid: React.FC<DsGridProps> = ({ children, columns, rows, className }) => {
4
+ import type { DsGridBaseProps, DsGridItemBaseProps } from './ds-grid.types';
5
+
6
+ export const DsGridBase = ({
7
+ children,
8
+ columns,
9
+ rows,
10
+ gutter,
11
+ margin,
12
+ className,
13
+ style,
14
+ }: DsGridBaseProps) => {
9
15
  const gridClass = classNames(
10
16
  'ds-grid',
11
17
  {
@@ -15,20 +21,31 @@ export const DsGrid: React.FC<DsGridProps> = ({ children, columns, rows, classNa
15
21
  className,
16
22
  );
17
23
 
18
- return <div className={gridClass}>{children}</div>;
24
+ const cssVars: Record<string, string> = {};
25
+
26
+ if (gutter !== undefined) {
27
+ cssVars['--ds-grid-gutter'] = typeof gutter === 'number' ? `${String(gutter)}px` : gutter;
28
+ }
29
+
30
+ if (margin !== undefined) {
31
+ cssVars['--ds-grid-margin'] = typeof margin === 'number' ? `${String(margin)}px` : margin;
32
+ }
33
+
34
+ return (
35
+ <div className={gridClass} style={{ ...style, ...cssVars } as CSSProperties}>
36
+ {children}
37
+ </div>
38
+ );
19
39
  };
20
40
 
21
- /**
22
- * Design system GridItem component
23
- */
24
- export const DsGridItem: React.FC<DsGridItemProps> = ({
41
+ export const DsGridItemBase = ({
25
42
  children,
26
43
  colSpan,
27
44
  colStart,
28
45
  rowSpan,
29
46
  rowStart,
30
47
  className,
31
- }) => {
48
+ }: DsGridItemBaseProps) => {
32
49
  const gridItemClass = classNames(
33
50
  {
34
51
  [`ds-grid-col-span-${String(colSpan)}`]: colSpan,
@@ -1,9 +1,6 @@
1
- import type { ReactNode } from 'react';
1
+ import type { CSSProperties, ReactNode } from 'react';
2
2
 
3
- export interface DsGridProps {
4
- /**
5
- * The content to be rendered inside the grid
6
- */
3
+ export interface DsGridBaseProps {
7
4
  children: ReactNode;
8
5
 
9
6
  /**
@@ -17,15 +14,24 @@ export interface DsGridProps {
17
14
  columns?: 2 | 4 | 6 | 8 | 10 | 12;
18
15
 
19
16
  /**
20
- * Custom class names to apply to the grid
17
+ * Gap between grid cells.
18
+ * A number is treated as px. A string is used as-is (e.g. `"1rem"`, `"var(--grid-gutter)"`).
19
+ * CSS default (when omitted): `16px` — set by `$gutter` in `_grid-variables.scss`.
21
20
  */
22
- className?: string;
23
- }
21
+ gutter?: number | string;
24
22
 
25
- export interface DsGridItemProps {
26
23
  /**
27
- * The content to be rendered inside the grid item
24
+ * Padding around the grid container.
25
+ * A number is treated as px (uniform). A string is used as-is (e.g. `"16px 20px"`, `"var(--grid-margin)"`).
26
+ * CSS default (when omitted): `16px 20px` — set by `$margin-y $margin-x` in `_grid-variables.scss`.
28
27
  */
28
+ margin?: number | string;
29
+
30
+ className?: string;
31
+ style?: CSSProperties;
32
+ }
33
+
34
+ export interface DsGridItemBaseProps {
29
35
  children: ReactNode;
30
36
 
31
37
  /**
@@ -48,8 +54,5 @@ export interface DsGridItemProps {
48
54
  */
49
55
  rowStart?: number;
50
56
 
51
- /**
52
- * Custom class names to apply to the grid item
53
- */
54
57
  className?: string;
55
58
  }
@@ -1,2 +1,10 @@
1
- export * from './ds-grid';
2
- export * from './ds-grid.types';
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { withResponsiveProps } from '../../utils/responsive';
4
+ import { DsGridBase, DsGridItemBase } from './ds-grid';
5
+
6
+ export const DsGrid = withResponsiveProps(DsGridBase, ['gutter', 'margin']);
7
+ export const DsGridItem = withResponsiveProps(DsGridItemBase, ['colSpan', 'rowSpan']);
8
+
9
+ export type DsGridProps = ComponentProps<typeof DsGrid>;
10
+ export type DsGridItemProps = ComponentProps<typeof DsGridItem>;
@@ -50,6 +50,7 @@ import { SpecialMapView } from './special-map-view';
50
50
 
51
51
  // Misc icons
52
52
  import { SpecialHome } from './special-home';
53
+ import { SpecialLeadingIcon } from './special-leading-icon';
53
54
  import { SpecialLego } from './special-lego';
54
55
  import { SpecialScheme } from './special-scheme';
55
56
  import { SpecialBook } from './special-book';
@@ -120,6 +121,7 @@ export const customIcons = {
120
121
 
121
122
  // Misc
122
123
  'special-home': SpecialHome,
124
+ 'special-leading-icon': SpecialLeadingIcon,
123
125
  'special-lego': SpecialLego,
124
126
  'special-scheme': SpecialScheme,
125
127
  'special-book': SpecialBook,
@@ -0,0 +1,11 @@
1
+ import type { FC, SVGProps } from 'react';
2
+
3
+ /** Leading / input-adjacent building icon from design (AR-58177). */
4
+ export const SpecialLeadingIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
5
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 13" fill="none" {...props}>
6
+ <path
7
+ d="M7.2 4.81737H7.8C7.95913 4.81737 8.11174 4.75164 8.22426 4.63464C8.33679 4.51765 8.4 4.35896 8.4 4.1935C8.4 4.02804 8.33679 3.86936 8.22426 3.75237C8.11174 3.63537 7.95913 3.56964 7.8 3.56964H7.2C7.04087 3.56964 6.88826 3.63537 6.77574 3.75237C6.66321 3.86936 6.6 4.02804 6.6 4.1935C6.6 4.35896 6.66321 4.51765 6.77574 4.63464C6.88826 4.75164 7.04087 4.81737 7.2 4.81737ZM7.2 7.31283H7.8C7.95913 7.31283 8.11174 7.24711 8.22426 7.13011C8.33679 7.01311 8.4 6.85443 8.4 6.68897C8.4 6.52351 8.33679 6.36483 8.22426 6.24783C8.11174 6.13083 7.95913 6.0651 7.8 6.0651H7.2C7.04087 6.0651 6.88826 6.13083 6.77574 6.24783C6.66321 6.36483 6.6 6.52351 6.6 6.68897C6.6 6.85443 6.66321 7.01311 6.77574 7.13011C6.88826 7.24711 7.04087 7.31283 7.2 7.31283ZM4.2 4.81737H4.8C4.95913 4.81737 5.11174 4.75164 5.22426 4.63464C5.33679 4.51765 5.4 4.35896 5.4 4.1935C5.4 4.02804 5.33679 3.86936 5.22426 3.75237C5.11174 3.63537 4.95913 3.56964 4.8 3.56964H4.2C4.04087 3.56964 3.88826 3.63537 3.77574 3.75237C3.66321 3.86936 3.6 4.02804 3.6 4.1935C3.6 4.35896 3.66321 4.51765 3.77574 4.63464C3.88826 4.75164 4.04087 4.81737 4.2 4.81737ZM4.2 7.31283H4.8C4.95913 7.31283 5.11174 7.24711 5.22426 7.13011C5.33679 7.01311 5.4 6.85443 5.4 6.68897C5.4 6.52351 5.33679 6.36483 5.22426 6.24783C5.11174 6.13083 4.95913 6.0651 4.8 6.0651H4.2C4.04087 6.0651 3.88826 6.13083 3.77574 6.24783C3.66321 6.36483 3.6 6.52351 3.6 6.68897C3.6 6.85443 3.66321 7.01311 3.77574 7.13011C3.88826 7.24711 4.04087 7.31283 4.2 7.31283ZM11.4 11.264H10.8V1.13558C10.8 0.97012 10.7368 0.811436 10.6243 0.694439C10.5117 0.577441 10.3591 0.511713 10.2 0.511713H1.8C1.64087 0.511713 1.48826 0.577441 1.37574 0.694439C1.26321 0.811436 1.2 0.97012 1.2 1.13558V11.264H0.6C0.44087 11.264 0.288258 11.3297 0.175736 11.4467C0.0632141 11.5637 0 11.7224 0 11.8879C0 12.0533 0.0632141 12.212 0.175736 12.329C0.288258 12.446 0.44087 12.5117 0.6 12.5117H11.4C11.5591 12.5117 11.7117 12.446 11.8243 12.329C11.9368 12.212 12 12.0533 12 11.8879C12 11.7224 11.9368 11.5637 11.8243 11.4467C11.7117 11.3297 11.5591 11.264 11.4 11.264ZM6.6 12.5117H5.4V9.91228H6.6V12.5117ZM9.6 11.264H7.8V9.28841C7.8 9.12295 7.73679 8.96426 7.62426 8.84727C7.51174 8.73027 7.35913 8.66454 7.2 8.66454H4.8C4.64087 8.66454 4.48826 8.73027 4.37574 8.84727C4.26321 8.96426 4.2 9.12295 4.2 9.28841V11.264H2.4V1.75945H9.6V11.264Z"
8
+ fill="currentColor"
9
+ />
10
+ </svg>
11
+ );
@@ -1,8 +1,12 @@
1
1
  import classNames from 'classnames';
2
2
  import styles from './ds-icon.module.scss';
3
- import type { DsIconProps } from './ds-icon.types';
3
+ import { iconColors, type DsIconProps } from './ds-icon.types';
4
4
  import { customIcons, isCustomIcon } from './custom-icons';
5
5
 
6
+ const iconColorSet = new Set<string>(iconColors);
7
+
8
+ const resolveColor = (color: string): string => (iconColorSet.has(color) ? `var(--icon-${color})` : color);
9
+
6
10
  /**
7
11
  * Design system Icon component that renders Google Material Icons, custom SVG icons, or inline SVGs
8
12
  */
@@ -11,29 +15,31 @@ const DsIcon = ({
11
15
  size = 'medium',
12
16
  variant = 'outlined',
13
17
  filled,
18
+ color,
14
19
  className = '',
15
20
  style = {},
16
21
  ...rest
17
22
  }: DsIconProps) => {
18
23
  const iconClass = classNames(styles.icon, styles[size], { [styles.filled]: filled }, className);
24
+ const mergedStyle = color ? { color: resolveColor(color), ...style } : style;
19
25
 
20
26
  // 1. SVG component passed directly
21
27
  if (typeof icon === 'function') {
22
28
  const SvgComponent = icon;
23
- return <SvgComponent className={iconClass} style={style} {...rest} />;
29
+ return <SvgComponent className={iconClass} style={mergedStyle} {...rest} />;
24
30
  }
25
31
 
26
32
  // 2. Custom registered icon from custom-icons registry
27
33
  if (isCustomIcon(icon)) {
28
34
  const SvgComponent = customIcons[icon];
29
- return <SvgComponent className={iconClass} style={style} {...rest} />;
35
+ return <SvgComponent className={iconClass} style={mergedStyle} {...rest} />;
30
36
  }
31
37
 
32
38
  // 3. Material Icon (font-based)
33
39
  const variantClass = `material-symbols-${variant}`;
34
40
 
35
41
  return (
36
- <span className={classNames(iconClass, variantClass)} style={style} {...rest}>
42
+ <span className={classNames(iconClass, variantClass)} style={mergedStyle} {...rest}>
37
43
  {icon}
38
44
  </span>
39
45
  );
@@ -7,6 +7,29 @@ export type IconSize = (typeof iconSizes)[number];
7
7
  export const iconVariants = ['outlined', 'rounded'] as const;
8
8
  export type IconVariant = (typeof iconVariants)[number];
9
9
 
10
+ export const iconColors = [
11
+ 'action',
12
+ 'action-hover',
13
+ 'action-secondary',
14
+ 'disabled',
15
+ 'error',
16
+ 'execution',
17
+ 'information-main',
18
+ 'information-secondary',
19
+ 'inverse',
20
+ 'main',
21
+ 'on-button',
22
+ 'on-dark-disabled',
23
+ 'pause',
24
+ 'pending',
25
+ 'secondary',
26
+ 'success',
27
+ 'success-light',
28
+ 'warning',
29
+ 'warning-light',
30
+ ] as const;
31
+ export type IconColor = (typeof iconColors)[number];
32
+
10
33
  export type MaterialIconName = {
11
34
  [K in keyof typeof materialIcons]: K extends `${IconPrefix}::${infer Name}` ? Name : never;
12
35
  }[keyof typeof materialIcons];
@@ -39,6 +62,13 @@ export interface DsIconProps {
39
62
  */
40
63
  filled?: boolean;
41
64
 
65
+ /**
66
+ * Icon color. Semantic names map to `--icon-*` tokens (e.g. `'error'` →
67
+ * `var(--icon-error)`); raw CSS values (hex, rgb, hsl, CSS variables)
68
+ * pass through unchanged. Omit to inherit from parent.
69
+ */
70
+ color?: IconColor | (string & {});
71
+
42
72
  /**
43
73
  * Additional CSS class names
44
74
  */
@@ -4,15 +4,25 @@ export const dsKeyValuePairOrientations = ['vertical', 'horizontal'] as const;
4
4
  export type DsKeyValuePairOrientation = (typeof dsKeyValuePairOrientations)[number];
5
5
 
6
6
  export interface DsKeyValuePairProps {
7
+ /** Label rendered for the key side of the pair (left or top depending on `orientation`). */
7
8
  keyLabel: ReactNode;
8
9
 
9
10
  /** Shown when the editor is not active (read-only or not hovered). */
10
11
  value?: ReactNode;
11
12
 
12
- /** @default false */
13
+ /**
14
+ * When `true`, the pair is rendered as a static display and the editor is never
15
+ * revealed (even on hover/focus).
16
+ * @default false
17
+ */
13
18
  readOnly?: boolean;
14
19
 
15
- /** @default 'vertical' */
20
+ /**
21
+ * Layout orientation:
22
+ * - `vertical`: key stacked above value
23
+ * - `horizontal`: key and value side-by-side
24
+ * @default 'vertical'
25
+ */
16
26
  orientation?: DsKeyValuePairOrientation;
17
27
 
18
28
  /** Editor content revealed on hover/focus when not read-only. */
@@ -13,6 +13,22 @@ export interface DsModalProps extends Pick<
13
13
  DialogProps,
14
14
  'modal' | 'closeOnEscape' | 'closeOnInteractOutside'
15
15
  > {
16
+ /**
17
+ * Whether the modal traps focus and blocks interaction with the page behind the
18
+ * backdrop. When `false`, users can continue interacting with the underlying page.
19
+ * @default true
20
+ */
21
+ modal?: DialogProps['modal'];
22
+ /**
23
+ * Whether pressing the Escape key closes the modal.
24
+ * @default true
25
+ */
26
+ closeOnEscape?: DialogProps['closeOnEscape'];
27
+ /**
28
+ * Whether clicking or focusing outside the modal closes it.
29
+ * @default true
30
+ */
31
+ closeOnInteractOutside?: DialogProps['closeOnInteractOutside'];
16
32
  /**
17
33
  * Whether the modal is open
18
34
  */
@@ -5,21 +5,60 @@ import type { ResponsiveValue } from '../../utils/responsive';
5
5
  export type DsPanelVariant = 'docked' | 'floating';
6
6
 
7
7
  export type DsPanelBaseProps = Omit<Collapsible.CollapsibleProps, 'onOpenChange'> & {
8
+ /**
9
+ * Visual treatment of the panel:
10
+ * - `docked`: sits flush within the surrounding layout (default)
11
+ * - `floating`: elevated card with shadow, detached from the layout
12
+ * @default docked
13
+ */
8
14
  variant?: DsPanelVariant;
15
+ /**
16
+ * Width of the panel in pixels when expanded.
17
+ */
9
18
  width?: number;
19
+ /**
20
+ * Whether the panel edge can be dragged to resize its width.
21
+ * @default false
22
+ */
10
23
  draggable?: boolean;
24
+ /**
25
+ * Disable the default inner padding so the panel content can bleed to the edges.
26
+ * @default false
27
+ */
11
28
  disablePadding?: boolean;
29
+ /**
30
+ * Props forwarded to nested sub-components.
31
+ */
12
32
  slotProps?: {
33
+ /**
34
+ * Props forwarded to the collapse/expand button shown on the panel edge.
35
+ */
13
36
  collapseButton?: DsPanelCollapseButtonProps;
14
37
  };
38
+ /**
39
+ * Called when the panel's open (expanded) state changes. Receives the next
40
+ * boolean state.
41
+ */
15
42
  onOpenChange?: (open: boolean) => void;
16
43
  };
17
44
 
18
45
  export type DsPanelProps = Omit<DsPanelBaseProps, 'width'> & {
46
+ /**
47
+ * Width of the panel when expanded. Accepts a single number (px) or a
48
+ * `ResponsiveValue` to vary width by breakpoint.
49
+ */
19
50
  width?: ResponsiveValue<number>;
20
51
  };
21
52
 
22
53
  export type DsPanelCollapseButtonProps = {
54
+ /**
55
+ * Called when the collapse button is clicked.
56
+ */
23
57
  onClick?: () => void;
58
+ /**
59
+ * Whether the panel is currently collapsed. Drives the button's chevron
60
+ * orientation and aria-label.
61
+ * @default false
62
+ */
24
63
  collapsed?: boolean;
25
64
  };
@@ -1,90 +1,99 @@
1
1
  @use '../../styles/typography';
2
2
 
3
+ $radio-click-target: 20px;
4
+ $radio-circle-size: 16px;
5
+ $radio-dot-size: 8px;
6
+
3
7
  .radioGroupRoot {
4
8
  display: flex;
5
9
  flex-direction: column;
6
- gap: 12px;
10
+ gap: var(--standard);
7
11
  }
8
12
 
9
13
  .radioItemContainer {
10
- position: relative;
11
14
  display: flex;
15
+ flex-wrap: wrap;
12
16
  align-items: center;
13
- gap: 4px;
17
+ column-gap: var(--3xs);
14
18
  cursor: pointer;
15
- padding-right: 1px;
16
- border: 1px solid transparent;
17
- border-radius: 4px;
19
+ border-radius: var(--4xs);
20
+
21
+ &:has(.labelInfo) {
22
+ align-items: flex-start;
23
+
24
+ .radioLabel {
25
+ padding-bottom: var(--4xs);
26
+ padding-right: var(--4xs);
27
+ }
28
+ }
18
29
 
19
30
  &:has([data-disabled]) {
20
31
  cursor: not-allowed;
21
32
 
33
+ .radioLabel {
34
+ color: var(--font-disabled);
35
+ }
36
+
22
37
  .labelInfo {
23
- color: var(--color-secondary-500-net-4);
38
+ color: var(--font-disabled);
24
39
  }
25
40
  }
26
41
 
27
- &:has([data-state='checked']:focus-visible) {
28
- border-radius: 4px;
29
- border-color: var(--color-primary-700-net-1);
42
+ &:not(:has([data-disabled])):hover {
43
+ background-color: var(--background-secondary-hover);
30
44
  }
31
45
 
32
- &:has([data-state='unchecked']:focus-visible) {
33
- border-color: var(--color-primary-800);
34
- }
46
+ &:has([data-focus-visible]) {
47
+ position: relative;
48
+ background-color: var(--background-secondary-hover);
35
49
 
36
- &:has(.labelInfo) {
37
- margin-bottom: 4px;
50
+ &::after {
51
+ content: '';
52
+ position: absolute;
53
+ inset: calc(-1px - var(--4xs));
54
+ border: var(--4xs) solid var(--border-action-primary-hover);
55
+ border-radius: var(--3xs);
56
+ pointer-events: none;
57
+ }
38
58
  }
39
59
  }
40
60
 
41
61
  .radioItem {
42
62
  all: unset;
43
- position: relative;
44
63
  display: flex;
45
64
  align-items: center;
46
65
  justify-content: center;
47
- border: 2px solid transparent;
48
- border-radius: 50%;
49
- transition:
50
- background-color 0.2s ease-in-out,
51
- border-color 0.2s ease-in-out;
66
+ width: $radio-click-target;
67
+ height: $radio-click-target;
68
+ flex-shrink: 0;
52
69
 
53
70
  .radioItemWrapper {
54
71
  display: flex;
55
72
  align-items: center;
56
73
  justify-content: center;
57
- height: 16px;
58
- width: 16px;
59
- border: 2px solid var(--color-secondary-500-net-4);
60
- padding: 2px;
74
+ height: $radio-circle-size;
75
+ width: $radio-circle-size;
76
+ border: 1.5px solid var(--border-contrast);
61
77
  border-radius: 50%;
62
- background: none;
78
+ background: var(--background);
79
+ transition: border-color 0.2s;
63
80
  }
64
81
 
65
82
  &[data-state='checked'],
66
83
  [data-state='checked'] > & {
67
84
  .radioItemWrapper {
68
- border-color: var(--color-primary-700-default);
69
- }
70
-
71
- &:hover {
72
- border-color: var(--color-primary-100);
85
+ border-color: var(--border-action-primary);
73
86
  }
74
87
  }
75
88
 
76
89
  &[data-state='checked'][data-disabled],
77
90
  [data-state='checked'][data-disabled] > & {
78
- .radioIndicator {
79
- background: var(--color-secondary-300);
91
+ .radioItemWrapper {
92
+ border-color: var(--border-disabled);
80
93
  }
81
- }
82
94
 
83
- &[data-state='unchecked'],
84
- [data-state='unchecked'] > & {
85
- &:hover {
86
- border-color: var(--color-secondary-100);
87
- background-color: var(--color-secondary-100);
95
+ .radioIndicator {
96
+ background: var(--border-disabled);
88
97
  }
89
98
  }
90
99
 
@@ -92,20 +101,16 @@
92
101
  [data-disabled] > & {
93
102
  pointer-events: none;
94
103
 
95
- & + label {
96
- pointer-events: none;
97
- }
98
-
99
104
  .radioItemWrapper {
100
- border-color: var(--color-secondary-300);
105
+ border-color: var(--border-disabled);
101
106
  }
102
107
  }
103
108
  }
104
109
 
105
110
  .radioIndicator {
106
- width: 8px;
107
- height: 8px;
108
- background: var(--color-primary-700-default);
111
+ width: $radio-dot-size;
112
+ height: $radio-dot-size;
113
+ background: var(--background-primary);
109
114
  border-radius: 50%;
110
115
 
111
116
  &[data-part='indicator'] {
@@ -123,16 +128,13 @@
123
128
  }
124
129
 
125
130
  .radioLabel {
126
- @include typography.body-sm-reg;
127
- color: var(--color-secondary-800);
128
- cursor: pointer;
131
+ @include typography.body-sm-md;
132
+ color: var(--font-main);
129
133
  }
130
134
 
131
135
  .labelInfo {
132
- @include typography.body-xs-reg;
133
- @include typography.ellipsis;
134
- position: absolute;
135
- color: var(--color-secondary-600-net-2);
136
- cursor: pointer;
137
- width: 100%;
136
+ color: var(--font-secondary);
137
+ flex-basis: 100%;
138
+ padding-left: calc($radio-click-target + var(--xs));
139
+ padding-right: var(--4xs);
138
140
  }