@drivenets/design-system 0.13.0 → 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.
- package/CHANGELOG.md +16 -0
- package/dist/index.cjs +791 -326
- package/dist/index.d.cts +265 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +265 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +789 -326
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +13 -11
- package/src/components/ds-button-v3/ds-button-v3.figma.ts +99 -0
- package/src/components/ds-button-v3/ds-button-v3.module.scss +22 -22
- package/src/components/ds-card/figma/ds-card-content.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card-footer.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card-header.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card.figma.ts +72 -0
- package/src/components/ds-date-picker/ds-date-picker.figma.ts +19 -0
- package/src/components/ds-form-control/ds-form-control.figma.ts +76 -0
- package/src/components/ds-icon/figma/ds-icon-material.figma.batch.ts +10 -0
- package/src/components/ds-icon/figma/ds-icon-special.figma.batch.ts +18 -0
- package/src/components/ds-icon/figma/ds-icon.figma.batch.json +2873 -0
- package/src/components/ds-modal/figma/ds-modal-content.figma.ts +18 -0
- package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +19 -0
- package/src/components/ds-modal/figma/ds-modal-header.figma.ts +21 -0
- package/src/components/ds-modal/figma/ds-modal.figma.ts +36 -0
- package/src/components/ds-popover/ds-popover.module.scss +58 -16
- package/src/components/ds-popover/ds-popover.tsx +148 -24
- package/src/components/ds-popover/ds-popover.types.ts +95 -12
- package/src/components/ds-popover/index.ts +1 -1
- package/src/components/ds-select/ds-select.figma.ts +53 -0
- package/src/components/ds-split-button/ds-split-button.figma.ts +95 -0
- package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +41 -0
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +8 -3
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +31 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +48 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.types.ts +12 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/index.ts +2 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +4 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +55 -39
- package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +4 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +2 -0
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +4 -0
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +2 -0
- package/src/components/ds-table/context/ds-table-context.ts +4 -0
- package/src/components/ds-table/ds-table.tsx +20 -2
- package/src/components/ds-table/ds-table.types.ts +55 -0
- package/src/components/ds-table/figma/ds-table-expandable.figma.ts +17 -0
- package/src/components/ds-table/figma/ds-table.figma.ts +16 -0
- package/src/components/ds-table/grouping/hooks/use-column-groups.ts +73 -0
- package/src/components/ds-table/grouping/index.ts +5 -0
- package/src/components/ds-table/grouping/types/column-group-meta.augmentation.ts +26 -0
- package/src/components/ds-table/grouping/types/column-group.types.ts +35 -0
- package/src/components/ds-table/grouping/utils/column-group.ts +119 -0
- package/src/components/ds-table/index.ts +3 -0
- package/src/components/ds-table/styles/_variables.scss +1 -0
- package/src/components/ds-tag/ds-tag.module.scss +22 -1
- package/src/components/ds-tag/ds-tag.tsx +16 -3
- package/src/components/ds-tag/ds-tag.types.ts +22 -8
- package/src/components/ds-tag/ds-tag.unit.test.ts +32 -0
- package/src/components/ds-tag-filter/ds-tag-filter.tsx +14 -22
- package/src/components/ds-text-input/ds-text-input.figma.ts +60 -0
- package/src/components/ds-time-picker/ds-time-picker.figma.ts +18 -0
- package/src/styles/_root_new.scss +2 -0
- 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
|
-
|
|
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:
|
|
12
|
-
|
|
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
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
2
|
-
import
|
|
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 {
|
|
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
|
|
8
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
|
1
|
+
import type { CSSProperties, ReactNode, Ref } from 'react';
|
|
2
2
|
|
|
3
|
-
export
|
|
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
|
-
*
|
|
15
|
+
* Preferred side of the trigger to render the panel against.
|
|
16
|
+
* @default 'bottom'
|
|
6
17
|
*/
|
|
7
|
-
|
|
18
|
+
side?: DsPopoverSide;
|
|
8
19
|
/**
|
|
9
|
-
*
|
|
20
|
+
* Alignment of the panel along the trigger edge.
|
|
21
|
+
* @default 'center'
|
|
10
22
|
*/
|
|
11
|
-
|
|
23
|
+
align?: DsPopoverAlign;
|
|
12
24
|
/**
|
|
13
|
-
*
|
|
25
|
+
* Gap in pixels between the trigger and the panel.
|
|
26
|
+
* @default 8
|
|
14
27
|
*/
|
|
15
|
-
|
|
28
|
+
gutter?: number;
|
|
16
29
|
/**
|
|
17
|
-
*
|
|
30
|
+
* Trap focus and block interaction with elements outside the panel.
|
|
31
|
+
* @default false
|
|
18
32
|
*/
|
|
19
|
-
|
|
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
|
-
*
|
|
47
|
+
* Panel width in pixels.
|
|
48
|
+
* @default 400
|
|
22
49
|
*/
|
|
23
|
-
|
|
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 {
|
|
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
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=34428-16204
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-status-badge-v2
|
|
3
|
+
// component=DsStatusBadgeV2
|
|
4
|
+
import figma from 'figma';
|
|
5
|
+
|
|
6
|
+
const instance = figma.selectedInstance;
|
|
7
|
+
|
|
8
|
+
const phase = instance.getEnum('status', {
|
|
9
|
+
'not-started': 'not-started',
|
|
10
|
+
temporary: 'temporary',
|
|
11
|
+
'in-review': 'in-review',
|
|
12
|
+
pending: 'pending',
|
|
13
|
+
active: 'active',
|
|
14
|
+
execution: 'execution',
|
|
15
|
+
'result-succeeded': 'result-succeeded',
|
|
16
|
+
'result-warning': 'result-warning',
|
|
17
|
+
'result-failed': 'result-failed',
|
|
18
|
+
deprecated: 'deprecated',
|
|
19
|
+
});
|
|
20
|
+
const variant = instance.getEnum('variant', { primary: 'primary', secondary: 'secondary' });
|
|
21
|
+
const size = instance.getEnum('Size', { medium: 'medium', small: 'small' });
|
|
22
|
+
const structure = instance.getEnum('structure', {
|
|
23
|
+
'icon+text': 'icon+text',
|
|
24
|
+
'icon-only': 'icon-only',
|
|
25
|
+
'text-only': 'text-only',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const labelNode = instance.findText('Active', { traverseInstances: true });
|
|
29
|
+
const label = labelNode.type === 'TEXT' ? labelNode.textContent : 'Active';
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
example:
|
|
33
|
+
structure === 'icon-only'
|
|
34
|
+
? figma.code`<DsStatusBadgeV2 phase="${phase}" label="${label}" variant="${variant}" size="${size}" iconOnly />`
|
|
35
|
+
: structure === 'text-only'
|
|
36
|
+
? figma.code`<DsStatusBadgeV2 phase="${phase}" label="${label}" variant="${variant}" size="${size}" icon={null} />`
|
|
37
|
+
: figma.code`<DsStatusBadgeV2 phase="${phase}" label="${label}" variant="${variant}" size="${size}" />`,
|
|
38
|
+
imports: ["import { DsStatusBadgeV2 } from '@drivenets/design-system'"],
|
|
39
|
+
id: 'ds-status-badge-v2',
|
|
40
|
+
metadata: { nestable: true },
|
|
41
|
+
};
|