@drivenets/design-system 0.18.1 → 0.20.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 (216) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/index.cjs +17038 -10114
  3. package/dist/index.d.cts +1355 -444
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +1354 -443
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +17073 -10172
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +41 -41
  11. package/src/components/ds-bot-button/ds-bot-button.figma.ts +38 -0
  12. package/src/components/ds-bot-button/ds-bot-button.module.scss +41 -0
  13. package/src/components/ds-bot-button/ds-bot-button.tsx +48 -0
  14. package/src/components/ds-bot-button/ds-bot-button.types.ts +33 -0
  15. package/src/components/ds-bot-button/index.ts +2 -0
  16. package/src/components/ds-bot-button/net-gen-logo.tsx +46 -0
  17. package/src/components/ds-bulk-actions/components/action-button.tsx +42 -0
  18. package/src/components/ds-bulk-actions/components/menu-entries.tsx +76 -0
  19. package/src/components/ds-bulk-actions/components/overflow.tsx +75 -0
  20. package/src/components/ds-bulk-actions/ds-bulk-actions.figma.ts +28 -0
  21. package/src/components/ds-bulk-actions/ds-bulk-actions.module.scss +199 -0
  22. package/src/components/ds-bulk-actions/ds-bulk-actions.tsx +220 -0
  23. package/src/components/ds-bulk-actions/ds-bulk-actions.types.ts +170 -0
  24. package/src/components/ds-bulk-actions/ds-bulk-actions.unit.test.ts +195 -0
  25. package/src/components/ds-bulk-actions/ds-bulk-actions.utils.ts +91 -0
  26. package/src/components/ds-bulk-actions/ds-bulk-actions.utils.unit.test.ts +134 -0
  27. package/src/components/ds-bulk-actions/hooks/compute-bulk-actions-overflow.ts +85 -0
  28. package/src/components/ds-bulk-actions/hooks/use-bulk-actions-overflow.ts +153 -0
  29. package/src/components/ds-bulk-actions/index.ts +18 -0
  30. package/src/components/ds-button-v3/ds-button-v3.module.scss +2 -2
  31. package/src/components/ds-catalog-layout/ds-catalog-layout.figma.ts +1 -1
  32. package/src/components/ds-checkbox/ds-checkbox.figma.ts +3 -0
  33. package/src/components/ds-checkbox/ds-checkbox.module.scss +102 -20
  34. package/src/components/ds-checkbox/ds-checkbox.tsx +30 -2
  35. package/src/components/ds-checkbox/ds-checkbox.types.ts +10 -0
  36. package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.module.scss +169 -0
  37. package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.tsx +92 -0
  38. package/src/components/ds-code-input/collapsed-code-field/index.ts +1 -0
  39. package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.module.scss +110 -0
  40. package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.tsx +61 -0
  41. package/src/components/ds-code-input/ds-code-input-panel/index.ts +1 -0
  42. package/src/components/ds-code-input/ds-code-input.module.scss +25 -0
  43. package/src/components/ds-code-input/ds-code-input.tsx +401 -0
  44. package/src/components/ds-code-input/ds-code-input.types.ts +166 -0
  45. package/src/components/ds-code-input/ds-code-input.utils.ts +94 -0
  46. package/src/components/ds-code-input/ds-code-input.utils.unit.test.ts +166 -0
  47. package/src/components/ds-code-input/index.ts +2 -0
  48. package/src/components/ds-code-input/use-collapsed-viewport.ts +289 -0
  49. package/src/components/ds-code-input/use-editor-host.ts +87 -0
  50. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -0
  51. package/src/components/ds-empty-state/ds-empty-state.module.scss +16 -0
  52. package/src/components/ds-empty-state/ds-empty-state.tsx +45 -0
  53. package/src/components/ds-empty-state/ds-empty-state.types.ts +38 -0
  54. package/src/components/ds-empty-state/index.ts +3 -0
  55. package/src/components/ds-file-upload/ds-file-upload.tsx +4 -1
  56. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.ts +11 -0
  57. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.types.ts +41 -0
  58. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/index.ts +1 -0
  59. package/src/components/ds-filters-bar/components/ds-filters-bar-clear-all.ts +11 -0
  60. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.ts +11 -0
  61. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.types.ts +30 -0
  62. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/index.ts +1 -0
  63. package/src/components/ds-filters-bar/components/ds-filters-bar-pinned.ts +31 -0
  64. package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.ts +11 -0
  65. package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.types.ts +22 -0
  66. package/src/components/ds-filters-bar/components/ds-filters-bar-query/index.ts +1 -0
  67. package/src/components/ds-filters-bar/components/ds-filters-bar-saved-filters.ts +19 -0
  68. package/src/components/ds-filters-bar/components/ds-filters-bar-search.ts +11 -0
  69. package/src/components/ds-filters-bar/components/ds-filters-bar-summary.ts +11 -0
  70. package/src/components/ds-filters-bar/components/ds-filters-bar-toolbar.ts +11 -0
  71. package/src/components/ds-filters-bar/components/ds-filters-bar-view-switch.ts +11 -0
  72. package/src/components/ds-filters-bar/components/ds-filters-bar-view.ts +11 -0
  73. package/src/components/ds-filters-bar/components/index.ts +11 -0
  74. package/src/components/ds-filters-bar/ds-filters-bar.context.ts +69 -0
  75. package/src/components/ds-filters-bar/ds-filters-bar.tsx +120 -0
  76. package/src/components/ds-filters-bar/ds-filters-bar.types.ts +440 -0
  77. package/src/components/ds-filters-bar/ds-filters-bar.unit.test.ts +216 -0
  78. package/src/components/ds-filters-bar/ds-filters-bar.utils.ts +141 -0
  79. package/src/components/ds-filters-bar/index.ts +1 -0
  80. package/src/components/ds-form-control/ds-form-control.module.scss +18 -0
  81. package/src/components/ds-form-control/ds-form-control.tsx +61 -19
  82. package/src/components/ds-form-control/ds-form-control.types.ts +2 -1
  83. package/src/components/ds-icon/custom-icons/index.ts +2 -0
  84. package/src/components/ds-icon/custom-icons/special-adv-filters.tsx +28 -0
  85. package/src/components/ds-illustration/ds-illustration.figma.ts +34 -0
  86. package/src/components/ds-illustration/ds-illustration.module.scss +6 -0
  87. package/src/components/ds-illustration/ds-illustration.tsx +29 -0
  88. package/src/components/ds-illustration/ds-illustration.types.ts +45 -0
  89. package/src/components/ds-illustration/illustrations/configuration.tsx +51 -0
  90. package/src/components/ds-illustration/illustrations/device.tsx +133 -0
  91. package/src/components/ds-illustration/illustrations/done.tsx +107 -0
  92. package/src/components/{ds-catalog-layout/catalog-layout-empty-illustration.tsx → ds-illustration/illustrations/empty-inbox.tsx} +10 -14
  93. package/src/components/ds-illustration/illustrations/error.tsx +86 -0
  94. package/src/components/ds-illustration/illustrations/go-filter.tsx +51 -0
  95. package/src/components/ds-illustration/illustrations/illustration-clouds.tsx +10 -0
  96. package/src/components/ds-illustration/illustrations/index.ts +39 -0
  97. package/src/components/ds-illustration/illustrations/no-connection.tsx +54 -0
  98. package/src/components/ds-illustration/illustrations/no-credit-card.tsx +48 -0
  99. package/src/components/ds-illustration/illustrations/no-device.tsx +61 -0
  100. package/src/components/ds-illustration/illustrations/no-documents.tsx +52 -0
  101. package/src/components/ds-illustration/illustrations/no-gps.tsx +57 -0
  102. package/src/components/ds-illustration/illustrations/no-images.tsx +82 -0
  103. package/src/components/ds-illustration/illustrations/no-items-cart.tsx +86 -0
  104. package/src/components/ds-illustration/illustrations/no-messages.tsx +51 -0
  105. package/src/components/ds-illustration/illustrations/no-search-result.tsx +76 -0
  106. package/src/components/ds-illustration/illustrations/no-tasks.tsx +61 -0
  107. package/src/components/ds-illustration/illustrations/site.tsx +80 -0
  108. package/src/components/ds-illustration/index.ts +3 -0
  109. package/src/components/ds-main-menu/components/expanded-item/expanded-item.tsx +108 -0
  110. package/src/components/ds-main-menu/components/expanded-item/expanded-item.types.ts +8 -0
  111. package/src/components/ds-main-menu/components/expanded-item/index.ts +1 -0
  112. package/src/components/ds-main-menu/components/menu-graphic/index.ts +1 -0
  113. package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.tsx +16 -0
  114. package/src/components/ds-main-menu/components/menu-graphic/menu-graphic.types.ts +7 -0
  115. package/src/components/ds-main-menu/components/tile-link/index.ts +1 -0
  116. package/src/components/ds-main-menu/components/tile-link/tile-link.tsx +85 -0
  117. package/src/components/ds-main-menu/components/tile-link/tile-link.types.ts +8 -0
  118. package/src/components/ds-main-menu/components/utility-link/index.ts +1 -0
  119. package/src/components/ds-main-menu/components/utility-link/utility-link.tsx +37 -0
  120. package/src/components/ds-main-menu/components/utility-link/utility-link.types.ts +7 -0
  121. package/src/components/ds-main-menu/ds-main-menu.figma.ts +17 -0
  122. package/src/components/ds-main-menu/ds-main-menu.module.scss +349 -0
  123. package/src/components/ds-main-menu/ds-main-menu.tsx +102 -0
  124. package/src/components/ds-main-menu/ds-main-menu.types.ts +86 -0
  125. package/src/components/ds-main-menu/ds-main-menu.utils.ts +40 -0
  126. package/src/components/ds-main-menu/index.ts +9 -0
  127. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +1 -1
  128. package/src/components/ds-pin-toggle/ds-pin-toggle.figma.ts +27 -0
  129. package/src/components/ds-pin-toggle/ds-pin-toggle.module.scss +41 -0
  130. package/src/components/ds-pin-toggle/ds-pin-toggle.tsx +43 -0
  131. package/src/components/ds-pin-toggle/ds-pin-toggle.types.ts +29 -0
  132. package/src/components/ds-pin-toggle/index.ts +2 -0
  133. package/src/components/ds-popover/components/popover-trigger/index.ts +1 -0
  134. package/src/components/ds-popover/components/popover-trigger/popover-trigger.tsx +15 -0
  135. package/src/components/ds-popover/ds-popover.context.ts +18 -0
  136. package/src/components/ds-popover/ds-popover.hover-intent.ts +65 -0
  137. package/src/components/ds-popover/ds-popover.tsx +164 -41
  138. package/src/components/ds-popover/ds-popover.types.ts +65 -0
  139. package/src/components/ds-popover/ds-popover.utils.ts +60 -0
  140. package/src/components/ds-popover/ds-popover.utils.unit.test.ts +25 -0
  141. package/src/components/ds-saved-filters/components/ds-saved-filters-delete-confirm.tsx +53 -0
  142. package/src/components/ds-saved-filters/components/ds-saved-filters-name-entry.tsx +64 -0
  143. package/src/components/ds-saved-filters/components/ds-saved-filters-name-modal.tsx +50 -0
  144. package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.module.scss +68 -0
  145. package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.tsx +83 -0
  146. package/src/components/ds-saved-filters/components/ds-saved-filters-row/index.ts +1 -0
  147. package/src/components/ds-saved-filters/components/ds-saved-filters-save.tsx +164 -0
  148. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.module.scss +21 -0
  149. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.tsx +237 -0
  150. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/index.ts +1 -0
  151. package/src/components/ds-saved-filters/components/index.ts +2 -0
  152. package/src/components/ds-saved-filters/ds-saved-filters.ts +6 -0
  153. package/src/components/ds-saved-filters/ds-saved-filters.types.ts +106 -0
  154. package/src/components/ds-saved-filters/ds-saved-filters.utils.ts +35 -0
  155. package/src/components/ds-saved-filters/hooks/index.ts +1 -0
  156. package/src/components/ds-saved-filters/hooks/use-picker-outside-dismiss.ts +42 -0
  157. package/src/components/ds-saved-filters/index.ts +2 -0
  158. package/src/components/ds-segment-group/ds-segment-group.module.scss +65 -37
  159. package/src/components/ds-segment-group/ds-segment-group.tsx +17 -5
  160. package/src/components/ds-segment-group/ds-segment-group.types.ts +20 -5
  161. package/src/components/ds-segment-group/figma/ds-segment-group-v2-item.figma.ts +109 -0
  162. package/src/components/ds-segment-group/figma/ds-segment-group-v2.figma.ts +31 -0
  163. package/src/components/ds-site-node/ds-site-node.tsx +2 -2
  164. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +12 -2
  165. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +2 -3
  166. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +1 -6
  167. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +6 -3
  168. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +18 -7
  169. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +27 -21
  170. package/src/components/ds-table/components/ds-table-header-label.module.scss +12 -0
  171. package/src/components/ds-table/components/ds-table-header-label.tsx +28 -0
  172. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +0 -1
  173. package/src/components/ds-table/context/ds-table-context.tsx +8 -0
  174. package/src/components/ds-table/ds-table.module.scss +26 -16
  175. package/src/components/ds-table/ds-table.tsx +46 -14
  176. package/src/components/ds-table/ds-table.types.ts +33 -3
  177. package/src/components/ds-table/hooks/use-column-resize.ts +114 -11
  178. package/src/components/ds-table/hooks/use-scrollbar-spacer.ts +108 -0
  179. package/src/components/ds-table/hooks/use-scrollbar-spacer.unit.test.ts +26 -0
  180. package/src/components/ds-table/styles/_variables.scss +0 -2
  181. package/src/components/ds-table/utils/column-size.ts +274 -1
  182. package/src/components/ds-table/utils/column-size.unit.test.ts +157 -0
  183. package/src/components/ds-table/utils/constants.ts +13 -5
  184. package/src/components/ds-table/utils/table-columns.tsx +27 -13
  185. package/src/components/ds-table/utils/table-columns.unit.test.ts +44 -0
  186. package/src/components/ds-tag/ds-tag.figma.ts +23 -3
  187. package/src/components/ds-tag/ds-tag.module.scss +71 -7
  188. package/src/components/ds-tag/ds-tag.tsx +115 -14
  189. package/src/components/ds-tag/ds-tag.types.ts +70 -3
  190. package/src/components/ds-tag/ds-tag.unit.test.ts +23 -4
  191. package/src/components/ds-tag/index.ts +2 -2
  192. package/src/components/ds-text-input/ds-input-field-v02.figma.ts +2 -0
  193. package/src/components/ds-toggle/ds-toggle.module.scss +18 -0
  194. package/src/components/ds-toggle/ds-toggle.tsx +15 -9
  195. package/src/components/ds-toggle/ds-toggle.types.ts +6 -0
  196. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.module.scss +69 -0
  197. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.tsx +55 -0
  198. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.types.ts +43 -0
  199. package/src/components/ds-toggle-filter-data/index.ts +1 -0
  200. package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -0
  201. package/src/components/ds-tooltip/ds-tooltip.tsx +7 -3
  202. package/src/components/ds-tooltip/ds-tooltip.types.ts +17 -0
  203. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.figma.ts +35 -0
  204. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.module.scss +150 -0
  205. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.tsx +161 -0
  206. package/src/components/ds-top-bar-navigation/ds-top-bar-navigation.types.ts +117 -0
  207. package/src/components/ds-top-bar-navigation/index.ts +2 -0
  208. package/src/components/ds-tree/ds-tree.tsx +10 -10
  209. package/src/components/ds-tree/ds-tree.types.ts +40 -7
  210. package/src/index.ts +8 -0
  211. package/src/utils/use-is-horizontally-overflowing.ts +35 -0
  212. package/src/components/ds-site-node/hooks/use-is-overflowing.ts +0 -31
  213. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +0 -112
  214. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +0 -67
  215. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.types.ts +0 -41
  216. package/src/components/ds-table/components/ds-table-bulk-actions/index.ts +0 -2
