@drivenets/design-system 0.13.0 → 0.15.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 (84) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/dist/index.cjs +1155 -381
  4. package/dist/index.d.cts +371 -21
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.ts +371 -21
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1151 -381
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.min.css +1 -1
  11. package/package.json +16 -13
  12. package/src/components/ds-button-v3/ds-button-v3.figma.ts +99 -0
  13. package/src/components/ds-button-v3/ds-button-v3.module.scss +22 -22
  14. package/src/components/ds-card/figma/ds-card-content.figma.ts +15 -0
  15. package/src/components/ds-card/figma/ds-card-footer.figma.ts +15 -0
  16. package/src/components/ds-card/figma/ds-card-header.figma.ts +15 -0
  17. package/src/components/ds-card/figma/ds-card.figma.ts +72 -0
  18. package/src/components/ds-date-picker/ds-date-picker.figma.ts +19 -0
  19. package/src/components/ds-drawer/ds-drawer.module.scss +14 -0
  20. package/src/components/ds-drawer/ds-drawer.tsx +20 -1
  21. package/src/components/ds-drawer/ds-drawer.types.ts +7 -0
  22. package/src/components/ds-file-upload/hooks/use-file-upload.ts +7 -0
  23. package/src/components/ds-form-control/ds-form-control.figma.ts +76 -0
  24. package/src/components/ds-icon/figma/ds-icon-material.figma.batch.ts +10 -0
  25. package/src/components/ds-icon/figma/ds-icon-special.figma.batch.ts +18 -0
  26. package/src/components/ds-icon/figma/ds-icon.figma.batch.json +2873 -0
  27. package/src/components/ds-modal/figma/ds-modal-content.figma.ts +18 -0
  28. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +19 -0
  29. package/src/components/ds-modal/figma/ds-modal-header.figma.ts +21 -0
  30. package/src/components/ds-modal/figma/ds-modal.figma.ts +36 -0
  31. package/src/components/ds-popover/ds-popover.module.scss +58 -16
  32. package/src/components/ds-popover/ds-popover.tsx +148 -24
  33. package/src/components/ds-popover/ds-popover.types.ts +95 -12
  34. package/src/components/ds-popover/index.ts +1 -1
  35. package/src/components/ds-progress-task-bar/ds-progress-task-bar.figma.ts +29 -0
  36. package/src/components/ds-progress-task-bar/ds-progress-task-bar.module.scss +39 -0
  37. package/src/components/ds-progress-task-bar/ds-progress-task-bar.tsx +122 -0
  38. package/src/components/ds-progress-task-bar/ds-progress-task-bar.types.ts +65 -0
  39. package/src/components/ds-progress-task-bar/index.ts +2 -0
  40. package/src/components/ds-progress-task-bar/utils.ts +51 -0
  41. package/src/components/ds-select/ds-select.figma.ts +53 -0
  42. package/src/components/ds-split-button/ds-split-button.figma.ts +95 -0
  43. package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +41 -0
  44. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +8 -3
  45. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +31 -0
  46. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +48 -0
  47. package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.types.ts +12 -0
  48. package/src/components/ds-table/components/ds-table-group-header-cell/index.ts +2 -0
  49. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +4 -0
  50. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +55 -39
  51. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +5 -1
  52. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +4 -0
  53. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +5 -1
  54. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +4 -0
  55. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.module.scss +14 -2
  56. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +19 -0
  57. package/src/components/ds-table/context/ds-table-context.ts +4 -0
  58. package/src/components/ds-table/ds-table-column.types.ts +24 -0
  59. package/src/components/ds-table/ds-table.tsx +48 -6
  60. package/src/components/ds-table/ds-table.types.ts +62 -0
  61. package/src/components/ds-table/figma/ds-table-expandable.figma.ts +17 -0
  62. package/src/components/ds-table/figma/ds-table.figma.ts +16 -0
  63. package/src/components/ds-table/grouping/hooks/use-column-groups.ts +73 -0
  64. package/src/components/ds-table/grouping/index.ts +5 -0
  65. package/src/components/ds-table/grouping/types/column-group-meta.augmentation.ts +26 -0
  66. package/src/components/ds-table/grouping/types/column-group.types.ts +35 -0
  67. package/src/components/ds-table/grouping/utils/column-group.ts +119 -0
  68. package/src/components/ds-table/index.ts +4 -0
  69. package/src/components/ds-table/styles/_variables.scss +1 -0
  70. package/src/components/ds-table/utils/constants.ts +5 -0
  71. package/src/components/ds-tabs/figma/ds-tabs-tab-vertical.figma.ts +78 -0
  72. package/src/components/ds-tabs/figma/ds-tabs-tab.figma.ts +89 -0
  73. package/src/components/ds-tabs/figma/ds-tabs-vertical.figma.ts +31 -0
  74. package/src/components/ds-tabs/figma/ds-tabs.figma.ts +29 -0
  75. package/src/components/ds-tag/ds-tag.module.scss +22 -1
  76. package/src/components/ds-tag/ds-tag.tsx +16 -3
  77. package/src/components/ds-tag/ds-tag.types.ts +22 -8
  78. package/src/components/ds-tag/ds-tag.unit.test.ts +32 -0
  79. package/src/components/ds-tag-filter/ds-tag-filter.tsx +14 -22
  80. package/src/components/ds-text-input/ds-text-input.figma.ts +60 -0
  81. package/src/components/ds-time-picker/ds-time-picker.figma.ts +18 -0
  82. package/src/index.ts +1 -0
  83. package/src/styles/_root_new.scss +2 -0
  84. package/src/styles/_scrollbars.scss +27 -50
