@drivenets/design-system 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/index.cjs +12409 -10548
- package/dist/index.d.cts +678 -312
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +679 -313
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12404 -10550
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +29 -29
- package/src/components/ds-checkbox/ds-checkbox.figma.ts +3 -0
- package/src/components/ds-checkbox/ds-checkbox.module.scss +102 -20
- package/src/components/ds-checkbox/ds-checkbox.tsx +30 -2
- package/src/components/ds-checkbox/ds-checkbox.types.ts +10 -0
- package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.module.scss +169 -0
- package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.tsx +92 -0
- package/src/components/ds-code-input/collapsed-code-field/index.ts +1 -0
- package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.module.scss +110 -0
- package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.tsx +61 -0
- package/src/components/ds-code-input/ds-code-input-panel/index.ts +1 -0
- package/src/components/ds-code-input/ds-code-input.module.scss +25 -0
- package/src/components/ds-code-input/ds-code-input.tsx +401 -0
- package/src/components/ds-code-input/ds-code-input.types.ts +166 -0
- package/src/components/ds-code-input/ds-code-input.utils.ts +94 -0
- package/src/components/ds-code-input/ds-code-input.utils.unit.test.ts +166 -0
- package/src/components/ds-code-input/index.ts +2 -0
- package/src/components/ds-code-input/use-collapsed-viewport.ts +289 -0
- package/src/components/ds-code-input/use-editor-host.ts +87 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.types.ts +41 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-builder/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-clear-all.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.types.ts +30 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-pinned.ts +31 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.types.ts +22 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-query/index.ts +1 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-saved-filters.ts +19 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-search.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-summary.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-toolbar.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-view-switch.ts +11 -0
- package/src/components/ds-filters-bar/components/ds-filters-bar-view.ts +11 -0
- package/src/components/ds-filters-bar/components/index.ts +11 -0
- package/src/components/ds-filters-bar/ds-filters-bar.context.ts +69 -0
- package/src/components/ds-filters-bar/ds-filters-bar.tsx +120 -0
- package/src/components/ds-filters-bar/ds-filters-bar.types.ts +440 -0
- package/src/components/ds-filters-bar/ds-filters-bar.unit.test.ts +216 -0
- package/src/components/ds-filters-bar/ds-filters-bar.utils.ts +141 -0
- package/src/components/ds-filters-bar/index.ts +1 -0
- package/src/components/ds-form-control/ds-form-control.module.scss +18 -0
- package/src/components/ds-form-control/ds-form-control.tsx +43 -9
- package/src/components/ds-form-control/ds-form-control.types.ts +2 -1
- package/src/components/ds-icon/custom-icons/index.ts +2 -0
- package/src/components/ds-icon/custom-icons/special-adv-filters.tsx +28 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.figma.ts +27 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.module.scss +41 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.tsx +43 -0
- package/src/components/ds-pin-toggle/ds-pin-toggle.types.ts +29 -0
- package/src/components/ds-pin-toggle/index.ts +2 -0
- package/src/components/ds-popover/components/popover-trigger/index.ts +1 -0
- package/src/components/ds-popover/components/popover-trigger/popover-trigger.tsx +15 -0
- package/src/components/ds-popover/ds-popover.context.ts +18 -0
- package/src/components/ds-popover/ds-popover.hover-intent.ts +65 -0
- package/src/components/ds-popover/ds-popover.tsx +163 -41
- package/src/components/ds-popover/ds-popover.types.ts +59 -1
- package/src/components/ds-popover/ds-popover.utils.ts +60 -0
- package/src/components/ds-popover/ds-popover.utils.unit.test.ts +25 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-delete-confirm.tsx +53 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-name-entry.tsx +64 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-name-modal.tsx +50 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.module.scss +68 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.tsx +83 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-row/index.ts +1 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-save.tsx +164 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.module.scss +21 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.tsx +237 -0
- package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/index.ts +1 -0
- package/src/components/ds-saved-filters/components/index.ts +2 -0
- package/src/components/ds-saved-filters/ds-saved-filters.ts +6 -0
- package/src/components/ds-saved-filters/ds-saved-filters.types.ts +106 -0
- package/src/components/ds-saved-filters/ds-saved-filters.utils.ts +35 -0
- package/src/components/ds-saved-filters/hooks/index.ts +1 -0
- package/src/components/ds-saved-filters/hooks/use-picker-outside-dismiss.ts +42 -0
- package/src/components/ds-saved-filters/index.ts +2 -0
- package/src/components/ds-segment-group/ds-segment-group.module.scss +65 -37
- package/src/components/ds-segment-group/ds-segment-group.tsx +17 -5
- package/src/components/ds-segment-group/ds-segment-group.types.ts +20 -5
- package/src/components/ds-segment-group/figma/ds-segment-group-v2-item.figma.ts +109 -0
- package/src/components/ds-segment-group/figma/ds-segment-group-v2.figma.ts +31 -0
- package/src/components/ds-tag/ds-tag.figma.ts +23 -3
- package/src/components/ds-tag/ds-tag.module.scss +71 -7
- package/src/components/ds-tag/ds-tag.tsx +115 -14
- package/src/components/ds-tag/ds-tag.types.ts +70 -3
- package/src/components/ds-tag/ds-tag.unit.test.ts +23 -4
- package/src/components/ds-tag/index.ts +2 -2
- package/src/components/ds-text-input/ds-input-field-v02.figma.ts +2 -0
- package/src/components/ds-toggle/ds-toggle.tsx +19 -15
- package/src/components/ds-toggle/ds-toggle.types.ts +6 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.module.scss +69 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.tsx +55 -0
- package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.types.ts +43 -0
- package/src/components/ds-toggle-filter-data/index.ts +1 -0
- package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -0
- package/src/components/ds-tooltip/ds-tooltip.tsx +1 -1
- package/src/components/ds-tree/ds-tree.tsx +10 -10
- package/src/components/ds-tree/ds-tree.types.ts +40 -7
- package/src/index.ts +2 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { CSSProperties, Ref } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface DsSavedFilterItem {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
/**
|
|
7
|
+
* Number of **Filter conditions** in this snapshot. Omit to hide the trailing count.
|
|
8
|
+
*/
|
|
9
|
+
count?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DsSavedFiltersLocale {
|
|
13
|
+
savedFilters?: string;
|
|
14
|
+
noSavedFilters?: string;
|
|
15
|
+
saveFilter?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Menu item for overwriting the active snapshot. `{name}` is replaced with the active name.
|
|
18
|
+
*/
|
|
19
|
+
updateFilter?: string;
|
|
20
|
+
saveAsNew?: string;
|
|
21
|
+
renameFilter?: string;
|
|
22
|
+
deleteFilter?: string;
|
|
23
|
+
nameLabel?: string;
|
|
24
|
+
confirm?: string;
|
|
25
|
+
cancel?: string;
|
|
26
|
+
close?: string;
|
|
27
|
+
unsavedChanges?: string;
|
|
28
|
+
rowActions?: string;
|
|
29
|
+
expandAriaLabel?: string;
|
|
30
|
+
clearAriaLabel?: string;
|
|
31
|
+
saveAsTitle?: string;
|
|
32
|
+
renameTitle?: string;
|
|
33
|
+
deleteTitle?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Confirmation body. `{name}` is replaced with the snapshot name.
|
|
36
|
+
*/
|
|
37
|
+
deleteMessage?: string;
|
|
38
|
+
deleteConfirm?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const defaultDsSavedFiltersLocale: Required<DsSavedFiltersLocale> = Object.freeze({
|
|
42
|
+
savedFilters: 'Saved filters',
|
|
43
|
+
noSavedFilters: 'No saved filters yet. Build a filter, then choose “Save filter”.',
|
|
44
|
+
saveFilter: 'Save filter',
|
|
45
|
+
updateFilter: 'Update “{name}”',
|
|
46
|
+
saveAsNew: 'Save as a new filter',
|
|
47
|
+
renameFilter: 'Rename filter',
|
|
48
|
+
deleteFilter: 'Delete filter',
|
|
49
|
+
nameLabel: 'Filter name',
|
|
50
|
+
confirm: 'Save',
|
|
51
|
+
cancel: 'Cancel',
|
|
52
|
+
close: 'Close',
|
|
53
|
+
unsavedChanges: 'Unsaved changes',
|
|
54
|
+
rowActions: 'Filter actions',
|
|
55
|
+
expandAriaLabel: 'Open saved filters',
|
|
56
|
+
clearAriaLabel: 'Clear filters',
|
|
57
|
+
saveAsTitle: 'Save as a new filter',
|
|
58
|
+
renameTitle: 'Rename filter',
|
|
59
|
+
deleteTitle: 'Delete filter',
|
|
60
|
+
deleteMessage: 'Delete “{name}”? This cannot be undone.',
|
|
61
|
+
deleteConfirm: 'Delete',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Return a Promise to keep the launching control in a loading state until it settles.
|
|
66
|
+
*/
|
|
67
|
+
export type DsSavedFiltersAsyncHandler<Args extends unknown[] = []> = (...args: Args) => void | Promise<void>;
|
|
68
|
+
|
|
69
|
+
export interface DsSavedFiltersTriggerProps {
|
|
70
|
+
items: ReadonlyArray<DsSavedFilterItem>;
|
|
71
|
+
/**
|
|
72
|
+
* Id of the **Active saved filter**, or `null` when none is applied.
|
|
73
|
+
*/
|
|
74
|
+
value: string | null;
|
|
75
|
+
/**
|
|
76
|
+
* Whether the working document diverged from the **Active saved filter**.
|
|
77
|
+
*/
|
|
78
|
+
dirty?: boolean;
|
|
79
|
+
locale?: DsSavedFiltersLocale;
|
|
80
|
+
ref?: Ref<HTMLDivElement>;
|
|
81
|
+
className?: string;
|
|
82
|
+
style?: CSSProperties;
|
|
83
|
+
onValueChange: (id: string | null) => void;
|
|
84
|
+
onClear: DsSavedFiltersAsyncHandler;
|
|
85
|
+
onRename: DsSavedFiltersAsyncHandler<[id: string, name: string]>;
|
|
86
|
+
onDelete: DsSavedFiltersAsyncHandler<[id: string]>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface DsSavedFiltersSaveProps {
|
|
90
|
+
items: ReadonlyArray<DsSavedFilterItem>;
|
|
91
|
+
/**
|
|
92
|
+
* Id of the **Active saved filter**, or `null` when none is applied.
|
|
93
|
+
*/
|
|
94
|
+
value: string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Blocks saving, for example while there is nothing to save
|
|
97
|
+
* @default false
|
|
98
|
+
*/
|
|
99
|
+
disabled?: boolean;
|
|
100
|
+
locale?: DsSavedFiltersLocale;
|
|
101
|
+
ref?: Ref<HTMLButtonElement>;
|
|
102
|
+
className?: string;
|
|
103
|
+
style?: CSSProperties;
|
|
104
|
+
onUpdate: DsSavedFiltersAsyncHandler;
|
|
105
|
+
onSaveAs: DsSavedFiltersAsyncHandler<[name: string]>;
|
|
106
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defaultDsSavedFiltersLocale, type DsSavedFiltersLocale } from './ds-saved-filters.types';
|
|
2
|
+
|
|
3
|
+
export const PICKER_GUTTER_PX = 4;
|
|
4
|
+
export const NAME_MODAL_COLUMNS = 4;
|
|
5
|
+
|
|
6
|
+
export type Locale = Required<DsSavedFiltersLocale>;
|
|
7
|
+
export type NamedTarget = { id: string; name: string };
|
|
8
|
+
|
|
9
|
+
export const resolveLocale = (locale?: DsSavedFiltersLocale): Locale => ({
|
|
10
|
+
...defaultDsSavedFiltersLocale,
|
|
11
|
+
...locale,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const interpolateName = (template: string, name: string) => template.replace('{name}', name);
|
|
15
|
+
|
|
16
|
+
export const isThenable = (value: unknown): value is Promise<unknown> =>
|
|
17
|
+
typeof value === 'object' && value !== null && typeof (value as Promise<unknown>).then === 'function';
|
|
18
|
+
|
|
19
|
+
export const runMaybeAsync = async (result: void | Promise<void>, setBusy: (busy: boolean) => void) => {
|
|
20
|
+
if (!isThenable(result)) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setBusy(true);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await result;
|
|
28
|
+
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
} finally {
|
|
33
|
+
setBusy(false);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { usePickerOutsideDismiss } from './use-picker-outside-dismiss';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useEffect, type RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
const OVERLAY_SCOPES = '[data-scope="popover"], [data-scope="menu"], [data-scope="dialog"]';
|
|
4
|
+
|
|
5
|
+
const isPickerDismissTarget = (event: Event, triggerEl: HTMLElement | null) => {
|
|
6
|
+
const path = event.composedPath();
|
|
7
|
+
|
|
8
|
+
if (triggerEl && path.includes(triggerEl)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return path.some((node) => node instanceof Element && Boolean(node.closest(OVERLAY_SCOPES)));
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Closes the picker on a real outside pointer.
|
|
17
|
+
* Ark interact-outside is suppressed so portaled menus/dialogs stay usable;
|
|
18
|
+
* this restores dismiss without treating those layers as outside.
|
|
19
|
+
*/
|
|
20
|
+
export const usePickerOutsideDismiss = (
|
|
21
|
+
enabled: boolean,
|
|
22
|
+
triggerEl: RefObject<HTMLElement | null>,
|
|
23
|
+
onDismiss: () => void,
|
|
24
|
+
) => {
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!enabled) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
31
|
+
if (isPickerDismissTarget(event, triggerEl.current)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
onDismiss();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
document.addEventListener('pointerdown', onPointerDown);
|
|
39
|
+
|
|
40
|
+
return () => document.removeEventListener('pointerdown', onPointerDown);
|
|
41
|
+
}, [enabled, triggerEl, onDismiss]);
|
|
42
|
+
};
|
|
@@ -1,20 +1,30 @@
|
|
|
1
|
+
$transition-duration: 0.2s;
|
|
2
|
+
$focus-border-width: 2px;
|
|
3
|
+
|
|
4
|
+
@mixin item-size($height, $inline-padding) {
|
|
5
|
+
height: $height;
|
|
6
|
+
padding: 0 $inline-padding;
|
|
7
|
+
|
|
8
|
+
&:not(:has(.segmentItemText)) {
|
|
9
|
+
width: $height;
|
|
10
|
+
padding: 0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
.segmentGroupRoot {
|
|
2
15
|
display: inline-flex;
|
|
3
16
|
align-items: center;
|
|
4
17
|
position: relative;
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
padding: var(--3xs);
|
|
20
|
+
gap: var(--2xs);
|
|
5
21
|
background: var(--background-secondary);
|
|
6
22
|
border: 1px solid var(--border-contrast);
|
|
7
|
-
border-radius:
|
|
8
|
-
gap: var(--2xs);
|
|
9
|
-
height: 36px;
|
|
10
|
-
padding: var(--3xs);
|
|
23
|
+
border-radius: var(--3xs);
|
|
11
24
|
|
|
12
25
|
&.small {
|
|
13
|
-
height: 28px;
|
|
14
|
-
padding: var(--4xs);
|
|
15
|
-
|
|
16
26
|
.segmentItem {
|
|
17
|
-
|
|
27
|
+
@include item-size(var(--lg), var(--xs));
|
|
18
28
|
}
|
|
19
29
|
}
|
|
20
30
|
}
|
|
@@ -24,49 +34,68 @@
|
|
|
24
34
|
display: flex;
|
|
25
35
|
align-items: center;
|
|
26
36
|
justify-content: center;
|
|
27
|
-
|
|
28
|
-
gap: var(--2xs);
|
|
29
|
-
flex: 1;
|
|
37
|
+
box-sizing: border-box;
|
|
30
38
|
z-index: 1;
|
|
39
|
+
gap: var(--3xs);
|
|
40
|
+
@include item-size(var(--xl), var(--sm));
|
|
41
|
+
border: 1px solid transparent;
|
|
42
|
+
border-radius: var(--3xs);
|
|
43
|
+
color: var(--font-main);
|
|
31
44
|
cursor: pointer;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
font-weight: var(--font-weight-medium);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
45
|
+
transition:
|
|
46
|
+
background-color $transition-duration,
|
|
47
|
+
border-color $transition-duration,
|
|
48
|
+
color $transition-duration,
|
|
49
|
+
border-width $transition-duration;
|
|
40
50
|
|
|
41
51
|
&[data-state='unchecked'] {
|
|
42
52
|
background: transparent;
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
&:hover:not([data-disabled]) {
|
|
55
|
+
background: var(--background);
|
|
46
56
|
}
|
|
47
57
|
|
|
58
|
+
&:active:not([data-disabled]) {
|
|
59
|
+
background: var(--background-tertiary);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&[data-focus-visible]:not([data-disabled]) {
|
|
63
|
+
background: var(--background-action-hover-weak);
|
|
64
|
+
border-width: $focus-border-width;
|
|
65
|
+
border-color: var(--border-action-secondary);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&[data-state='checked'] {
|
|
48
70
|
&:hover:not([data-disabled]) {
|
|
49
|
-
background: var(--background-action-
|
|
71
|
+
background: var(--background-action-hover-weak);
|
|
72
|
+
border-color: var(--border);
|
|
50
73
|
}
|
|
51
74
|
|
|
52
75
|
&:active:not([data-disabled]) {
|
|
53
|
-
background: var(--background);
|
|
54
|
-
border-color: var(--border-
|
|
76
|
+
background: var(--background-secondary-selected-weak);
|
|
77
|
+
border-color: var(--border-action-secondary);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&[data-focus-visible]:not([data-disabled]) {
|
|
81
|
+
background: var(--background-secondary-selected-weak);
|
|
82
|
+
border-width: $focus-border-width;
|
|
83
|
+
border-color: var(--border-action-secondary);
|
|
55
84
|
}
|
|
56
85
|
}
|
|
57
86
|
|
|
58
87
|
&[data-disabled] {
|
|
59
88
|
cursor: not-allowed;
|
|
60
|
-
|
|
89
|
+
color: var(--font-disabled);
|
|
61
90
|
|
|
62
|
-
|
|
63
|
-
|
|
91
|
+
&[data-state='unchecked'] {
|
|
92
|
+
background: transparent;
|
|
64
93
|
}
|
|
65
|
-
}
|
|
66
94
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
95
|
+
&[data-state='checked'] {
|
|
96
|
+
background: var(--background);
|
|
97
|
+
border-color: var(--border-disabled);
|
|
98
|
+
}
|
|
70
99
|
}
|
|
71
100
|
}
|
|
72
101
|
|
|
@@ -81,19 +110,18 @@
|
|
|
81
110
|
align-items: center;
|
|
82
111
|
justify-content: center;
|
|
83
112
|
white-space: nowrap;
|
|
84
|
-
transition: color
|
|
113
|
+
transition: color $transition-duration;
|
|
85
114
|
}
|
|
86
115
|
|
|
87
116
|
.segmentIndicator {
|
|
88
117
|
position: absolute;
|
|
89
|
-
background: var(--background);
|
|
90
|
-
border: 1px solid var(--border-contrast);
|
|
91
|
-
border-radius: 4px;
|
|
92
|
-
box-shadow: 0 1.25px 4px 0 rgba(0, 0, 0, 0.25);
|
|
93
118
|
z-index: 0;
|
|
94
|
-
pointer-events: none;
|
|
95
119
|
width: var(--width);
|
|
96
120
|
height: var(--height);
|
|
97
121
|
left: var(--left);
|
|
98
122
|
top: var(--top);
|
|
123
|
+
background: var(--background);
|
|
124
|
+
border: 1px solid var(--border);
|
|
125
|
+
border-radius: var(--3xs);
|
|
126
|
+
pointer-events: none;
|
|
99
127
|
}
|
|
@@ -32,7 +32,15 @@ const Root: React.FC<DsSegmentGroupRootProps> = ({
|
|
|
32
32
|
/**
|
|
33
33
|
* Item component - renders a single segment button
|
|
34
34
|
*/
|
|
35
|
-
const Item: React.FC<DsSegmentGroupItemProps> = ({
|
|
35
|
+
const Item: React.FC<DsSegmentGroupItemProps> = ({
|
|
36
|
+
value,
|
|
37
|
+
label,
|
|
38
|
+
className,
|
|
39
|
+
style,
|
|
40
|
+
children,
|
|
41
|
+
size = 'default',
|
|
42
|
+
...props
|
|
43
|
+
}) => {
|
|
36
44
|
return (
|
|
37
45
|
<SegmentGroup.Item
|
|
38
46
|
value={value}
|
|
@@ -40,7 +48,7 @@ const Item: React.FC<DsSegmentGroupItemProps> = ({ value, label, className, styl
|
|
|
40
48
|
style={style}
|
|
41
49
|
{...props}
|
|
42
50
|
>
|
|
43
|
-
{label ? <ItemText>{label}</ItemText> : children}
|
|
51
|
+
{label ? <ItemText size={size}>{label}</ItemText> : children}
|
|
44
52
|
<SegmentGroup.ItemControl />
|
|
45
53
|
<SegmentGroup.ItemHiddenInput />
|
|
46
54
|
</SegmentGroup.Item>
|
|
@@ -50,9 +58,14 @@ const Item: React.FC<DsSegmentGroupItemProps> = ({ value, label, className, styl
|
|
|
50
58
|
/**
|
|
51
59
|
* ItemText component - renders text content within an item
|
|
52
60
|
*/
|
|
53
|
-
const ItemText: React.FC<DsSegmentGroupItemTextProps> = ({
|
|
61
|
+
const ItemText: React.FC<DsSegmentGroupItemTextProps> = ({
|
|
62
|
+
className,
|
|
63
|
+
children,
|
|
64
|
+
size = 'default',
|
|
65
|
+
...props
|
|
66
|
+
}) => (
|
|
54
67
|
<SegmentGroup.ItemText className={classNames(styles.segmentItemText, className)} {...props} asChild>
|
|
55
|
-
<DsTypography variant=
|
|
68
|
+
<DsTypography variant={size === 'small' ? 'body-xs-md' : 'body-sm-md'}>{children}</DsTypography>
|
|
56
69
|
</SegmentGroup.ItemText>
|
|
57
70
|
);
|
|
58
71
|
|
|
@@ -61,7 +74,6 @@ const ItemText: React.FC<DsSegmentGroupItemTextProps> = ({ className, children,
|
|
|
61
74
|
*
|
|
62
75
|
* @example
|
|
63
76
|
* <DsSegmentGroup.Root value={value} onValueChange={setValue} size="default">
|
|
64
|
-
* <DsSegmentGroup.Indicator />
|
|
65
77
|
* <DsSegmentGroup.Item value="option1" label="Option 1" />
|
|
66
78
|
* <DsSegmentGroup.Item value="option2">
|
|
67
79
|
* <DsIcon icon="settings" />
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
/**
|
|
9
9
|
* Size variants for the segment group
|
|
10
10
|
*/
|
|
11
|
-
export
|
|
11
|
+
export const segmentGroupSizes = ['small', 'default'] as const;
|
|
12
|
+
export type DsSegmentGroupSize = (typeof segmentGroupSizes)[number];
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Props for the DsSegmentGroup Root component
|
|
@@ -50,9 +51,14 @@ export interface DsSegmentGroupRootProps extends Pick<
|
|
|
50
51
|
*/
|
|
51
52
|
export type DsSegmentGroupItemProps = Pick<
|
|
52
53
|
SegmentGroupItemProps,
|
|
53
|
-
'id' | 'value' | 'disabled' | 'className' | 'style'
|
|
54
|
-
> &
|
|
55
|
-
|
|
54
|
+
'id' | 'value' | 'disabled' | 'className' | 'style' | 'aria-label'
|
|
55
|
+
> & {
|
|
56
|
+
/**
|
|
57
|
+
* Size variant of the segment item. Pass through to `ItemText` when using `label`.
|
|
58
|
+
* @default 'default'
|
|
59
|
+
*/
|
|
60
|
+
size?: DsSegmentGroupSize;
|
|
61
|
+
} & (
|
|
56
62
|
| {
|
|
57
63
|
/**
|
|
58
64
|
* The segment group item content
|
|
@@ -78,4 +84,13 @@ export type DsSegmentGroupItemProps = Pick<
|
|
|
78
84
|
/**
|
|
79
85
|
* Props for the DsSegmentGroup ItemText component
|
|
80
86
|
*/
|
|
81
|
-
export type DsSegmentGroupItemTextProps = Pick<
|
|
87
|
+
export type DsSegmentGroupItemTextProps = Pick<
|
|
88
|
+
SegmentGroupItemTextProps,
|
|
89
|
+
'className' | 'style' | 'children'
|
|
90
|
+
> & {
|
|
91
|
+
/**
|
|
92
|
+
* Size variant of the item text
|
|
93
|
+
* @default 'default'
|
|
94
|
+
*/
|
|
95
|
+
size?: DsSegmentGroupSize;
|
|
96
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=42657-2125
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-segment-group
|
|
3
|
+
// component=DsSegmentGroup.Item
|
|
4
|
+
//
|
|
5
|
+
// Figma `Part_SegmentItemV2` maps onto `DsSegmentGroup.Item` (see
|
|
6
|
+
// ds-segment-group-item.figma.ts for the V1 part). Hover / pressed / focus are
|
|
7
|
+
// visual-only and have no matching props.
|
|
8
|
+
import figma from 'figma';
|
|
9
|
+
|
|
10
|
+
const instance = figma.selectedInstance;
|
|
11
|
+
|
|
12
|
+
const state = instance.getEnum('state', {
|
|
13
|
+
default: 'default',
|
|
14
|
+
hover: 'hover',
|
|
15
|
+
pressed: 'pressed',
|
|
16
|
+
focus: 'focus',
|
|
17
|
+
disabled: 'disabled',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const structure = instance.findInstance('Structure_SegmentItemV2', { traverseInstances: true });
|
|
21
|
+
|
|
22
|
+
const iconOnly =
|
|
23
|
+
structure.type === 'INSTANCE' ? structure.getEnum('icon-only', { true: true, false: false }) : false;
|
|
24
|
+
const withIcon = structure.type === 'INSTANCE' ? structure.getBoolean('withIcon') : false;
|
|
25
|
+
|
|
26
|
+
const mediumLabel = structure.type === 'INSTANCE' ? structure.findText('SegBtn') : undefined;
|
|
27
|
+
const smallLabel = structure.type === 'INSTANCE' ? structure.findText('Button') : undefined;
|
|
28
|
+
const label =
|
|
29
|
+
mediumLabel?.type === 'TEXT'
|
|
30
|
+
? mediumLabel.textContent
|
|
31
|
+
: smallLabel?.type === 'TEXT'
|
|
32
|
+
? smallLabel.textContent
|
|
33
|
+
: 'Option';
|
|
34
|
+
|
|
35
|
+
const toIconName = (name: string): string =>
|
|
36
|
+
name
|
|
37
|
+
.replace(/^DAP_DAP_/, 'DAP_')
|
|
38
|
+
.replace(/^DAP_GM_[OS]_/, '')
|
|
39
|
+
.replace(/^DAP_outline_icon\//, '')
|
|
40
|
+
.replace(/^DAP_/, '')
|
|
41
|
+
.replace(/^\/+/, '')
|
|
42
|
+
.replace(/[\s/]+/g, '_')
|
|
43
|
+
.toLowerCase();
|
|
44
|
+
|
|
45
|
+
const iconNameFromCodeConnectId = (id: string | null): string | undefined => {
|
|
46
|
+
if (!id) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
if (id.startsWith('ds-icon-o-')) {
|
|
50
|
+
return id.slice('ds-icon-o-'.length);
|
|
51
|
+
}
|
|
52
|
+
if (id.startsWith('ds-icon-s-')) {
|
|
53
|
+
return id.slice('ds-icon-s-'.length);
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const resolveIcon = (iconInstance: figma.InstanceHandle): string | undefined => {
|
|
59
|
+
const fromCcId = iconNameFromCodeConnectId(iconInstance.codeConnectId());
|
|
60
|
+
if (fromCcId) {
|
|
61
|
+
return fromCcId;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const iconFromMetadata = iconInstance.executeTemplate().metadata?.props?.icon;
|
|
65
|
+
if (typeof iconFromMetadata === 'string') {
|
|
66
|
+
return iconFromMetadata;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return toIconName(iconInstance.name);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const iconNode =
|
|
73
|
+
structure.type === 'INSTANCE'
|
|
74
|
+
? structure
|
|
75
|
+
.findConnectedInstances((node) => node.codeConnectId()?.startsWith('ds-icon-') ?? false, {
|
|
76
|
+
traverseInstances: true,
|
|
77
|
+
})
|
|
78
|
+
.find((node): node is figma.InstanceHandle => node.type === 'INSTANCE')
|
|
79
|
+
: undefined;
|
|
80
|
+
const icon = iconNode ? resolveIcon(iconNode) : undefined;
|
|
81
|
+
|
|
82
|
+
const slug = label
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
85
|
+
.replace(/^-+|-+$/g, '');
|
|
86
|
+
const value = slug || 'option';
|
|
87
|
+
|
|
88
|
+
const disabledAttr = state === 'disabled' ? ' disabled' : '';
|
|
89
|
+
const showIcon = Boolean(icon && (iconOnly || withIcon));
|
|
90
|
+
|
|
91
|
+
export default {
|
|
92
|
+
example:
|
|
93
|
+
iconOnly && icon
|
|
94
|
+
? figma.code`<DsSegmentGroup.Item value="${value}" aria-label="${label}"${disabledAttr}>
|
|
95
|
+
<DsIcon icon="${icon}" size="tiny" color="main" />
|
|
96
|
+
</DsSegmentGroup.Item>`
|
|
97
|
+
: showIcon && icon
|
|
98
|
+
? figma.code`<DsSegmentGroup.Item value="${value}"${disabledAttr}>
|
|
99
|
+
<DsIcon icon="${icon}" size="tiny" color="main" />
|
|
100
|
+
<DsSegmentGroup.ItemText>${label}</DsSegmentGroup.ItemText>
|
|
101
|
+
</DsSegmentGroup.Item>`
|
|
102
|
+
: figma.code`<DsSegmentGroup.Item value="${value}" label="${label}"${disabledAttr} />`,
|
|
103
|
+
imports:
|
|
104
|
+
showIcon || iconOnly
|
|
105
|
+
? ["import { DsIcon, DsSegmentGroup } from '@drivenets/design-system';"]
|
|
106
|
+
: ["import { DsSegmentGroup } from '@drivenets/design-system';"],
|
|
107
|
+
id: 'ds-segment-group-v2-item',
|
|
108
|
+
metadata: { nestable: true },
|
|
109
|
+
} satisfies figma.Template;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=42663-2224
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-segment-group
|
|
3
|
+
// component=DsSegmentGroup.Root
|
|
4
|
+
//
|
|
5
|
+
// Figma ships `DsSegmentGroupV2` alongside `DsSegmentGroupV1`. Both map onto the
|
|
6
|
+
// same code component (see ds-segment-group.figma.ts for the older set).
|
|
7
|
+
import figma from 'figma';
|
|
8
|
+
|
|
9
|
+
const instance = figma.selectedInstance;
|
|
10
|
+
|
|
11
|
+
const size = instance.getEnum('Size', {
|
|
12
|
+
medium: 'medium',
|
|
13
|
+
small: 'small',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const sizeAttr = size === 'small' ? ' size="small"' : '';
|
|
17
|
+
|
|
18
|
+
// `Slot` is homogeneous `Part_SegmentItemV2` instances. Interpolating the slot
|
|
19
|
+
// object itself renders a clickable "Slot" label; execute each child's template
|
|
20
|
+
// so the parent snippet inline `DsSegmentGroup.Item`.
|
|
21
|
+
const slot = instance.getSlot('Slot');
|
|
22
|
+
const items = (slot?.connectedInstances ?? []).map((item) => item.executeTemplate().example);
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
example: figma.code`<DsSegmentGroup.Root${sizeAttr}>
|
|
26
|
+
${items}
|
|
27
|
+
</DsSegmentGroup.Root>`,
|
|
28
|
+
imports: ["import { DsSegmentGroup } from '@drivenets/design-system';"],
|
|
29
|
+
id: 'ds-segment-group-v2',
|
|
30
|
+
metadata: { nestable: false },
|
|
31
|
+
} satisfies figma.Template;
|
|
@@ -7,7 +7,18 @@
|
|
|
7
7
|
// is a single variant backed by an internal, non-exposed include/exclude sub-toggle, so it can
|
|
8
8
|
// only be surfaced as one code value — it maps to `variant="include"`; switch to `variant="exclude"`
|
|
9
9
|
// for the exclude case. `label` is content, pulled from the tag text when present, otherwise a
|
|
10
|
-
// placeholder; `value` (required
|
|
10
|
+
// placeholder; `value` (required by `key-value`, `operator-filter` and `query-filter`) is emitted
|
|
11
|
+
// as a placeholder.
|
|
12
|
+
//
|
|
13
|
+
// Three code props are deliberately not derived from this node:
|
|
14
|
+
// - `shape` — the pill radius is not modelled in Figma at all, so `shape="round"` is a code-side
|
|
15
|
+
// choice.
|
|
16
|
+
// - `warning` / `onExpandClick` — the warning ellipse and `Part_expandCollapseBtn_v01` are hidden
|
|
17
|
+
// layers in every variant of this node, so the emitted example leaves both off; add them in code
|
|
18
|
+
// when the design that consumes the tag shows them.
|
|
19
|
+
//
|
|
20
|
+
// `operator-filter` nests a `DsButtonV4` trigger that does not exist in code. The trigger is a
|
|
21
|
+
// consumer-owned slot, so the example emits a `DsSelect` placeholder for the developer to wire.
|
|
11
22
|
import figma from 'figma';
|
|
12
23
|
|
|
13
24
|
const instance = figma.selectedInstance;
|
|
@@ -17,6 +28,8 @@ const variant =
|
|
|
17
28
|
default: 'default',
|
|
18
29
|
'include-exclude': 'include',
|
|
19
30
|
'key-value': 'key-value',
|
|
31
|
+
'operator-filter': 'operator-filter',
|
|
32
|
+
'query-filter': 'query-filter',
|
|
20
33
|
}) ?? 'default';
|
|
21
34
|
|
|
22
35
|
const size = instance.getEnum('Size', { medium: 'medium', small: 'small' }) ?? 'medium';
|
|
@@ -32,10 +45,13 @@ const state = instance.getEnum('State', {
|
|
|
32
45
|
const labelNode = instance.findText('Tag-name', { traverseInstances: true });
|
|
33
46
|
const label = labelNode.type === 'TEXT' ? labelNode.textContent : 'Label';
|
|
34
47
|
|
|
48
|
+
const requiresValue = variant === 'key-value' || variant === 'operator-filter' || variant === 'query-filter';
|
|
49
|
+
|
|
35
50
|
const attrs = [
|
|
36
51
|
`label="${label}"`,
|
|
37
52
|
variant !== 'default' ? `variant="${variant}"` : '',
|
|
38
|
-
|
|
53
|
+
requiresValue ? 'value="Value"' : '',
|
|
54
|
+
variant === 'operator-filter' ? 'slots={{ operator: <DsSelect options={[]} value="" /> }}' : '',
|
|
39
55
|
size === 'small' ? 'size="small"' : '',
|
|
40
56
|
state === 'selected' ? 'selected' : '',
|
|
41
57
|
state === 'disabled' ? 'disabled' : '',
|
|
@@ -45,7 +61,11 @@ const attrs = [
|
|
|
45
61
|
|
|
46
62
|
export default {
|
|
47
63
|
example: figma.code`<DsTag ${attrs} />`,
|
|
48
|
-
imports: [
|
|
64
|
+
imports: [
|
|
65
|
+
variant === 'operator-filter'
|
|
66
|
+
? "import { DsSelect, DsTag } from '@drivenets/design-system';"
|
|
67
|
+
: "import { DsTag } from '@drivenets/design-system';",
|
|
68
|
+
],
|
|
49
69
|
id: 'ds-tag',
|
|
50
70
|
metadata: { nestable: true },
|
|
51
71
|
} satisfies figma.Template;
|