@drivenets/design-system 0.12.1 → 0.14.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 (122) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/index.cjs +1989 -735
  3. package/dist/index.d.cts +1378 -998
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +1378 -998
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1978 -735
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +43 -41
  11. package/src/components/ds-button-v3/ds-button-v3.figma.ts +99 -0
  12. package/src/components/ds-button-v3/ds-button-v3.module.scss +22 -22
  13. package/src/components/ds-card/figma/ds-card-content.figma.ts +15 -0
  14. package/src/components/ds-card/figma/ds-card-footer.figma.ts +15 -0
  15. package/src/components/ds-card/figma/ds-card-header.figma.ts +15 -0
  16. package/src/components/ds-card/figma/ds-card.figma.ts +72 -0
  17. package/src/components/ds-date-input/ds-date-input.utils.ts +2 -2
  18. package/src/components/ds-date-picker/ds-date-picker.figma.ts +19 -0
  19. package/src/components/ds-drawer/ds-drawer.tsx +2 -0
  20. package/src/components/ds-form-control/ds-form-control.figma.ts +76 -0
  21. package/src/components/ds-form-control/ds-form-control.tsx +14 -11
  22. package/src/components/ds-form-control/ds-form-control.types.ts +4 -0
  23. package/src/components/ds-grid/ds-grid.tsx +1 -2
  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/ds-modal.tsx +3 -0
  28. package/src/components/ds-modal/figma/ds-modal-content.figma.ts +18 -0
  29. package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +19 -0
  30. package/src/components/ds-modal/figma/ds-modal-header.figma.ts +21 -0
  31. package/src/components/ds-modal/figma/ds-modal.figma.ts +36 -0
  32. package/src/components/ds-popover/ds-popover.module.scss +58 -16
  33. package/src/components/ds-popover/ds-popover.tsx +148 -24
  34. package/src/components/ds-popover/ds-popover.types.ts +95 -12
  35. package/src/components/ds-popover/index.ts +1 -1
  36. package/src/components/ds-select/ds-select.figma.ts +53 -0
  37. package/src/components/ds-split-button/ds-split-button.figma.ts +95 -0
  38. package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +41 -0
  39. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +21 -2
  40. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +22 -14
  41. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +1 -6
  42. package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +2 -2
  43. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +2 -0
  44. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.tsx +42 -11
  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 +28 -4
  50. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +65 -28
  51. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +26 -0
  52. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +3 -0
  53. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +23 -0
  54. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +4 -1
  55. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.types.ts +1 -1
  56. package/src/components/ds-table/components/edit/cell-editors/cell-editors.module.scss +3 -0
  57. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +27 -0
  58. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +50 -0
  59. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +51 -0
  60. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +36 -0
  61. package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +50 -0
  62. package/src/components/ds-table/components/edit/cell-editors/index.ts +14 -0
  63. package/src/components/ds-table/components/edit/cell-editors/table-edit-form-control.tsx +31 -0
  64. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.module.scss +28 -0
  65. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +39 -0
  66. package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.types.ts +7 -0
  67. package/src/components/ds-table/components/edit/ds-table-editable-cell/index.ts +2 -0
  68. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.module.scss +48 -0
  69. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +76 -0
  70. package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.types.ts +5 -0
  71. package/src/components/ds-table/components/edit/ds-table-editing-cell/index.ts +2 -0
  72. package/src/components/ds-table/context/ds-table-context.ts +83 -1
  73. package/src/components/ds-table/ds-table-column.types.ts +12 -0
  74. package/src/components/ds-table/ds-table.module.scss +100 -10
  75. package/src/components/ds-table/ds-table.tsx +38 -21
  76. package/src/components/ds-table/ds-table.types.ts +107 -0
  77. package/src/components/ds-table/figma/ds-table-expandable.figma.ts +17 -0
  78. package/src/components/ds-table/figma/ds-table.figma.ts +16 -0
  79. package/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx +24 -40
  80. package/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx +10 -31
  81. package/src/components/ds-table/filters/adapters/create-filter-adapter.ts +6 -98
  82. package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.module.scss +36 -0
  83. package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.tsx +92 -13
  84. package/src/components/ds-table/filters/components/checkbox-filter/index.ts +1 -1
  85. package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.module.scss +42 -0
  86. package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.tsx +89 -0
  87. package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.types.ts +25 -0
  88. package/src/components/ds-table/filters/components/column-filter-popover/index.ts +5 -0
  89. package/src/components/ds-table/filters/hooks/use-table-filters.ts +113 -78
  90. package/src/components/ds-table/filters/index.ts +2 -44
  91. package/src/components/ds-table/filters/types/column-meta.augmentation.ts +19 -0
  92. package/src/components/ds-table/filters/types/filter-adapter.types.ts +48 -81
  93. package/src/components/ds-table/grouping/hooks/use-column-groups.ts +73 -0
  94. package/src/components/ds-table/grouping/index.ts +5 -0
  95. package/src/components/ds-table/grouping/types/column-group-meta.augmentation.ts +26 -0
  96. package/src/components/ds-table/grouping/types/column-group.types.ts +35 -0
  97. package/src/components/ds-table/grouping/utils/column-group.ts +119 -0
  98. package/src/components/ds-table/hooks/use-cell-editor.ts +81 -0
  99. package/src/components/ds-table/index.ts +32 -0
  100. package/src/components/ds-table/styles/_variables.scss +1 -0
  101. package/src/components/ds-table/utils/column-size.ts +9 -8
  102. package/src/components/ds-tag/ds-tag.module.scss +22 -1
  103. package/src/components/ds-tag/ds-tag.tsx +18 -5
  104. package/src/components/ds-tag/ds-tag.types.ts +22 -8
  105. package/src/components/ds-tag/ds-tag.unit.test.ts +32 -0
  106. package/src/components/ds-tag-filter/ds-tag-filter.tsx +14 -22
  107. package/src/components/ds-text-input/ds-text-input.figma.ts +60 -0
  108. package/src/components/ds-time-picker/ds-time-picker.figma.ts +18 -0
  109. package/src/components/ds-workspace-layout/ds-workspace-layout.context.ts +19 -0
  110. package/src/components/ds-workspace-layout/ds-workspace-layout.module.scss +166 -0
  111. package/src/components/ds-workspace-layout/ds-workspace-layout.tsx +135 -0
  112. package/src/components/ds-workspace-layout/ds-workspace-layout.types.ts +52 -0
  113. package/src/components/ds-workspace-layout/index.ts +11 -0
  114. package/src/index.ts +1 -1
  115. package/src/styles/_root_new.scss +2 -0
  116. package/src/styles/_scrollbars.scss +36 -22
  117. package/src/utils/restore-document-body-modal-styles.ts +44 -0
  118. package/src/utils/use-ark-dialog-body-lock-cleanup.ts +36 -0
  119. package/src/components/ds-workspace/ds-workspace.module.scss +0 -55
  120. package/src/components/ds-workspace/ds-workspace.tsx +0 -37
  121. package/src/components/ds-workspace/ds-workspace.types.ts +0 -17
  122. package/src/components/ds-workspace/index.ts +0 -8