@@ -28,6 +28,9 @@ const state = instance.getEnum('state', {
28
28
  const checkedAttr =
29
29
  checked === 'partial' ? 'defaultChecked="indeterminate"' : checked === 'checked' ? 'defaultChecked' : '';
30
30
 
31
+ // `size` is not mapped: it lives on `Structure_CheckboxV1`, while the published
32
+ // `DsCheckboxV3` set still exposes only `checked x state x color`. The snippet
33
+ // therefore emits the `medium` default until design bubbles the variant up.
31
34
  const attrs = [
32
35
  'label="Label"',
33
36
  color === 'warning' ? 'variant="warning"' : '',
@@ -3,7 +3,10 @@
3
3
 
4
4
  $checkbox-size: 16px;
5
5
  $checkbox-border-radius: 2px;
6
+ $checkbox-border-width: 1.5px;
6
7
  $checkbox-focus-ring-width: 2px;
8
+ $row-padding-medium: var(--4xs);
9
+ $row-padding-large: var(--xs);
7
10
  $transition-duration: 0.2s;
8
11
 
9
12
  .root {
@@ -11,7 +14,7 @@ $transition-duration: 0.2s;
11
14
  display: inline-flex;
12
15
  align-items: flex-start;
13
16
  gap: var(--3xs);
14
- padding: var(--4xs);
17
+ padding: $row-padding-medium;
15
18
  border-radius: $checkbox-border-radius;
16
19
  cursor: pointer;
17
20
  transition: background-color $transition-duration ease-in-out;
@@ -34,7 +37,7 @@ $transition-duration: 0.2s;
34
37
  flex-shrink: 0;
35
38
  width: $checkbox-size;
36
39
  height: $checkbox-size;
37
- border: 1px solid var(--secondary-400);
40
+ border: $checkbox-border-width solid var(--border-contrast);
38
41
  border-radius: $checkbox-border-radius;
39
42
  background-color: transparent;
40
43
  transition:
@@ -60,7 +63,7 @@ $transition-duration: 0.2s;
60
63
  }
61
64
 
62
65
  .label {
63
- @include typography.body-sm-reg;
66
+ @include typography.body-sm-md;
64
67
  line-height: normal;
65
68
  color: var(--font-main);
66
69
  }
@@ -103,24 +106,35 @@ $transition-duration: 0.2s;
103
106
  }
104
107
  }
105
108
  }