@@ -0,0 +1,18 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=34877-158343
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-modal
3
+ // component=DsModal.Body
4
+ //
5
+ // `DAP_modal-content_v02` maps to `DsModal.Body`. Its freeform `Modal content slot`
6
+ // becomes the body children.
7
+ import figma from 'figma';
8
+
9
+ const instance = figma.selectedInstance;
10
+
11
+ const content = instance.getSlot('Modal content slot');
12
+
13
+ export default {
14
+ example: figma.code`<DsModal.Body>${content}</DsModal.Body>`,
15
+ imports: ["import { DsModal } from '@drivenets/design-system'"],
16
+ id: 'ds-modal-content',
17
+ metadata: { nestable: true },
18
+ };
@@ -0,0 +1,19 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=32014-16402
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-modal
3
+ // component=DsModal.Footer
4
+ //
5
+ // `DAP_modal-footer_v02` maps to `DsModal.Footer` > `DsModal.Actions`. The
6
+ // `right side buttons` slot resolves its `DAP_Button_v03` instances via the
7
+ // existing `ds-button-v3` Code Connect.
8
+ import figma from 'figma';
9
+
10
+ const instance = figma.selectedInstance;
11
+
12
+ const rightButtons = instance.getSlot('right side buttons');
13
+
14
+ export default {
15
+ example: figma.code`<DsModal.Footer><DsModal.Actions>${rightButtons}</DsModal.Actions></DsModal.Footer>`,
16
+ imports: ["import { DsModal } from '@drivenets/design-system'"],
17
+ id: 'ds-modal-footer',
18
+ metadata: { nestable: true },
19
+ };
@@ -0,0 +1,21 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=32030-12809
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-modal
3
+ // component=DsModal.Header
4
+ //
5
+ // `DAP_modal-header_v02` maps to the composed `DsModal.Header` (title + close
6
+ // trigger). The `Type=drill-down` variant (back breadcrumb) and the optional
7
+ // booleans (status badge, description, tags, etc.) have no code-component prop and
8
+ // are omitted.
9
+ import figma from 'figma';
10
+
11
+ const instance = figma.selectedInstance;
12
+
13
+ const titleNode = instance.findText('Left side', { traverseInstances: true });
14
+ const title = titleNode.type === 'TEXT' ? titleNode.textContent : 'Modal title';
15
+
16
+ export default {
17
+ example: figma.code`<DsModal.Header><DsModal.Title>${title}</DsModal.Title><DsModal.CloseTrigger /></DsModal.Header>`,
18
+ imports: ["import { DsModal } from '@drivenets/design-system'"],
19
+ id: 'ds-modal-header',
20
+ metadata: { nestable: true },
21
+ };
@@ -0,0 +1,36 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=34877-158966
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-modal
3
+ // component=DsModal
4
+ //
5
+ // `DAP_modal_v02` is the full modal. It composes the header, content, and footer
6
+ // parts, each of which has its own Code Connect template; resolve them dynamically
7
+ // so swapped variants stay in sync. The `Type` variant (confirmation/form) has no
8
+ // `DsModal` prop equivalent — both render the same composition.
9
+ import figma from 'figma';
10
+
11
+ const instance = figma.selectedInstance;
12
+
13
+ const header = instance.findInstance('DAP_modal-header_v02', { traverseInstances: true });
14
+ let headerCode: figma.ResultSection[] | undefined;
15
+ if (header.type === 'INSTANCE') {
16
+ headerCode = header.executeTemplate().example;
17
+ }
18
+
19
+ const content = instance.findInstance('DAP_modal-content_v02', { traverseInstances: true });
20
+ let contentCode: figma.ResultSection[] | undefined;
21
+ if (content.type === 'INSTANCE') {
22
+ contentCode = content.executeTemplate().example;
23
+ }
24
+
25
+ const footer = instance.findInstance('DAP_modal-footer_v02', { traverseInstances: true });
26
+ let footerCode: figma.ResultSection[] | undefined;
27
+ if (footer.type === 'INSTANCE') {
28
+ footerCode = footer.executeTemplate().example;
29
+ }
30
+
31
+ export default {
32
+ example: figma.code`<DsModal open columns={6} onOpenChange={() => {}}>${headerCode}${contentCode}${footerCode}</DsModal>`,
33
+ imports: ["import { DsModal } from '@drivenets/design-system'"],
34
+ id: 'ds-modal',
35
+ metadata: { nestable: false },
36
+ };
@@ -1,23 +1,65 @@
1
- @use '../../styles/typography';
1
+ .panel {
2
+ z-index: 1000;
3
+ display: flex;
4
+ flex-direction: column;
5
+ background: var(--background);
6
+ border-radius: var(--3xs);
7
+ // Figma "Elevation 1" — only the alpha is tokenized; offset/blur stay component-scoped.
8
+ box-shadow: 0 5px 15px 0 var(--shadow-moderate);
9
+ outline: 0;
10
+ transform-origin: var(--transform-origin);
11
+
12
+ // `display: flex` above outranks the UA `[hidden]` rule, so the closed panel
13
+ // would stay on screen. Re-assert `display: none` for Ark's hidden state.
14
+ &[hidden] {
15
+ display: none;
16
+ }
17
+
18
+ &[data-state='open'] {
19
+ animation: ds-popover-in 0.2s ease;
20
+ }
21
+ }
22
+
23
+ .header {
24
+ display: flex;
25
+ align-items: center;
26
+ gap: var(--xs);
27
+ padding: var(--sm) var(--lg);
28
+ border-bottom: 1px solid var(--border);
29
+ }
30
+
31
+ .headerIcon {
32
+ display: inline-flex;
33
+ flex-shrink: 0;
34
+ }
35
+
36
+ .title {
37
+ // Typography (heading3) + color come from the DsTypography it composes onto.
38
+ margin: 0;
39
+ }
2
40
 