@@ -4,6 +4,7 @@ import { Portal } from '@ark-ui/react/portal';
4
4
  import classNames from 'classnames';
5
5
  import type { DsModalProps } from './ds-modal.types';
6
6
  import styles from './ds-modal.module.scss';
7
+ import { useArkDialogBodyLockCleanup } from '../../utils/use-ark-dialog-body-lock-cleanup';
7
8
  import { DsIcon } from '../ds-icon';
8
9
  import { DsTypography } from '../ds-typography';
9
10
 
@@ -24,6 +25,8 @@ const DsModal = ({
24
25
  children,
25
26
  onOpenChange,
26
27
  }: DsModalProps) => {
28
+ useArkDialogBodyLockCleanup(open, modal);
29
+
27
30
  const handleOpenChange = (details: { open: boolean }) => {
28
31
  onOpenChange(details.open);
29
32
  };
@@ -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,53 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=13773-1189
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-select
3
+ // component=DsSelect
4
+ import figma from 'figma';
5
+
6
+ const instance = figma.selectedInstance;
7
+
8
+ const size = instance.getEnum('Size', {
9
+ Default: 'default',
10
+ Small: 'small',
11
+ });
12
+
13
+ const state = instance.getEnum('State', {
14
+ Default: 'default',
15
+ Hover: 'hover',
16
+ Focus: 'focus',
17
+ Active: 'active',
18
+ Disable: 'disable',
19
+ Error: 'error',
20
+ 'Error - Focus': 'errorFocus',
21
+ });
22
+
23
+ const disabled = state === 'disable';
24
+
25
+ const sizeAttr = size === 'small' ? 'size="small"' : '';
26
+
27
+ // Multi-select is driven by the nested dropdown menu's `Type` (Checkbox = multi). The menu
28
+ // lives inside the select tree even when collapsed, so the designer's intent is read in any
29
+ // state; an absent menu (ErrorHandle) safely defaults to single-select.
30
+ const menu = instance.findInstance('DAP_dropdown menu_v01', { traverseInstances: true });
31
+ const multiple =
32
+ menu.type === 'INSTANCE' && menu.getEnum('Type', { default: false, Checkbox: true, Radio: false }) === true;
33
+
34
+ // `value` and `options` are required by `DsSelectProps` but are data, not derivable from
35
+ // Figma, so emit placeholders for the developer to fill in (the discriminated union needs
36
+ // an array `value` when `multiple`). The same string is surfaced as `fcProps` so a parent
37
+ // `DsFormControl` template can emit `<DsFormControl.Select ${fcProps} />` uniformly.
38
+ const attrs = [
39
+ sizeAttr,
40
+ disabled ? 'disabled' : '',
41
+ multiple ? 'multiple' : '',
42
+ 'options={[]}',
43
+ multiple ? 'value={[]}' : 'value=""',
44
+ ]
45
+ .filter(Boolean)
46
+ .join(' ');
47
+
48
+ export default {
49
+ example: figma.code`<DsSelect ${attrs} />`,
50
+ imports: ["import { DsSelect } from '@drivenets/design-system'"],
51
+ id: 'ds-select',
52
+ metadata: { nestable: true, props: { fcMember: 'Select', fcProps: attrs } },
53
+ };
@@ -0,0 +1,95 @@
1
+ // url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=32830-9950
2
+ // source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-split-button
3
+ // component=DsSplitButton
4
+ import figma from 'figma';
5
+
6
+ const instance = figma.selectedInstance;
7
+
8
+ const state = instance.getEnum('state', {
9
+ default: 'default',
10
+ hover: 'hover',
11
+ select: 'select',
12
+ focus: 'focus',
13
+ disabled: 'disabled',
14
+ running: 'running',
15
+ });
16
+ const disabled = state === 'disabled';
17
+ const loading = state === 'running';
18
+
19
+ const button = instance.findInstance('DAP_Button_v03', { traverseInstances: true });
20
+ const size =
21
+ button.type === 'INSTANCE'
22
+ ? button.getEnum('Size', { tiny: 'small', small: 'small', medium: 'medium', large: 'medium' })
23
+ : 'medium';
24
+
25
+ const structure = instance.findInstance('.button-structure', { traverseInstances: true });
26
+ const showIcon = structure.type === 'INSTANCE' ? structure.getBoolean('Show icon') : false;
27
+
28
+ // `DsButtonV3.icon` is an icon-name string. Swapped icons keep the placeholder layer
29
+ // name (e.g. `DAP_GM_O_check_circle`), so resolve via Code Connect ID or metadata.
30
+ const toIconName = (name: string): string =>
31
+ name
32
+ .replace(/^DAP_DAP_/, 'DAP_')
33
+ .replace(/^DAP_GM_[OS]_/, '')
34
+ .replace(/^DAP_outline_icon\//, '')
35
+ .replace(/^DAP_/, '')
36
+ .replace(/^\/+/, '')
37
+ .replace(/[\s/]+/g, '_')
38
+ .toLowerCase();
39
+
40
+ const iconNameFromCodeConnectId = (id: string | null): string | undefined => {
41
+ if (!id) {
42
+ return undefined;
43
+ }
44
+ if (id.startsWith('ds-icon-o-')) {
45
+ return id.slice('ds-icon-o-'.length);
46
+ }
47
+ if (id.startsWith('ds-icon-s-')) {
48
+ return id.slice('ds-icon-s-'.length);
49
+ }
50
+ return undefined;
51
+ };
52
+
53
+ const resolveIcon = (iconInstance: figma.InstanceHandle): string | undefined => {
54
+ const fromCcId = iconNameFromCodeConnectId(iconInstance.codeConnectId());
55
+ if (fromCcId) {
56
+ return fromCcId;
57
+ }
58
+
59
+ const iconFromMetadata = iconInstance.executeTemplate().metadata?.props?.icon;
60
+ if (typeof iconFromMetadata === 'string') {
61
+ return iconFromMetadata;
62
+ }
63
+
64
+ return toIconName(iconInstance.name);
65
+ };
66
+
67
+ const leadingIcon = structure.type === 'INSTANCE' ? structure.getInstanceSwap('Leading icon') : undefined;
68
+ const iconFromSwap = leadingIcon?.type === 'INSTANCE' ? resolveIcon(leadingIcon) : undefined;
69
+ const iconFromChild =
70
+ structure.type === 'INSTANCE'
71
+ ? structure
72
+ .findConnectedInstances((node) => node.codeConnectId()?.startsWith('ds-icon-') ?? false, {
73
+ traverseInstances: true,
74
+ })
75
+ .find((node): node is figma.InstanceHandle => node.type === 'INSTANCE')
76
+ : undefined;
77
+ const icon = iconFromSwap ?? (iconFromChild ? resolveIcon(iconFromChild) : undefined);
78
+
79
+ // The split button's button slot is intended for an icon-only button, so emit
80
+ // `icon` (and `loading`) rather than a text `children` label.
81
+ // While loading, a spinner replaces the icon, so omit it
82
+ // (the leading icon is the component's default placeholder in that state).
83
+ const buttonProps = [
84
+ !loading && showIcon && icon ? `icon: '${icon}'` : undefined,
85
+ loading ? 'loading: true' : undefined,
86
+ ]
87
+ .filter(Boolean)
88
+ .join(', ');
89
+
90
+ export default {
91
+ example: figma.code`<DsSplitButton size="${size}"${disabled ? ' disabled' : ''} slotProps={{ button: { ${buttonProps} }, select: { options: [], value: '' } }} />`,
92
+ imports: ["import { DsSplitButton } from '@drivenets/design-system'"],
93
+ id: 'ds-split-button',
94
+ metadata: { nestable: false },
95
+ };