106
- }
107
109
 
108
- // Overlay the native input over the control so `getByRole('checkbox')` clicks
109
- // land on a real interactive surface. Ark applies `visuallyHiddenStyle` inline
110
- // (1x1, clipped, off-screen) which Playwright refuses to click; `!important`
111
- // is required to beat inline-style specificity.
112
- .hiddenInput {
113
- position: absolute !important;
114
- inset-block-start: var(--4xs) !important;
115
- inset-inline-start: var(--4xs) !important;
116
- width: $checkbox-size !important;
117
- height: $checkbox-size !important;
118
- margin: 0 !important;
119
- padding: 0 !important;
120
- clip: auto !important;
121
- overflow: visible !important;
122
- opacity: 0 !important;
123
- cursor: inherit !important;
110
+ // Overlay the native input over the control so `getByRole('checkbox')`
111
+ // clicks land on a real interactive surface. Ark applies
112
+ // `visuallyHiddenStyle` inline (1x1, clipped, off-screen) which Playwright
113
+ // refuses to click; `!important` is required to beat inline-style
114
+ // specificity. The control is the row's first child, so the offset is the
115
+ // root's own padding — which is size-dependent.
116
+ .hiddenInput {
117
+ position: absolute !important;
118
+ inset-block-start: $row-padding-medium !important;
119
+ inset-inline-start: $row-padding-medium !important;
120
+ width: $checkbox-size !important;
121
+ height: $checkbox-size !important;
122
+ margin: 0 !important;
123
+ padding: 0 !important;
124
+ clip: auto !important;
125
+ overflow: visible !important;
126
+ opacity: 0 !important;
127
+ cursor: inherit !important;
128
+ }
129
+
130
+ &[data-size='large'] {
131
+ padding: $row-padding-large;
132
+
133
+ .hiddenInput {
134
+ inset-block-start: $row-padding-large !important;
135
+ inset-inline-start: $row-padding-large !important;
136
+ }
137
+ }
124
138
  }
