@drivenets/design-system 0.14.0 → 0.16.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 (193) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +1 -1
  3. package/dist/index.cjs +1239 -262
  4. package/dist/index.d.cts +904 -1169
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.ts +904 -1169
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1221 -258
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.css +1 -1
  11. package/package.json +15 -12
  12. package/src/components/ds-alert-banner/ds-alert-banner.tsx +11 -2
  13. package/src/components/ds-autocomplete/ds-autocomplete.figma.ts +36 -0
  14. package/src/components/ds-autocomplete/ds-autocomplete.tsx +2 -0
  15. package/src/components/ds-avatar/ds-avatar.figma.ts +30 -0
  16. package/src/components/ds-avatar/ds-avatar.tsx +6 -1
  17. package/src/components/ds-avatar/ds-avatar.types.ts +5 -2
  18. package/src/components/ds-avatar-group/ds-avatar-group.figma.ts +27 -0
  19. package/src/components/ds-avatar-group/ds-avatar-group.tsx +6 -1
  20. package/src/components/ds-avatar-group/ds-avatar-group.types.ts +3 -1
  21. package/src/components/ds-breadcrumb/ds-breadcrumb.figma.ts +19 -0
  22. package/src/components/ds-breadcrumb/ds-breadcrumb.tsx +2 -0
  23. package/src/components/ds-button/ds-button.tsx +2 -0
  24. package/src/components/ds-button-v3/ds-button-v3.figma.ts +1 -1
  25. package/src/components/ds-button-v3/ds-button-v3.tsx +6 -0
  26. package/src/components/ds-button-v3/index.ts +2 -0
  27. package/src/components/ds-card/ds-card.tsx +7 -3
  28. package/src/components/ds-catalog-layout/ds-catalog-layout.tsx +8 -0
  29. package/src/components/ds-checkbox/ds-checkbox-level-2.figma.ts +40 -0
  30. package/src/components/ds-checkbox/ds-checkbox.figma.ts +45 -0
  31. package/src/components/ds-checkbox/ds-checkbox.module.scss +1 -1
  32. package/src/components/ds-checkbox/ds-checkbox.tsx +9 -1
  33. package/src/components/ds-checkbox-group/ds-checkbox-group.figma.ts +15 -0
  34. package/src/components/ds-checkbox-group/ds-checkbox-group.tsx +2 -0
  35. package/src/components/ds-chip/ds-chip.tsx +2 -0
  36. package/src/components/ds-chip-group/ds-chip-group.tsx +2 -0
  37. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.tsx +1 -0
  38. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx +1 -0
  39. package/src/components/ds-comment-bubble/ds-comment-bubble.tsx +1 -0
  40. package/src/components/ds-comment-card/ds-comment-card.tsx +1 -0
  41. package/src/components/ds-comment-indicator/ds-comment-indicator.tsx +1 -0
  42. package/src/components/ds-comments-drawer/ds-comments-drawer.tsx +1 -0
  43. package/src/components/ds-confirmation/ds-confirmation.tsx +1 -0
  44. package/src/components/ds-date-input/ds-date-input.tsx +2 -0
  45. package/src/components/ds-date-picker/ds-date-picker.tsx +9 -0
  46. package/src/components/ds-date-picker/ds-date-picker.types.ts +1 -1
  47. package/src/components/ds-date-range-picker/ds-date-range-picker.tsx +2 -0
  48. package/src/components/ds-dialog/ds-dialog.tsx +9 -0
  49. package/src/components/ds-dialog/ds-dialog.types.ts +1 -1
  50. package/src/components/ds-divider/ds-divider.figma.ts +19 -0
  51. package/src/components/ds-divider/ds-divider.tsx +2 -0
  52. package/src/components/ds-drawer/ds-drawer.module.scss +14 -0
  53. package/src/components/ds-drawer/ds-drawer.tsx +30 -1
  54. package/src/components/ds-drawer/ds-drawer.types.ts +7 -0
  55. package/src/components/ds-drawer/figma/ds-drawer-content.figma.ts +15 -0
  56. package/src/components/ds-drawer/figma/ds-drawer-footer.figma.ts +19 -0
  57. package/src/components/ds-drawer/figma/ds-drawer-header.figma.ts +16 -0
  58. package/src/components/ds-drawer/figma/ds-drawer.figma.ts +36 -0
  59. package/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss +12 -3
  60. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +24 -6
  61. package/src/components/ds-dropdown-menu/ds-dropdown-menu.types.ts +10 -1
  62. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-footer.figma.ts +11 -0
  63. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-group.figma.ts +23 -0
  64. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-item.figma.ts +29 -0
  65. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-separator.figma.ts +11 -0
  66. package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu.figma.ts +24 -0
  67. package/src/components/ds-expandable-text-input/ds-expandable-text-input.figma.ts +24 -0
  68. package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +1 -0
  69. package/src/components/ds-file-upload/ds-file-upload.figma.ts +29 -0
  70. package/src/components/ds-file-upload/ds-file-upload.tsx +2 -0
  71. package/src/components/ds-file-upload/hooks/use-file-upload.ts +7 -0
  72. package/src/components/ds-filter-status-icon/ds-filter-status-icon.figma.ts +22 -0
  73. package/src/components/ds-filter-status-icon/ds-filter-status-icon.tsx +1 -0
  74. package/src/components/ds-form-control/ds-form-control.tsx +3 -0
  75. package/src/components/ds-grid/index.ts +3 -0
  76. package/src/components/ds-icon/ds-icon.tsx +2 -0
  77. package/src/components/ds-key-value-pair/ds-key-value-pair.figma.ts +24 -0
  78. package/src/components/ds-key-value-pair/ds-key-value-pair.tsx +2 -0
  79. package/src/components/ds-loader/ds-loader.figma.ts +11 -0
  80. package/src/components/ds-loader/ds-loader.tsx +2 -0
  81. package/src/components/ds-modal/ds-modal.tsx +9 -0
  82. package/src/components/ds-number-input/ds-number-input.tsx +2 -0
  83. package/src/components/ds-panel/ds-panel.figma.ts +16 -0
  84. package/src/components/ds-panel/index.ts +2 -0
  85. package/src/components/ds-password-input/ds-password-input.tsx +2 -0
  86. package/src/components/ds-popover/ds-popover.tsx +9 -0
  87. package/src/components/ds-popover/figma/ds-popover-content-item.figma.ts +18 -0
  88. package/src/components/ds-popover/figma/ds-popover-content.figma.ts +15 -0
  89. package/src/components/ds-popover/figma/ds-popover-footer.figma.ts +19 -0
  90. package/src/components/ds-popover/figma/ds-popover-header.figma.ts +16 -0
  91. package/src/components/ds-popover/figma/ds-popover.figma.ts +34 -0
  92. package/src/components/ds-progress-arc/ds-progress-arc.figma.ts +23 -0
  93. package/src/components/ds-progress-arc/ds-progress-arc.tsx +2 -0
  94. package/src/components/ds-progress-donut/ds-progress-donut.figma.ts +45 -0
  95. package/src/components/ds-progress-donut/ds-progress-donut.tsx +2 -0
  96. package/src/components/ds-progress-linear/ds-progress-linear.figma.ts +26 -0
  97. package/src/components/ds-progress-linear/ds-progress-linear.tsx +2 -0
  98. package/src/components/ds-progress-task-bar/ds-progress-task-bar.figma.ts +29 -0
  99. package/src/components/ds-progress-task-bar/ds-progress-task-bar.module.scss +39 -0
  100. package/src/components/ds-progress-task-bar/ds-progress-task-bar.tsx +124 -0
  101. package/src/components/ds-progress-task-bar/ds-progress-task-bar.types.ts +65 -0
  102. package/src/components/ds-progress-task-bar/index.ts +2 -0
  103. package/src/components/ds-progress-task-bar/utils.ts +51 -0
  104. package/src/components/ds-radio-group/ds-radio-group.tsx +3 -0
  105. package/src/components/ds-radio-group/figma/ds-radio-group-item.figma.ts +24 -0
  106. package/src/components/ds-radio-group/figma/ds-radio-group.figma.ts +14 -0
  107. package/src/components/ds-segment-group/ds-segment-group.tsx +3 -0
  108. package/src/components/ds-segment-group/figma/ds-segment-group-item.figma.ts +25 -0
  109. package/src/components/ds-segment-group/figma/ds-segment-group.figma.ts +23 -0
  110. package/src/components/ds-select/ds-select.tsx +2 -0
  111. package/src/components/ds-site-node/ds-site-node.figma.ts +27 -0
  112. package/src/components/ds-site-node/ds-site-node.module.scss +59 -0
  113. package/src/components/ds-site-node/ds-site-node.tsx +73 -0
  114. package/src/components/ds-site-node/ds-site-node.types.ts +36 -0
  115. package/src/components/ds-site-node/hooks/use-is-overflowing.ts +31 -0
  116. package/src/components/ds-site-node/index.ts +3 -0
  117. package/src/components/ds-skeleton/ds-skeleton-circle.tsx +2 -0
  118. package/src/components/ds-skeleton/ds-skeleton-rect.tsx +2 -0
  119. package/src/components/ds-skeleton/ds-skeleton-text.tsx +2 -0
  120. package/src/components/ds-skeleton/ds-skeleton.ts +7 -1
  121. package/src/components/ds-smart-tabs/ds-smart-tabs.tsx +4 -0
  122. package/src/components/ds-spinner/ds-spinner.figma.ts +19 -0
  123. package/src/components/ds-spinner/ds-spinner.tsx +2 -0
  124. package/src/components/ds-split-button/ds-split-button.tsx +6 -0
  125. package/src/components/ds-status-badge/ds-status-badge.tsx +2 -0
  126. package/src/components/ds-status-badge-v2/index.ts +2 -0
  127. package/src/components/ds-stepper/components/ds-step-content.tsx +2 -0
  128. package/src/components/ds-stepper/components/ds-step-next-button.tsx +2 -0
  129. package/src/components/ds-stepper/components/ds-step.tsx +2 -0
  130. package/src/components/ds-stepper/ds-stepper.figma.ts +38 -0
  131. package/src/components/ds-stepper/ds-stepper.tsx +1 -0
  132. package/src/components/ds-system-status/ds-system-status.tsx +2 -0
  133. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +1 -1
  134. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +2 -0
  135. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +1 -1
  136. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +2 -0
  137. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +1 -0
  138. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +1 -0
  139. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +1 -0
  140. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +1 -0
  141. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +1 -0
  142. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.module.scss +14 -2
  143. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +20 -0
  144. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +1 -0
  145. package/src/components/ds-table/ds-table-column.types.ts +24 -0
  146. package/src/components/ds-table/ds-table.tsx +30 -4
  147. package/src/components/ds-table/ds-table.types.ts +7 -0
  148. package/src/components/ds-table/index.ts +4 -0
  149. package/src/components/ds-table/utils/constants.ts +5 -0
  150. package/src/components/ds-tabs/components/ds-tabs-content/ds-tabs-content.tsx +2 -0
  151. package/src/components/ds-tabs/components/ds-tabs-list/ds-tabs-list.tsx +2 -0
  152. package/src/components/ds-tabs/components/ds-tabs-tab/ds-tabs-tab.tsx +2 -0
  153. package/src/components/ds-tabs/ds-tabs.tsx +2 -0
  154. package/src/components/ds-tabs/figma/ds-tabs-tab-vertical.figma.ts +78 -0
  155. package/src/components/ds-tabs/figma/ds-tabs-tab.figma.ts +89 -0
  156. package/src/components/ds-tabs/figma/ds-tabs-vertical.figma.ts +31 -0
  157. package/src/components/ds-tabs/figma/ds-tabs.figma.ts +29 -0
  158. package/src/components/ds-tag/ds-tag.tsx +2 -0
  159. package/src/components/ds-tag-filter/ds-tag-filter.tsx +2 -0
  160. package/src/components/ds-text-input/ds-input-field-v02.figma.ts +87 -0
  161. package/src/components/ds-text-input/ds-text-input.tsx +2 -0
  162. package/src/components/ds-textarea/ds-textarea.tsx +2 -0
  163. package/src/components/ds-time-picker/ds-time-picker.tsx +2 -0
  164. package/src/components/ds-toast/ds-toast-context.tsx +1 -0
  165. package/src/components/ds-toast/ds-toast.figma.ts +34 -0
  166. package/src/components/ds-toast/ds-toast.tsx +3 -0
  167. package/src/components/ds-toggle/ds-toggle.figma.ts +39 -0
  168. package/src/components/ds-toggle/ds-toggle.tsx +2 -0
  169. package/src/components/ds-tooltip/ds-tooltip.figma.ts +16 -0
  170. package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -1
  171. package/src/components/ds-tooltip/ds-tooltip.tsx +35 -15
  172. package/src/components/ds-tooltip/ds-tooltip.types.ts +40 -0
  173. package/src/components/ds-tree/ds-tree.tsx +16 -0
  174. package/src/components/ds-typography/ds-typography.config.ts +1 -1
  175. package/src/components/ds-typography/ds-typography.figma.ts +63 -0
  176. package/src/components/ds-typography/ds-typography.module.scss +16 -0
  177. package/src/components/ds-typography/ds-typography.tsx +64 -7
  178. package/src/components/ds-typography/ds-typography.types.ts +13 -0
  179. package/src/components/ds-typography/hooks/use-truncation.ts +151 -0
  180. package/src/components/ds-user-card/ds-user-card.figma.ts +16 -0
  181. package/src/components/ds-user-card/ds-user-card.module.scss +25 -0
  182. package/src/components/ds-user-card/ds-user-card.tsx +31 -0
  183. package/src/components/ds-user-card/ds-user-card.types.ts +26 -0
  184. package/src/components/ds-user-card/index.ts +2 -0
  185. package/src/components/ds-vertical-tabs/ds-vertical-tabs.tsx +6 -0
  186. package/src/components/ds-workspace-layout/ds-workspace-layout.tsx +10 -0
  187. package/src/index.ts +3 -0
  188. package/src/styles/_root.scss +468 -363
  189. package/src/styles/shared/_dropdown.scss +2 -2
  190. package/src/styles/styles.scss +0 -3
  191. package/src/styles/_colors.scss +0 -65
  192. package/src/styles/_root_new.scss +0 -451
  193. package/src/styles/_spacing.scss +0 -47