3
41
  .content {
4
- width: max-content;
5
- // approx. 2 columns (64 * 2)
6
- min-width: 128px;
7
- // approx. 6 columns (64 * 6)
8
- max-width: 384px;
9
42
  display: flex;
10
43
  flex-direction: column;
11
- gap: 12px;
12
- box-shadow: 0 1.25px 4px 0 rgba(0, 0, 0, 0.25);
13
- border-radius: 2px;
14
- background: var(--color-dap-gray-050);
15
- padding: 12px;
44
+ gap: var(--xs);
45
+ padding: var(--standard) var(--lg);
16
46
  }
17
47
 
18
- .arrow {
19
- fill: var(--color-dap-gray-050);
20
- filter: drop-shadow(0 3px 2px rgba(0, 0, 0, 0.1));
21
- width: 18px;
22
- height: 10px;
48
+ .footer {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: var(--xs);
52
+ padding: var(--sm) var(--lg);
53
+ }
54
+
55
+ @keyframes ds-popover-in {
56
+ from {
57
+ opacity: 0;
58
+ transform: scale(0.96);
59
+ }
60
+
61
+ to {
62
+ opacity: 1;
63
+ transform: scale(1);
64
+ }
23
65
  }
@@ -1,33 +1,157 @@
1
- import type React from 'react';
2
- import * as Popover from '@radix-ui/react-popover';
1
+ import { Popover } from '@ark-ui/react/popover';
2
+ import { Portal } from '@ark-ui/react/portal';
3
3
  import classNames from 'classnames';
4
+ import { DsStack } from '../ds-stack';
5
+ import { DsTypography } from '../ds-typography';
4
6
  import styles from './ds-popover.module.scss';
5
- import type { DsPopoverProps } from './ds-popover.types';
7
+ import type {
8
+ DsPopoverAlign,
9
+ DsPopoverContentItemProps,
10
+ DsPopoverContentProps,
11
+ DsPopoverFooterProps,
12
+ DsPopoverHeaderProps,
13
+ DsPopoverPanelProps,
14
+ DsPopoverProps,
15
+ DsPopoverRootProps,
16
+ DsPopoverSide,
17
+ DsPopoverTriggerProps,
18
+ } from './ds-popover.types';
6
19
 
7
- const DsPopover: React.FC<DsPopoverProps> = ({
8
- trigger,
20
+ const DEFAULT_PANEL_WIDTH = 400;
21
+
22
+ export const toPlacement = (side: DsPopoverSide, align: DsPopoverAlign) =>
23
+ align === 'center' ? side : (`${side}-${align}` as const);
24
+
25
+ const DsPopoverRoot = ({
26
+ open,
27
+ defaultOpen,
28
+ side = 'bottom',
29
+ align = 'center',
30
+ gutter = 8,
31
+ modal = false,
9
32
  children,
33
+ onOpenChange,
34
+ }: DsPopoverRootProps) => (
35
+ <Popover.Root
36
+ open={open}
37
+ defaultOpen={defaultOpen}
38
+ modal={modal}
39
+ positioning={{ placement: toPlacement(side, align), gutter }}
40
+ onOpenChange={(details) => onOpenChange?.(details.open)}
41
+ >
42
+ {children}
43
+ </Popover.Root>
44
+ );
45
+
46
+ const DsPopoverTrigger = ({ children, className }: DsPopoverTriggerProps) => (
47
+ <Popover.Trigger asChild className={className}>
48
+ {children}
49
+ </Popover.Trigger>
50
+ );
51
+
52
+ const DsPopoverPanel = ({
53
+ width = DEFAULT_PANEL_WIDTH,
10
54
  className,
11
- align = 'center',
12
- side = 'top',
13
- }) => {
55
+ style,
56
+ children,
57
+ ref,
58
+ 'aria-label': ariaLabel,
59
+ }: DsPopoverPanelProps) => (
60
+ <Portal>
61
+ <Popover.Positioner>
62
+ <Popover.Content
63
+ ref={ref}
64
+ aria-label={ariaLabel}
65
+ className={classNames(styles.panel, className)}
66
+ style={{ ...style, width }}
67
+ >
68
+ {children}
69
+ </Popover.Content>
70
+ </Popover.Positioner>
71
+ </Portal>
72
+ );
73
+
74
+ const DsPopoverHeader = ({ icon, className, style, children }: DsPopoverHeaderProps) => (
75
+ <div className={classNames(styles.header, className)} style={style}>
76
+ {icon && <span className={styles.headerIcon}>{icon}</span>}
77
+ <Popover.Title className={styles.title} asChild>
78
+ <DsTypography variant="heading3" color="main">
79
+ {children}
80
+ </DsTypography>
81
+ </Popover.Title>
82
+ </div>
83
+ );
84
+
85
+ const DsPopoverContent = ({ className, style, children }: DsPopoverContentProps) => (
86
+ <div className={classNames(styles.content, className)} style={style}>
87
+ {children}
88
+ </div>
89
+ );
90
+
91
+ const DsPopoverContentItem = ({
92
+ status,
93
+ headline,
94
+ className,
95
+ style,
96
+ children,
97
+ }: DsPopoverContentItemProps) => {
98
+ const hasHeader = Boolean(status) || Boolean(headline);
99
+
14
100
  return (
15
- <Popover.Root>
16
- <Popover.Trigger asChild>{trigger}</Popover.Trigger>
17
- <Popover.Portal>
18
- <Popover.Content
19
- side={side}
20
- align={align}
21
- sideOffset={8}
22
- collisionPadding={16}
23
- className={classNames(styles.content, className)}
24
- >
25
- {children}
26
- <Popover.Arrow className={styles.arrow} />
27
- </Popover.Content>
28
- </Popover.Portal>
29
- </Popover.Root>
101
+ <DsStack direction="column" gap="var(--xs)" className={className} style={style}>
102
+ {hasHeader && (
103
+ <DsStack direction="row" alignItems="center" gap="var(--xs)">
104
+ {status}
105
+ {headline && (
106
+ <DsTypography variant="body-sm-semi-bold" color="main">
107
+ {headline}
108
+ </DsTypography>
109
+ )}
110
+ </DsStack>
111
+ )}
112
+ {children && (
113
+ <DsTypography variant="body-sm-reg" color="main" asChild>
114
+ <DsStack direction="row" alignItems="center" gap="var(--xs)">
115
+ {children}
116
+ </DsStack>
117
+ </DsTypography>
118
+ )}
119
+ </DsStack>
30
120
  );
31
121
  };
32
122
 
33
- export default DsPopover;
123
+ const DsPopoverFooter = ({ className, style, children }: DsPopoverFooterProps) => (
124
+ <div className={classNames(styles.footer, className)} style={style}>
125
+ {children}
126
+ </div>
127
+ );
128
+
129
+ /**
130
+ * Legacy single-element form. Keeps the old call signature and `side` default
131
+ * ('top') compiling and opening, but now renders the new fixed-width panel —
132
+ * styling differs from the old Radix popover and the Radix `Popover.Close`
133
+ * context is no longer wired.
134
+ * @deprecated Migrate to the compound API (`DsPopover.Root` / `.Trigger` / `.Panel`).
135
+ */
136
+ const DsPopoverLegacy = ({
137
+ trigger,
138
+ children,
139
+ className,
140
+ side = 'top',
141
+ align = 'center',
142
+ }: DsPopoverProps) => (
143
+ <DsPopoverRoot side={side} align={align}>
144
+ <DsPopoverTrigger>{trigger}</DsPopoverTrigger>
145
+ <DsPopoverPanel className={className}>{children}</DsPopoverPanel>
146
+ </DsPopoverRoot>
147
+ );
148
+
149
+ export const DsPopover = Object.assign(DsPopoverLegacy, {
150
+ Root: DsPopoverRoot,
151
+ Trigger: DsPopoverTrigger,
152
+ Panel: DsPopoverPanel,
153
+ Header: DsPopoverHeader,
154
+ Content: DsPopoverContent,
155
+ ContentItem: DsPopoverContentItem,
156
+ Footer: DsPopoverFooter,
157
+ });
@@ -1,24 +1,107 @@
1
- import type React from 'react';
1
+ import type { CSSProperties, ReactNode, Ref } from 'react';
2
2
 
3
- export interface DsPopoverProps {
3
+ export const popoverSides = ['top', 'right', 'bottom', 'left'] as const;
4
+ export type DsPopoverSide = (typeof popoverSides)[number];
5
+
6
+ export const popoverAligns = ['start', 'center', 'end'] as const;
7
+ export type DsPopoverAlign = (typeof popoverAligns)[number];
8
+
9
+ export interface DsPopoverRootProps {
10
+ /** Controlled open state. Pair with `onOpenChange`. */
11
+ open?: boolean;
12
+ /** Initial open state when uncontrolled. */
13
+ defaultOpen?: boolean;
4
14
  /**
5
- * The element that triggers the popover
15
+ * Preferred side of the trigger to render the panel against.
16
+ * @default 'bottom'
6
17
  */
7
- trigger: React.ReactNode;
18
+ side?: DsPopoverSide;
8
19
  /**
9
- * The content to be rendered inside the popover
20
+ * Alignment of the panel along the trigger edge.
21
+ * @default 'center'
10
22
  */
11
- children: React.ReactNode;
23
+ align?: DsPopoverAlign;
12
24
  /**
13
- * Additional CSS class names
25
+ * Gap in pixels between the trigger and the panel.
26
+ * @default 8
14
27
  */
15
- className?: string;
28
+ gutter?: number;
16
29
  /**
17
- * The alignment of the popover content
30
+ * Trap focus and block interaction with elements outside the panel.
31
+ * @default false
18
32
  */
19
- align?: 'start' | 'center' | 'end';
33
+ modal?: boolean;
34
+ children: ReactNode;
35
+ /** Fires when the popover opens or closes. */
36
+ onOpenChange?: (open: boolean) => void;
37
+ }
38
+
39
+ export interface DsPopoverTriggerProps {
40
+ /** Single focusable element that toggles the popover. */
41
+ children: ReactNode;
42
+ className?: string;
43
+ }
44
+
45
+ export interface DsPopoverPanelProps {
20
46
  /**
21
- * The position of the popover relative to the trigger element
47
+ * Panel width in pixels.
48
+ * @default 400
22
49
  */
23
- side?: 'top' | 'right' | 'bottom' | 'left';
50
+ width?: number;
51
+ className?: string;
52
+ style?: CSSProperties;
53
+ children: ReactNode;
54
+ ref?: Ref<HTMLDivElement>;
55
+ 'aria-label'?: string;
56
+ }
57
+
58
+ export interface DsPopoverHeaderProps {
59
+ /** Leading visual (e.g. a colored task-type icon) rendered before the title. */
60
+ icon?: ReactNode;
61
+ className?: string;
62
+ style?: CSSProperties;
63
+ /** Title content, exposed as the popover's accessible name. */
64
+ children: ReactNode;
65
+ }
66
+
67
+ export interface DsPopoverContentProps {
68
+ className?: string;
69
+ style?: CSSProperties;
70
+ /** Ordered `DsPopover.ContentItem` children. Compose `DsDivider` between them for visual separation. */
71
+ children: ReactNode;
72
+ }
73
+
74
+ export interface DsPopoverContentItemProps {
75
+ /** Status badge shown in the item's header row. */
76
+ status?: ReactNode;
77
+ /** Bold headline text shown in the item's header row. */
78
+ headline?: ReactNode;
79
+ className?: string;
80
+ style?: CSSProperties;
81
+ /** Item body — text, avatar rows, icons, images, etc. */
82
+ children?: ReactNode;
83
+ }
84
+
85
+ export interface DsPopoverFooterProps {
86
+ className?: string;
87
+ style?: CSSProperties;
88
+ /** Footer actions, typically a single secondary CTA button. */
89
+ children: ReactNode;
90
+ }
91
+
92
+ /**
93
+ * Props for the legacy single-element call form (`<DsPopover trigger=… />`).
94
+ * @deprecated Use the compound API: `DsPopover.Root` + `DsPopover.Trigger` + `DsPopover.Panel`.
95
+ */
96
+ export interface DsPopoverProps {
97
+ /** Element that toggles the popover. */
98
+ trigger: ReactNode;
99
+ /** Panel content. */
100
+ children: ReactNode;
101
+ /** Forwarded to the panel. */
102
+ className?: string;
103
+ /** @default 'center' */
104
+ align?: DsPopoverAlign;
105
+ /** @default 'top' */
106
+ side?: DsPopoverSide;
24
107
  }
@@ -1,2 +1,2 @@
1
- export { default as DsPopover } from './ds-popover';
1
+ export { DsPopover } from './ds-popover';
2
2
  export * from './ds-popover.types';
@@ -0,0 +1,29 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=36987-43620
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-progress-task-bar
3
+ // component=DsProgressTaskBar
4
+ import figma from 'figma';
5
+
6
+ const instance = figma.selectedInstance;
7
+
8
+ // Boolean property names are case-sensitive: `Running` / `Failed` are capitalized.
9
+ const zero = instance.getBoolean('zero');
10
+ const completed = instance.getBoolean('completed');
11
+ const running = instance.getBoolean('Running');
12
+ const failed = instance.getBoolean('Failed');
13
+
14
+ // Booleans only toggle a status on/off, so emit a representative count per status.
15
+ const attrs = [
16
+ completed ? 'completed={300}' : '',
17
+ running ? 'running={100}' : '',
18
+ failed ? 'failed={100}' : '',
19
+ 'total={1000}',
20
+ ]
21
+ .filter(Boolean)
22
+ .join(' ');
23
+
24
+ export default {
25
+ example: zero ? figma.code`<DsProgressTaskBar total={999} />` : figma.code`<DsProgressTaskBar ${attrs} />`,
26
+ imports: ["import { DsProgressTaskBar } from '@drivenets/design-system'"],
27
+ id: 'ds-progress-task-bar',
28
+ metadata: { nestable: false },
29
+ };
@@ -0,0 +1,39 @@
1
+ $bar-height: 8px;
2
+ $segment-min-width: 4px;
3
+
4
+ .statusIcon {
5
+ display: inline-flex;
6
+ }
7
+
8
+ .total {
9
+ flex-shrink: 0;
10
+ }
11
+
12
+ .bar {
13
+ display: flex;
14
+ align-items: center;
15
+ width: 100%;
16
+ height: $bar-height;
17
+ overflow: hidden;
18
+ background-color: var(--background-action-secondary-hover);
19
+ border-radius: var(--3xs);
20
+ }
21
+
22
+ .segment {
23
+ flex: none;
24
+ min-width: $segment-min-width;
25
+ height: 100%;
26
+ transition: width 0.2s ease;
27
+ }
28
+
29
+ .segmentCompleted {
30
+ background-color: var(--background-success-medium);
31
+ }
32
+
33
+ .segmentRunning {
34
+ background-color: var(--background-light-primary);
35
+ }
36
+
37
+ .segmentFailed {
38
+ background-color: var(--background-error);
39
+ }
@@ -0,0 +1,122 @@
1
+ import classNames from 'classnames';
2
+
3
+ import { DsIcon, type IconColor, type IconName } from '../ds-icon';
4
+ import { DsStack } from '../ds-stack';
5
+ import { DsTooltip } from '../ds-tooltip';
6
+ import { DsTypography, type TypographyColor } from '../ds-typography';
7
+
8
+ import styles from './ds-progress-task-bar.module.scss';
9
+ import {
10
+ type DsProgressTaskBarLocale,
11
+ type DsProgressTaskBarProps,
12
+ type TaskBarStatus,
13
+ taskBarStatuses,
14
+ } from './ds-progress-task-bar.types';
15
+ import { formatCount } from './utils';
16
+
17
+ const statusIcon: Record<TaskBarStatus, IconName> = Object.freeze({
18
+ completed: 'check_circle',
19
+ running: 'play_circle',
20
+ failed: 'cancel',
21
+ });
22
+
23
+ const statusIconColor: Record<TaskBarStatus, IconColor> = Object.freeze({
24
+ completed: 'success',
25
+ running: 'action',
26
+ failed: 'error',
27
+ });
28
+
29
+ const statusTextColor: Record<TaskBarStatus, TypographyColor> = Object.freeze({
30
+ completed: 'success',
31
+ running: 'action',
32
+ failed: 'error',
33
+ });
34
+
35
+ const statusSegmentClass: Record<TaskBarStatus, string> = Object.freeze({
36
+ completed: styles.segmentCompleted,
37
+ running: styles.segmentRunning,
38
+ failed: styles.segmentFailed,
39
+ });
40
+
41
+ const defaultLocale: Required<DsProgressTaskBarLocale> = Object.freeze({
42
+ completed: 'Completed',
43
+ running: 'Running',
44
+ failed: 'Failed',
45
+ total: (total: string) => `of ${total}`,
46
+ });
47
+
48
+ const DsProgressTaskBar = ({
49
+ completed = 0,
50
+ running = 0,
51
+ failed = 0,
52
+ total,
53
+ locale,
54
+ className,
55
+ style,
56
+ ref,
57
+ }: DsProgressTaskBarProps) => {
58
+ const counts: Record<TaskBarStatus, number> = {
59
+ completed: Math.max(0, completed),
60
+ running: Math.max(0, running),
61
+ failed: Math.max(0, failed),
62
+ };
63
+
64
+ const sum = counts.completed + counts.running + counts.failed;
65
+ const denominator = Math.max(total ?? sum, sum);
66
+
67
+ const mergedLocale = { ...defaultLocale, ...locale };
68
+ const activeStatuses = taskBarStatuses.filter((status) => counts[status] > 0);
69
+
70
+ const segmentWidth = (status: TaskBarStatus) =>
71
+ denominator > 0 ? `${String((counts[status] / denominator) * 100)}%` : '0%';
72
+
73
+ return (
74
+ <DsStack ref={ref} direction="column" gap="var(--xs)" width="100%" className={className} style={style}>
75
+ <DsStack direction="row" alignItems="center" justifyContent="space-between">
76
+ <DsStack direction="row" alignItems="center" gap="var(--3xs)">
77
+ {activeStatuses.length === 0 ? (
78
+ <DsTypography variant="body-xs-reg" color="secondary">
79
+ 0
80
+ </DsTypography>
81
+ ) : (
82
+ activeStatuses.map((status) => (
83
+ <DsStack key={status} direction="row" alignItems="center" gap="var(--4xs)">
84
+ <DsTooltip content={mergedLocale[status]}>
85
+ <span className={styles.statusIcon}>
86
+ <DsIcon icon={statusIcon[status]} size="tiny" color={statusIconColor[status]} filled />
87
+ </span>
88
+ </DsTooltip>
89
+ <DsTypography variant="body-xs-reg" color={statusTextColor[status]}>
90
+ {formatCount(counts[status])}
91
+ </DsTypography>
92
+ </DsStack>
93
+ ))
94
+ )}
95
+ </DsStack>
96
+
97
+ <DsTypography variant="body-xs-reg" color="secondary" className={styles.total}>
98
+ {mergedLocale.total(formatCount(denominator))}
99
+ </DsTypography>
100
+ </DsStack>
101
+
102
+ <div
103
+ className={styles.bar}
104
+ role="progressbar"
105
+ aria-valuemin={0}
106
+ aria-valuemax={denominator}
107
+ aria-valuenow={sum}
108
+ >
109
+ {activeStatuses.map((status) => (
110
+ <DsTooltip key={status} content={mergedLocale[status]}>
111
+ <span
112
+ className={classNames(styles.segment, statusSegmentClass[status])}
113
+ style={{ width: segmentWidth(status) }}
114
+ />
115
+ </DsTooltip>
116
+ ))}
117
+ </div>
118
+ </DsStack>
119
+ );
120
+ };
121
+
122
+ export default DsProgressTaskBar;