125
139
 
126
140
  .warning {
@@ -142,3 +156,71 @@ $transition-duration: 0.2s;
142
156
  }
143
157
  }
144
158
  }
159
+
160
+ // Only rendered when `actions` is set, so the plain checkbox keeps its
161
+ // original single-element layout.
162
+ //
163
+ // The row reads as one unit, so the hover surface lives here rather than on
164
+ // `.root`. `.root` is the `<label>`, which ends with the label text — painting
165
+ // there leaves `actions` as a cold gap outside the highlight.
166
+ .wrapper {
167
+ display: inline-flex;
168
+ align-items: flex-start;
169
+ gap: var(--3xs);
170
+ border-radius: $checkbox-border-radius;
171
+ transition: background-color $transition-duration ease-in-out;
172
+
173
+ // Balances the root's inline padding on the far side of `actions`, so the
174
+ // highlight is not inset at one end and tight against the pin at the other.
175
+ padding-inline-end: $row-padding-medium;
176
+
177
+ &[data-size='large'] {
178
+ padding-inline-end: $row-padding-large;
179
+ }
180
+
181
+ &:not([data-disabled]):hover,
182
+ &:not([data-disabled]):has(.root[data-focus-visible]) {
183
+ background-color: var(--background-secondary-hover);
184
+ }
185
+
186
+ &[data-variant='warning']:not([data-disabled]):hover,
187
+ &[data-variant='warning']:not([data-disabled]):has(.root[data-focus-visible]) {
188
+ background-color: var(--background-warning);
189
+ }
190
+
191
+ &:has(.root[data-focus-visible]) {
192
+ outline: $checkbox-focus-ring-width solid var(--background-primary-hover);
193
+ outline-offset: 0;
194
+ }
195
+
196
+ // The wrapper paints the row, so the label half must not paint a second,
197
+ // narrower highlight or ring inside it.
198
+ .root {
199
+ &:hover,
200
+ &[data-hover],
201
+ &[data-focus-visible] {
202
+ background-color: transparent;
203
+ outline: none;
204
+ }
205
+ }
206
+ }
207
+
208
+ // Matches the height of the root's first row — the control plus the root's
209
+ // block padding — so the slot centres against the control at either size,
210
+ // with or without `labelInfo`.
211
+ //
212
+ // `margin-inline-start: auto` pins the slot to the end of the row whenever the
213
+ // row is wider than its content — a stretched list row, as inside a
214
+ // `DsCheckboxGroup` — matching Figma, where the pin sits at the row's trailing
215
+ // edge rather than hugging the label. It is inert when the row is content-width.
216
+ .actions {
217
+ display: inline-flex;
218
+ align-items: center;
219
+ flex-shrink: 0;
220
+ margin-inline-start: auto;
221
+ min-block-size: calc(#{$checkbox-size} + #{$row-padding-medium} * 2);
222
+
223
+ &[data-size='large'] {
224
+ min-block-size: calc(#{$checkbox-size} + #{$row-padding-large} * 2);
225
+ }
226
+ }
@@ -15,19 +15,28 @@ import { DsIcon } from '../ds-icon';
15
15
  */
16
16
  const DsCheckbox = ({
17
17
  variant = 'default',
18
+ size = 'medium',
18
19
  label,
19
20
  labelInfo,
21
+ actions,
20
22
  className,
23
+ style,
21
24
  onCheckedChange,
22
25
  value,
23
26
  ...props
24
27
  }: DsCheckboxProps) => {
25
- return (
28
+ // `className` and `style` belong to whichever element is outermost, so they
29
+ // move to the wrapper when `actions` adds one.
30
+ const hasActions = Boolean(actions);
31
+
32
+ const checkbox = (
26
33
  <Checkbox.Root
27
- className={classNames(styles.root, variant === 'warning' && styles.warning, className)}
34
+ className={classNames(styles.root, variant === 'warning' && styles.warning, !hasActions && className)}
35
+ style={hasActions ? undefined : style}
28
36
  onCheckedChange={(details) => onCheckedChange?.(details.checked)}
29
37
  {...props}
30
38
  value={value === undefined ? undefined : String(value)}
39
+ data-size={size}
31
40
  >
32
41
  <Checkbox.Control className={styles.control}>
33
42
  <Checkbox.Indicator className={styles.indicator}>
@@ -46,6 +55,25 @@ const DsCheckbox = ({
46
55
  )}
47
56
  </Checkbox.Root>
48
57
  );
58
+
59
+ if (!hasActions) {
60
+ return checkbox;
61
+ }
62
+
63
+ return (
64
+ <div
65
+ className={classNames(styles.wrapper, className)}
66
+ style={style}
67
+ data-size={size}
68
+ data-variant={variant}
69
+ data-disabled={props.disabled ? '' : undefined}
70
+ >
71
+ {checkbox}
72
+ <div className={styles.actions} data-size={size}>
73
+ {actions}
74
+ </div>
75
+ </div>
76
+ );
49
77
  };
50
78
 
51
79
  DsCheckbox.displayName = 'DsCheckbox';
@@ -4,6 +4,9 @@ import { type Checkbox } from '@ark-ui/react/checkbox';
4
4
  export const checkboxVariants = ['default', 'warning'] as const;
5
5
  export type CheckboxVariant = (typeof checkboxVariants)[number];
6
6
 
7
+ export const checkboxSizes = ['medium', 'large'] as const;
8
+ export type CheckboxSize = (typeof checkboxSizes)[number];
9
+
7
10
  export interface DsCheckboxProps extends Omit<
8
11
  ComponentPropsWithoutRef<typeof Checkbox.Root>,
9
12
  'onCheckedChange' | 'value'
@@ -16,6 +19,7 @@ export interface DsCheckboxProps extends Omit<
16
19
  * Visual variant of the checkbox
17
20
  */
18
21
  variant?: CheckboxVariant;
22
+ size?: CheckboxSize;
19
23
  /**
20
24
  * Label for the checkbox
21
25
  */
@@ -24,6 +28,12 @@ export interface DsCheckboxProps extends Omit<
24
28
  * Additional label info for the checkbox
25
29
  */
26
30
  labelInfo?: ReactNode;
31
+ /**
32
+ * Trailing slot for interactive content, such as a pin or overflow menu.
33
+ * Rendered as a sibling of the checkbox rather than inside it: the checkbox
34
+ * root is a `<label>`,
35
+ */
36
+ actions?: ReactNode;
27
37
  /**
28
38
  * Disables the checkbox, preventing user interaction
29
39
  */
@@ -0,0 +1,169 @@
1
+ @use '../../../styles/typography' as typography;
2
+ @use '../../../styles/shared/input' as input;
3
+
4
+ .container {
5
+ @include input.inputContainer;
6
+ gap: var(--xs);
7
+
8
+ &[data-invalid] {
9
+ --form-control-border-color: var(--background-error);
10
+ }
11
+
12
+ &.small {
13
+ height: input.$input-text-height-small;
14
+ padding-block: var(--none);
15
+
16
+ .input,
17
+ .echo,
18
+ .input::placeholder {
19
+ line-height: calc(#{input.$input-text-height-small} - 2px);
20
+ }
21
+ }
22
+
23
+ &:hover:not([data-disabled]) {
24
+ border-color: var(--form-control-border-color, var(--background-action-hover));
25
+ }
26
+
27
+ &:focus-within {
28
+ border-color: var(--form-control-border-color, var(--border-action-primary));
29
+ }
30
+ }
31
+
32
+ .field {
33
+ position: relative;
34
+ display: flex;
35
+ flex: 1;
36
+ align-self: stretch;
37
+ min-width: 0;
38
+ cursor: text;
39
+ }
40
+
41
+ .editorSlot {
42
+ display: flex;
43
+ flex: 1;
44
+ align-self: stretch;
45
+ min-width: 0;
46
+ }
47
+
48
+ .editorSlotHidden {
49
+ position: absolute;
50
+ width: 0;
51
+ height: 0;
52
+ overflow: hidden;
53
+ }
54
+
55
+ .editorHost {
56
+ display: flex;
57
+ flex: 1;
58
+ align-self: stretch;
59
+ min-width: 0;
60
+ height: 100%;
61
+ }
62
+
63
+ .echo {
64
+ @include typography.code-sm-reg;
65
+ position: absolute;
66
+ inset: 0;
67
+ display: flex;
68
+ align-items: baseline;
69
+ overflow: hidden;
70
+ color: var(--font-main);
71
+ text-overflow: ellipsis;
72
+ white-space: nowrap;
73
+ pointer-events: none;
74
+ }
75
+
76
+ .placeholder {
77
+ color: var(--font-placeholder);
78
+ }
79
+
80
+ .input {
81
+ @include input.input;
82
+ @include typography.code-sm-reg;
83
+ width: 100%;
84
+ height: 100%;
85
+ padding: 0;
86
+ appearance: none;
87
+ transition: none;
88
+ white-space: pre;
89
+ overflow: hidden;
90
+ resize: none;
91
+
92
+ &:focus,
93
+ &:focus-visible {
94
+ outline: none;
95
+ box-shadow: none;
96
+ }
97
+
98
+ &::placeholder {
99
+ @include typography.code-sm-reg;
100
+ color: var(--font-placeholder);
101
+ }
102
+
103
+ &:disabled {
104
+ color: var(--font-disabled);
105
+ }
106
+ }
107
+
108
+ .container[data-disabled] .echo {
109
+ color: var(--font-disabled);
110
+ }
111
+
112
+ .fadeStart,
113
+ .fadeEnd {
114
+ position: absolute;
115
+ inset-block: 0;
116
+ width: var(--xl);
117
+ pointer-events: none;
118
+ }
119
+
120
+ .fadeStart {
121
+ inset-inline-start: 0;
122
+ background: linear-gradient(to right, var(--background), transparent);
123
+ }
124
+
125
+ .fadeEnd {
126
+ inset-inline-end: 0;
127
+ background: linear-gradient(to left, var(--background), transparent);
128
+ }
129
+
130
+ .ellipsis {
131
+ position: absolute;
132
+ inset-block: 0;
133
+ inset-inline-end: 0;
134
+ display: flex;
135
+ align-items: center;
136
+ pointer-events: none;
137
+ color: var(--font-main);
138
+ background: var(--background);
139
+
140
+ &::after {
141
+ content: '…';
142
+ @include typography.code-sm-reg;
143
+ }
144
+ }
145
+
146
+ .indicator,
147
+ .indicatorButton {
148
+ @include typography.code-xs-reg;
149
+ flex: none;
150
+ color: var(--font-secondary);
151
+ white-space: nowrap;
152
+ }
153
+
154
+ .indicatorButton {
155
+ margin: 0;
156
+ padding: 0;
157
+ border: 0;
158
+ background: transparent;
159
+ cursor: pointer;
160
+
161
+ &:hover {
162
+ color: var(--font-main);
163
+ }
164
+
165
+ &:focus-visible {
166
+ outline: 2px solid var(--border-action-primary);
167
+ outline-offset: 2px;
168
+ }
169
+ }
@@ -0,0 +1,92 @@
1
+ import { type ReactNode, type Ref } from 'react';
2
+ import classNames from 'classnames';
3
+ import type { CodeInputSize } from '../ds-code-input.types';
4
+ import type { CollapsedIndicator } from '../use-collapsed-viewport';
5
+ import { DsStack } from '../../ds-stack';
6
+ import styles from './collapsed-code-field.module.scss';
7
+
8
+ export interface CollapsedCodeFieldProps {
9
+ size: CodeInputSize;
10
+ placeholder?: string;
11
+ disabled: boolean;
12
+ invalid: boolean;
13
+ showEcho: boolean;
14
+ echoText: string;
15
+ fadeStart: boolean;
16
+ fadeEnd: boolean;
17
+ showEllipsis: boolean;
18
+ indicator: CollapsedIndicator | null;
19
+ additionalLinesLabel: (count: number) => string;
20
+ additionalLines: (count: number) => string;
21
+ linePosition: (current: number, total: number) => string;
22
+ selectionStatus: (selected: number, total: number) => string;
23
+ editorSlotRef: Ref<HTMLDivElement>;
24
+ adornment: ReactNode;
25
+ className?: string;
26
+ onExpand: () => void;
27
+ }
28
+
29
+ const CollapsedCodeField = ({
30
+ size,
31
+ placeholder,
32
+ disabled,
33
+ invalid,
34
+ showEcho,
35
+ echoText,
36
+ fadeStart,
37
+ fadeEnd,
38
+ showEllipsis,
39
+ indicator,
40
+ additionalLinesLabel,
41
+ additionalLines,
42
+ linePosition,
43
+ selectionStatus,
44
+ editorSlotRef,
45
+ adornment,
46
+ className,
47
+ onExpand,
48
+ }: CollapsedCodeFieldProps) => (
49
+ <div
50
+ className={classNames(styles.container, styles[size], className)}
51
+ data-disabled={disabled || undefined}
52
+ data-invalid={invalid || undefined}
53
+ >
54
+ <div className={styles.field}>
55
+ {/* While expanded, paint a fake line so the overlay can take the real
56
+ textarea without the compact row going empty. */}
57
+ {showEcho && (
58
+ <div className={classNames(styles.echo, !echoText && styles.placeholder)}>
59
+ {echoText || placeholder}
60
+ </div>
61
+ )}
62
+ <div
63
+ ref={editorSlotRef}
64
+ className={classNames(styles.editorSlot, showEcho && styles.editorSlotHidden)}
65
+ />
66
+ {fadeStart && <div className={styles.fadeStart} aria-hidden />}
67
+ {fadeEnd && <div className={styles.fadeEnd} aria-hidden />}
68
+ {showEllipsis && !showEcho && <div className={styles.ellipsis} aria-hidden />}
69
+ </div>
70
+ <DsStack direction="row" alignItems="center" flex="none" gap="var(--xs)">
71
+ {indicator?.kind === 'plusN' && (
72
+ <button
73
+ type="button"
74
+ className={styles.indicatorButton}
75
+ aria-label={additionalLinesLabel(indicator.count)}
76
+ onClick={onExpand}
77
+ >
78
+ {additionalLines(indicator.count)}
79
+ </button>
80
+ )}
81
+ {indicator?.kind === 'line' && (
82
+ <span className={styles.indicator}>{linePosition(indicator.current, indicator.total)}</span>
83
+ )}
84
+ {indicator?.kind === 'selection' && (
85
+ <span className={styles.indicator}>{selectionStatus(indicator.selected, indicator.total)}</span>
86
+ )}
87
+ {adornment}
88
+ </DsStack>
89
+ </div>
90
+ );
91
+
92
+ export default CollapsedCodeField;
@@ -0,0 +1 @@
1
+ export { default as CollapsedCodeField } from './collapsed-code-field';
@@ -0,0 +1,110 @@
1
+ @use '../../../styles/typography' as typography;
2
+
3
+ $code-surface-max-height: 220px;
4
+
5
+ // One line of code plus the surface padding, so an empty panel keeps its shape.
6
+ $code-surface-min-height: calc(var(--body-line-height-small) + 2 * var(--standard));
7
+
8
+ @mixin code-surface-layer {
9
+ @include typography.code-sm-reg;
10
+ box-sizing: border-box;
11
+ margin: 0;
12
+ padding: var(--standard);
13
+ border: 0;
14
+ text-align: left;
15
+ direction: ltr;
16
+ tab-size: 2;
17
+ white-space: pre-wrap;
18
+ overflow-wrap: break-word;
19
+ word-break: normal;
20
+ }
21
+
22
+ .body {
23
+ display: flex;
24
+ flex-direction: column;
25
+ }
26
+
27
+ .searchRow {
28
+ display: block;
29
+ order: -1;
30
+ padding: var(--standard);
31
+ border-bottom: 1px solid var(--border);
32
+ }
33
+
34
+ // Scrolling both layers as one element is what keeps the marks glued to the glyphs —
35
+ // neither layer scrolls on its own, so there is nothing to keep in sync.
36
+ .codeScroll {
37
+ max-height: $code-surface-max-height;
38
+ overflow-y: auto;
39
+ overflow-x: hidden;
40
+ }
41
+
42
+ .codeStack {
43
+ position: relative;
44
+ min-height: $code-surface-min-height;
45
+
46
+ --ds-code-input-match-background: var(--background-warning);
47
+ }
48
+
49
+ .backdrop {
50
+ @include code-surface-layer;
51
+ position: relative;
52
+ pointer-events: none;
53
+ user-select: none;
54
+ color: transparent;
55
+ }
56
+
57
+ .mark {
58
+ color: inherit;
59
+ background: var(--ds-code-input-match-background);
60
+ border-radius: 2px;
61
+ }
62
+
63
+ .editorSlot {
64
+ position: absolute;
65
+ inset: 0;
66
+ }
67
+
68
+ .editorHost {
69
+ position: absolute;
70
+ inset: 0;
71
+ }
72
+
73
+ .codeArea {
74
+ @include code-surface-layer;
75
+ position: absolute;
76
+ inset: 0;
77
+ display: block;
78
+ width: 100%;
79
+ overflow: hidden;
80
+ background: transparent;
81
+ color: var(--font-main);
82
+ caret-color: var(--font-main);
83
+
84
+ // A user-resized textarea would drift away from the backdrop that sizes it.
85
+ resize: none;
86
+ outline: none;
87
+
88
+ &::selection {
89
+ background: var(--form-control-selection-color, var(--background-secondary-hover));
90
+ }
91
+
92
+ &:read-only,
93
+ &:disabled {
94
+ color: var(--font-main);
95
+ opacity: 1;
96
+ cursor: default;
97
+ }
98
+ }
99
+
100
+ .visuallyHidden {
101
+ position: absolute;
102
+ width: 1px;
103
+ height: 1px;
104
+ margin: -1px;
105
+ padding: 0;
106
+ border: 0;
107
+ overflow: hidden;
108
+ white-space: nowrap;
109
+ clip-path: inset(50%);
110
+ }
@@ -0,0 +1,61 @@
1
+ import { type Ref, Fragment, useEffect, useState } from 'react';
2
+ import { useHighlight } from '@ark-ui/react/highlight';
3
+ import { DsIcon } from '../../ds-icon';
4
+ import { DsTextInput } from '../../ds-text-input';
5
+ import styles from './ds-code-input-panel.module.scss';
6
+
7
+ export interface DsCodeInputPanelProps {
8
+ value: string;
9
+ isOpen: boolean;
10
+ searchPlaceholder: string;
11
+ editorSlotRef: Ref<HTMLDivElement>;
12
+ }
13
+
14
+ const DsCodeInputPanel = ({ value, isOpen, searchPlaceholder, editorSlotRef }: DsCodeInputPanelProps) => {
15
+ const [query, setQuery] = useState('');
16
+
17
+ useEffect(() => {
18
+ if (!isOpen) {
19
+ setQuery('');
20
+ }
21
+ }, [isOpen]);
22
+
23
+ const chunks = useHighlight({ text: value, query, ignoreCase: true, matchAll: true });
24
+
25
+ return (
26
+ <div className={styles.body}>
27
+ <div className={styles.codeScroll}>
28
+ <div className={styles.codeStack}>
29
+ {isOpen && (
30
+ <div className={styles.backdrop} aria-hidden="true">
31
+ {chunks.map((chunk, index) =>
32
+ chunk.match ? (
33
+ <mark key={index} className={styles.mark}>
34
+ {chunk.text}
35
+ </mark>
36
+ ) : (
37
+ <Fragment key={index}>{chunk.text}</Fragment>
38
+ ),
39
+ )}
40
+ {'\n'}
41
+ </div>
42
+ )}
43
+ <div ref={editorSlotRef} className={styles.editorSlot} />
44
+ </div>
45
+ </div>
46
+ {isOpen && (
47
+ <label className={styles.searchRow}>
48
+ <span className={styles.visuallyHidden}>{searchPlaceholder}</span>
49
+ <DsTextInput
50
+ value={query}
51
+ placeholder={searchPlaceholder}
52
+ onValueChange={setQuery}
53
+ slots={{ startAdornment: <DsIcon icon="search" size="tiny" aria-hidden /> }}
54
+ />
55
+ </label>
56
+ )}
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default DsCodeInputPanel;
@@ -0,0 +1 @@
1
+ export { default as DsCodeInputPanel } from './ds-code-input-panel';