@@ -0,0 +1,39 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=15035-29699
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-toggle
3
+ // component=DsToggle
4
+ import figma from 'figma';
5
+
6
+ const instance = figma.selectedInstance;
7
+
8
+ const on = instance.getEnum('On', {
9
+ Yes: 'yes',
10
+ No: 'no',
11
+ });
12
+
13
+ const size = instance.getEnum('Size', {
14
+ Default: 'default',
15
+ Small: 'small',
16
+ });
17
+
18
+ const state = instance.getEnum('State', {
19
+ Default: 'default',
20
+ Hover: 'hover',
21
+ Focus: 'focus',
22
+ Disabled: 'disabled',
23
+ });
24
+
25
+ const attrs = [
26
+ 'label="Label"',
27
+ size === 'small' ? 'size="small"' : '',
28
+ on === 'yes' ? 'checked' : '',
29
+ state === 'disabled' ? 'disabled' : '',
30
+ ]
31
+ .filter(Boolean)
32
+ .join(' ');
33
+
34
+ export default {
35
+ example: figma.code`<DsToggle ${attrs} />`,
36
+ imports: ["import { DsToggle } from '@drivenets/design-system'"],
37
+ id: 'ds-toggle',
38
+ metadata: { nestable: true },
39
+ };
@@ -75,4 +75,6 @@ const DsToggle = ({
75
75
  );
76
76
  };
77
77
 
78
+ DsToggle.displayName = 'DsToggle';
79
+
78
80
  export default DsToggle;
@@ -0,0 +1,16 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=14845-9203
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-tooltip
3
+ // component=DsTooltip
4
+ import figma from 'figma';
5
+
6
+ const instance = figma.selectedInstance;
7
+
8
+ const labelNode = instance.findText('Tooltip', { traverseInstances: true });
9
+ const content = labelNode.type === 'TEXT' ? labelNode.textContent : 'Tooltip text';
10
+
11
+ export default {
12
+ example: figma.code`<DsTooltip content="${content}">{/* trigger element */}</DsTooltip>`,
13
+ imports: ["import { DsTooltip } from '@drivenets/design-system'"],
14
+ id: 'ds-tooltip',
15
+ metadata: { nestable: false },
16
+ };
@@ -1,4 +1,7 @@
1
+ @use '../../styles/typography';
2
+
1
3
  .tooltip {
4
+ --arrow-size: 7px;
2
5
  color: var(--font-on-action);
3
6
  background-color: var(--background-brand);
4
7
  padding: var(--xs) var(--sm);
@@ -8,6 +11,10 @@
8
11
  z-index: 9999;
9
12
  }
10
13
 
14
+ .text {
15
+ @include typography.body-xs-reg;
16
+ }
17
+
11
18
  .arrow {
12
- fill: var(--color-dap-gray-700);
19
+ --arrow-background: var(--color-dap-gray-700);
13
20
  }
@@ -1,33 +1,53 @@
1
1
  import { type FC, isValidElement } from 'react';
2
- import * as Tooltip from '@radix-ui/react-tooltip';
2
+ import { Tooltip } from '@ark-ui/react/tooltip';
3
+ import { Portal } from '@ark-ui/react/portal';
3
4
  import classNames from 'classnames';
4
5
  import styles from './ds-tooltip.module.scss';
5
6
  import type { DsTooltipProps } from './ds-tooltip.types';
6
- import { DsTypography } from '../ds-typography';
7
7
 
8
- const DsTooltip: FC<DsTooltipProps> = ({ content, children, slotProps }) => {
8
+ const OPEN_DELAY_MS = 200;
9
+ const CLOSE_DELAY_MS = 0;
10
+ const TOOLTIP_GUTTER_PX = 0;
11
+
12
+ const DsTooltip: FC<DsTooltipProps> = ({
13
+ content,
14
+ children,
15
+ placement = 'top',
16
+ disabled = false,
17
+ getAnchorRect,
18
+ slotProps,
19
+ }) => {
9
20
  if (content === undefined) {
10
21
  return children;
11
22
  }
23
+
12
24
  return (
13
- <Tooltip.Provider delayDuration={200}>
14
- <Tooltip.Root>
15
- <Tooltip.Trigger asChild>{children}</Tooltip.Trigger>
16
- <Tooltip.Portal>
25
+ <Tooltip.Root
26
+ openDelay={OPEN_DELAY_MS}
27
+ closeDelay={CLOSE_DELAY_MS}
28
+ disabled={disabled}
29
+ positioning={{ placement, gutter: TOOLTIP_GUTTER_PX, getAnchorRect: getAnchorRect ?? undefined }}
30
+ lazyMount
31
+ unmountOnExit
32
+ >
33
+ <Tooltip.Trigger asChild>{children}</Tooltip.Trigger>
34
+ <Portal>
35
+ <Tooltip.Positioner>
17
36
  <Tooltip.Content
18
37
  className={classNames(styles.tooltip, slotProps?.content?.className)}
19
38
  style={slotProps?.content?.style}
20
- side="top"
21
- align="center"
22
- sideOffset={4}
23
39
  >
24
- {isValidElement(content) ? content : <DsTypography variant="body-xs-reg">{content}</DsTypography>}
25
- <Tooltip.Arrow className={styles.arrow} />
40
+ {isValidElement(content) ? content : <span className={styles.text}>{content}</span>}
41
+ <Tooltip.Arrow className={styles.arrow}>
42
+ <Tooltip.ArrowTip />
43
+ </Tooltip.Arrow>
26
44
  </Tooltip.Content>
27
- </Tooltip.Portal>
28
- </Tooltip.Root>
29
- </Tooltip.Provider>
45
+ </Tooltip.Positioner>
46
+ </Portal>
47
+ </Tooltip.Root>
30
48
  );
31
49
  };
32
50
 
51
+ DsTooltip.displayName = 'DsTooltip';
52
+
33
53
  export default DsTooltip;
@@ -1,5 +1,32 @@
1
1
  import type { CSSProperties, ReactNode } from 'react';
2
2
 
3
+ export const tooltipPlacements = [
4
+ 'top',
5
+ 'top-start',
6
+ 'top-end',
7
+ 'bottom',
8
+ 'bottom-start',
9
+ 'bottom-end',
10
+ 'left',
11
+ 'left-start',
12
+ 'left-end',
13
+ 'right',
14
+ 'right-start',
15
+ 'right-end',
16
+ ] as const;
17
+
18
+ export type TooltipPlacement = (typeof tooltipPlacements)[number];
19
+
20
+ /**
21
+ * Viewport-relative rect (CSS pixels) the tooltip anchors to, instead of the trigger.
22
+ */
23
+ export interface TooltipAnchorRect {
24
+ x: number;
25
+ y: number;
26
+ width: number;
27
+ height: number;
28
+ }
29
+
3
30
  export interface DsTooltipProps {
4
31
  /**
5
32
  * Tooltip content
@@ -9,6 +36,19 @@ export interface DsTooltipProps {
9
36
  * The content to be rendered inside the tooltip
10
37
  */
11
38
  children: ReactNode;
39
+ /**
40
+ * Placement relative to the trigger; `-start` / `-end` align to that edge.
41
+ */
42
+ placement?: TooltipPlacement;
43
+ /**
44
+ * When true the trigger renders but the tooltip never opens.
45
+ */
46
+ disabled?: boolean;
47
+ /**
48
+ * Anchors the tooltip to a virtual rect instead of the trigger, re-evaluated on
49
+ * each reposition. Return `null` to fall back to the trigger.
50
+ */
51
+ getAnchorRect?: () => TooltipAnchorRect | null;
12
52
  /**
13
53
  * Props forwarded to nested sub-components.
14
54
  */
@@ -153,6 +153,22 @@ const DsTreeItemAction = ({ className, style, children, onClick }: DsTreeItemAct
153
153
  </button>
154
154
  );
155
155
 
156
+ DsTreeRoot.displayName = 'DsTree.Root';
157
+ DsTreeTree.displayName = 'DsTree.Tree';
158
+ Object.defineProperty(TreeView.NodeProvider, 'displayName', { value: 'DsTree.NodeProvider' });
159
+ Object.defineProperty(TreeView.NodeContext, 'displayName', { value: 'DsTree.NodeContext' });
160
+ DsTreeBranch.displayName = 'DsTree.Branch';
161
+ DsTreeBranchControl.displayName = 'DsTree.BranchControl';
162
+ DsTreeBranchIndicator.displayName = 'DsTree.BranchIndicator';
163
+ DsTreeBranchText.displayName = 'DsTree.BranchText';
164
+ DsTreeBranchContent.displayName = 'DsTree.BranchContent';
165
+ DsTreeBranchIndentGuide.displayName = 'DsTree.BranchIndentGuide';
166
+ DsTreeItem.displayName = 'DsTree.Item';
167
+ DsTreeItemText.displayName = 'DsTree.ItemText';
168
+ DsTreeItemIndicator.displayName = 'DsTree.ItemIndicator';
169
+ DsTreeItemAction.displayName = 'DsTree.ItemAction';
170
+ DsTreeNodeCheckbox.displayName = 'DsTree.NodeCheckbox';
171
+
156
172
  export const DsTree = {
157
173
  Root: DsTreeRoot,
158
174
  Tree: DsTreeTree,
@@ -30,7 +30,7 @@ export const typographyVariantConfig = {
30
30
  heading4: { element: 'h4', component: ark.h4 },
31
31
  } as const;
32
32
 
33
- /** Semantic text colors backed by `--font-*` tokens in `_root_new.scss`. */
33
+ /** Semantic text colors backed by `--font-*` tokens in `_root.scss`. */
34
34
  export const typographyColors = [
35
35
  'main',
36
36
  'secondary',
@@ -0,0 +1,63 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=39324-248
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-typography
3
+ // component=DsTypography
4
+ import figma from 'figma';
5
+
6
+ const instance = figma.selectedInstance;
7
+
8
+ const variant = instance.getEnum('font-style', {
9
+ heading1: 'heading1',
10
+ heading2: 'heading2',
11
+ heading3: 'heading3',
12
+ heading4: 'heading4',
13
+ 'body-md-reg': 'body-md-reg',
14
+ 'body-md-md': 'body-md-md',
15
+ 'body-md-semi-bold': 'body-md-semi-bold',
16
+ 'body-md-bold': 'body-md-bold',
17
+ 'body-md-link': 'body-md-link',
18
+ 'body-sm-reg': 'body-sm-reg',
19
+ 'body-sm-md': 'body-sm-md',
20
+ 'body-sm-semi-bold': 'body-sm-semi-bold',
21
+ 'body-sm-bold': 'body-sm-bold',
22
+ 'body-sm-link': 'body-sm-link',
23
+ 'body-xs-reg': 'body-xs-reg',
24
+ 'body-xs-md': 'body-xs-md',
25
+ 'body-xs-semi-bold': 'body-xs-semi-bold',
26
+ 'body-xs-bold': 'body-xs-bold',
27
+ 'body-xs-link': 'body-xs-link',
28
+ 'code-sm-reg': 'code-sm-reg',
29
+ 'code-sm-semi-bold': 'code-sm-semi-bold',
30
+ 'code-xs-reg': 'code-xs-reg',
31
+ 'code-xs-semi-bold': 'code-xs-semi-bold',
32
+ });
33
+
34
+ const color = instance.getEnum('color', {
35
+ main: 'main',
36
+ secondary: 'secondary',
37
+ action: 'action',
38
+ 'action-hover': 'action-hover',
39
+ 'action-secondary': 'action-secondary',
40
+ 'action-secondary-hover': 'action-secondary-hover',
41
+ disabled: 'disabled',
42
+ 'light-disabled': 'light-disabled',
43
+ 'on-action': 'on-action',
44
+ 'on-disabled': 'on-disabled',
45
+ placeholder: 'placeholder',
46
+ highlight: 'highlight',
47
+ success: 'success',
48
+ warning: 'warning',
49
+ error: 'error',
50
+ code: 'code',
51
+ });
52
+
53
+ const textNode = instance.children.find((child): child is figma.TextHandle => child.type === 'TEXT');
54
+ const text = textNode?.textContent ?? 'Text';
55
+
56
+ const colorProp = color === 'main' ? '' : figma.code` color="${color}"`;
57
+
58
+ export default {
59
+ example: figma.code`<DsTypography variant="${variant}"${colorProp}>${text}</DsTypography>`,
60
+ imports: ["import { DsTypography } from '@drivenets/design-system'"],
61
+ id: 'ds-typography',
62
+ metadata: { nestable: true },
63
+ };
@@ -1,5 +1,21 @@
1
1
  @use '../../styles/typography';
2
2
 
3
+ .truncateSingle {
4
+ display: block;
5
+ max-width: 100%;
6
+ overflow: hidden;
7
+ white-space: nowrap;
8
+ text-overflow: ellipsis;
9
+ }
10
+
11
+ .truncateMultiline {
12
+ display: -webkit-box;
13
+ -webkit-box-orient: vertical;
14
+ -webkit-line-clamp: var(--ds-typography-line-clamp);
15
+ line-clamp: var(--ds-typography-line-clamp);
16
+ overflow: hidden;
17
+ }
18
+
3
19
  .heading1 {
4
20
  @include typography.heading1;
5
21
  }
@@ -1,6 +1,10 @@
1
+ import { type CSSProperties, useRef } from 'react';
1
2
  import classNames from 'classnames';
2
3
  import type { DsTypographyProps } from './ds-typography.types';
3
4
  import { typographyColors, typographyVariantConfig } from './ds-typography.config';
5
+ import { useTruncation } from './hooks/use-truncation';
6
+ import { mergeRefs } from '../../utils/merge-refs';
7
+ import { DsTooltip } from '../ds-tooltip';
4
8
  import styles from './ds-typography.module.scss';
5
9
 
6
10
  const semanticColorSet = new Set<string>(typographyColors);
@@ -8,6 +12,9 @@ const semanticColorSet = new Set<string>(typographyColors);
8
12
  const resolveColor = (color: string): string =>
9
13
  semanticColorSet.has(color) ? `var(--font-${color})` : color;
10
14
 
15
+ const resolveLineClamp = (truncate: boolean | number): number =>
16
+ typeof truncate === 'number' ? Math.max(1, Math.floor(truncate)) : 1;
17
+
11
18
  /*
12
19
  * Design system Typography component that provides consistent text styling
13
20
  */
@@ -18,25 +25,75 @@ const DsTypography = ({
18
25
  className,
19
26
  style,
20
27
  color,
28
+ truncate = false,
29
+ tooltip = false,
30
+ tooltipContent,
21
31
  children,
22
32
  ...props
23
33
  }: DsTypographyProps) => {
24
34
  const Component = typographyVariantConfig[variant].component;
25
35
 
26
- return (
36
+ const internalRef = useRef<HTMLElement>(null);
37
+
38
+ const truncationEnabled = truncate !== false;
39
+ const lineClamp = resolveLineClamp(truncate);
40
+ const isMultiline = truncationEnabled && lineClamp > 1;
41
+
42
+ const tooltipEnabled = truncationEnabled && tooltip;
43
+ const { isTruncated, getAnchorRect } = useTruncation(internalRef, tooltipEnabled, [children, truncate]);
44
+
45
+ let resolvedStyle: CSSProperties | undefined;
46
+
47
+ if (color || isMultiline || style) {
48
+ resolvedStyle = {};
49
+
50
+ if (color) {
51
+ resolvedStyle.color = resolveColor(color);
52
+ }
53
+
54
+ if (isMultiline) {
55
+ Object.assign(resolvedStyle, { '--ds-typography-line-clamp': lineClamp });
56
+ }
57
+
58
+ if (style) {
59
+ Object.assign(resolvedStyle, style);
60
+ }
61
+ }
62
+
63
+ const truncateClass = truncationEnabled
64
+ ? isMultiline
65
+ ? styles.truncateMultiline
66
+ : styles.truncateSingle
67
+ : undefined;
68
+
69
+ const element = (
27
70
  <Component
28
- // very dynamic ref in here
29
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
30
- // @ts-expect-error
31
- ref={ref}
71
+ ref={mergeRefs(ref, internalRef)}
32
72
  asChild={asChild}
33
- className={classNames(styles[variant], className)}
34
- style={color ? { color: resolveColor(color), ...style } : style}
73
+ className={classNames(styles[variant], truncateClass, className)}
74
+ style={resolvedStyle}
35
75
  {...props}
36
76
  >
37
77
  {children}
38
78
  </Component>
39
79
  );
80
+
81
+ if (!tooltipEnabled) {
82
+ return element;
83
+ }
84
+
85
+ return (
86
+ <DsTooltip
87
+ content={tooltipContent ?? children}
88
+ placement="top"
89
+ disabled={!isTruncated}
90
+ getAnchorRect={getAnchorRect}
91
+ >
92
+ {element}
93
+ </DsTooltip>
94
+ );
40
95
  };
41
96
 
97
+ DsTypography.displayName = 'DsTypography';
98
+
42
99
  export default DsTypography;
@@ -31,6 +31,19 @@ export interface DsTypographyProps {
31
31
  * When true, composes typography styles onto the child element without extra DOM nodes
32
32
  */
33
33
  asChild?: boolean;
34
+ /**
35
+ * Truncates overflowing text with an ellipsis. A number clamps to that many lines
36
+ * (`true` is a single line). Needs a width-constrained layout context.
37
+ */
38
+ truncate?: boolean | number;
39
+ /**
40
+ * Reveals the full text in a tooltip on hover/focus, but only while it overflows.
41
+ */
42
+ tooltip?: boolean;
43
+ /**
44
+ * Tooltip content override; defaults to `children`.
45
+ */
46
+ tooltipContent?: React.ReactNode;
34
47
  /**
35
48
  * The content of the component
36
49
  */
@@ -0,0 +1,151 @@
1
+ import { type RefObject, useCallback, useLayoutEffect, useState } from 'react';
2
+ import type { TooltipAnchorRect } from '../../ds-tooltip';
3
+
4
+ const OVERFLOW_TOLERANCE_PX = 1;
5
+ const FALLBACK_LINE_HEIGHT_RATIO = 1.2;
6
+ const DEFAULT_FONT_SIZE_PX = 16;
7
+ const ELLIPSIS = '\u2026';
8
+
9
+ interface Truncation {
10
+ isTruncated: boolean;
11
+ getAnchorRect: () => TooltipAnchorRect | null;
12
+ }
13
+
14
+ const ellipsisWidthCache = new Map<string, number>();
15
+ let ellipsisMeasureSpan: HTMLSpanElement | null = null;
16
+
17
+ const measureEllipsisWidth = (computed: CSSStyleDeclaration): number => {
18
+ const cacheKey = `${computed.fontStyle}|${computed.fontWeight}|${computed.fontSize}|${computed.fontFamily}|${computed.letterSpacing}`;
19
+ const cached = ellipsisWidthCache.get(cacheKey);
20
+
21
+ if (cached !== undefined) {
22
+ return cached;
23
+ }
24
+
25
+ ellipsisMeasureSpan ??= document.createElement('span');
26
+ const span = ellipsisMeasureSpan;
27
+
28
+ span.textContent = ELLIPSIS;
29
+ span.style.position = 'absolute';
30
+ span.style.visibility = 'hidden';
31
+ span.style.whiteSpace = 'pre';
32
+ span.style.fontStyle = computed.fontStyle;
33
+ span.style.fontWeight = computed.fontWeight;
34
+ span.style.fontSize = computed.fontSize;
35
+ span.style.fontFamily = computed.fontFamily;
36
+ span.style.letterSpacing = computed.letterSpacing;
37
+
38
+ document.body.appendChild(span);
39
+ const width = span.getBoundingClientRect().width;
40
+ span.remove();
41
+
42
+ ellipsisWidthCache.set(cacheKey, width);
43
+ return width;
44
+ };
45
+
46
+ let measureRange: Range | null = null;
47
+
48
+ const measureFittingPrefixRight = (textNode: Text, maxRight: number): number => {
49
+ measureRange ??= document.createRange();
50
+ const range = measureRange;
51
+
52
+ let low = 0;
53
+ let high = textNode.length;
54
+ let fittingLength = 0;
55
+
56
+ while (low <= high) {
57
+ const mid = (low + high) >>> 1;
58
+ range.setStart(textNode, 0);
59
+ range.setEnd(textNode, mid);
60
+
61
+ if (range.getBoundingClientRect().right <= maxRight) {
62
+ fittingLength = mid;
63
+ low = mid + 1;
64
+ } else {
65
+ high = mid - 1;
66
+ }
67
+ }
68
+
69
+ range.setStart(textNode, 0);
70
+ range.setEnd(textNode, fittingLength);
71
+ return range.getBoundingClientRect().right;
72
+ };
73
+
74
+ const getFirstTextNode = (element: HTMLElement): Text | null =>
75
+ element.firstChild instanceof Text ? element.firstChild : null;
76
+
77
+ export const useTruncation = (
78
+ ref: RefObject<HTMLElement | null>,
79
+ enabled: boolean,
80
+ deps: readonly unknown[] = [],
81
+ ): Truncation => {
82
+ const [isTruncated, setIsTruncated] = useState(false);
83
+
84
+ useLayoutEffect(() => {
85
+ const element = ref.current;
86
+
87
+ if (!enabled || !element) {
88
+ setIsTruncated(false);
89
+ return;
90
+ }
91
+
92
+ const measure = () => {
93
+ const overflowsHorizontally = element.scrollWidth - element.clientWidth > OVERFLOW_TOLERANCE_PX;
94
+ const overflowsVertically = element.scrollHeight - element.clientHeight > OVERFLOW_TOLERANCE_PX;
95
+
96
+ setIsTruncated(overflowsHorizontally || overflowsVertically);
97
+ };
98
+
99
+ const rafId = requestAnimationFrame(measure);
100
+
101
+ const resizeObserver = new ResizeObserver(() => requestAnimationFrame(measure));
102
+ resizeObserver.observe(element);
103
+
104
+ return () => {
105
+ cancelAnimationFrame(rafId);
106
+ resizeObserver.disconnect();
107
+ };
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ }, [ref, enabled, ...deps]);
110
+
111
+ const getAnchorRect = useCallback((): TooltipAnchorRect | null => {
112
+ const element = ref.current;
113
+
114
+ if (!element) {
115
+ return null;
116
+ }
117
+
118
+ const rect = element.getBoundingClientRect();
119
+ const computed = getComputedStyle(element);
120
+
121
+ const paddingRight = parseFloat(computed.paddingRight) || 0;
122
+ const borderRight = parseFloat(computed.borderRightWidth) || 0;
123
+ const paddingBottom = parseFloat(computed.paddingBottom) || 0;
124
+ const borderBottom = parseFloat(computed.borderBottomWidth) || 0;
125
+
126
+ const fontSize = parseFloat(computed.fontSize) || DEFAULT_FONT_SIZE_PX;
127
+ const lineHeight = parseFloat(computed.lineHeight) || fontSize * FALLBACK_LINE_HEIGHT_RATIO;
128
+
129
+ const contentRight = rect.right - paddingRight - borderRight;
130
+ const ellipsisWidth = measureEllipsisWidth(computed);
131
+
132
+ const textNode = getFirstTextNode(element);
133
+ const isSingleLine = computed.whiteSpace === 'nowrap';
134
+
135
+ const ellipsisCenterX =
136
+ isSingleLine && textNode
137
+ ? measureFittingPrefixRight(textNode, contentRight - ellipsisWidth) + ellipsisWidth / 2
138
+ : contentRight - ellipsisWidth / 2;
139
+
140
+ const lastLineBottom = rect.bottom - paddingBottom - borderBottom;
141
+
142
+ return {
143
+ x: ellipsisCenterX,
144
+ y: lastLineBottom - lineHeight,
145
+ width: 0,
146
+ height: lineHeight,
147
+ };
148
+ }, [ref]);
149
+
150
+ return { isTruncated, getAnchorRect };
151
+ };
@@ -0,0 +1,16 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=38540-44159
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-user-card
3
+ // component=DsUserCard
4
+ //
5
+ // `DAP_user-menu-header_v01` (#38540:44159) is a plain composed instance with no
6
+ // variant props, so it maps directly to `DsUserCard`. `name` and `subtitle` are text
7
+ // content and `src` is a photo URL — none are derivable from Figma — so they are
8
+ // emitted as placeholders for the developer to fill in (see `ds-select.figma.ts`).
9
+ import figma from 'figma';
10
+
11
+ export default {
12
+ example: figma.code`<DsUserCard name="Full Name" subtitle="subtitle" />`,
13
+ imports: ["import { DsUserCard } from '@drivenets/design-system'"],
14
+ id: 'ds-user-card',
15
+ metadata: { nestable: true },
16
+ };
@@ -0,0 +1,25 @@
1
+ .userCard {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: var(--xs);
5
+ padding: var(--sm);
6
+ border-bottom: 1px solid var(--border-secondary);
7
+ }
8
+
9
+ .text {
10
+ display: flex;
11
+ flex-direction: column;
12
+ flex: 1;
13
+ gap: var(--3xs);
14
+ padding-top: var(--4xs);
15
+ min-width: 0;
16
+ }
17
+
18
+ .name,
19
+ .subtitle {
20
+ display: block;
21
+ max-width: 100%;
22
+ overflow: hidden;
23
+ white-space: nowrap;
24
+ text-overflow: ellipsis;
25
+ }
@@ -0,0 +1,31 @@
1
+ import type { FC } from 'react';
2
+ import classNames from 'classnames';
3
+ import styles from './ds-user-card.module.scss';
4
+ import type { DsUserCardProps } from './ds-user-card.types';
5
+ import { DsAvatar } from '../ds-avatar';
6
+ import { DsTypography } from '../ds-typography';
7
+
8
+ /**
9
+ * @summary Compact identity header pairing an avatar with a name and optional subtitle.
10
+ *
11
+ * Presentational card used as the header of a user menu. Renders a medium
12
+ * `DsAvatar` (initials fallback derived from `name`) beside a two-line text
13
+ * block; both lines truncate with an ellipsis when they overflow.
14
+ */
15
+ export const DsUserCard: FC<DsUserCardProps> = ({ name, subtitle, src, className, style }) => {
16
+ return (
17
+ <div className={classNames(styles.userCard, className)} style={style}>
18
+ <DsAvatar name={name} src={src} size="md" />
19
+ <div className={styles.text}>
20
+ <DsTypography variant="heading4" color="main" className={styles.name}>
21
+ {name}
22
+ </DsTypography>
23
+ {subtitle && (
24
+ <DsTypography variant="body-xs-reg" color="secondary" className={styles.subtitle}>
25
+ {subtitle}
26
+ </DsTypography>
27
+ )}
28
+ </div>
29
+ </div>
30
+ );
31
+ };