@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.
Files changed (110) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/index.cjs +12409 -10548
  3. package/dist/index.d.cts +678 -312
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +679 -313
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +12404 -10550
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +29 -29
  11. package/src/components/ds-checkbox/ds-checkbox.figma.ts +3 -0
  12. package/src/components/ds-checkbox/ds-checkbox.module.scss +102 -20
  13. package/src/components/ds-checkbox/ds-checkbox.tsx +30 -2
  14. package/src/components/ds-checkbox/ds-checkbox.types.ts +10 -0
  15. package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.module.scss +169 -0
  16. package/src/components/ds-code-input/collapsed-code-field/collapsed-code-field.tsx +92 -0
  17. package/src/components/ds-code-input/collapsed-code-field/index.ts +1 -0
  18. package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.module.scss +110 -0
  19. package/src/components/ds-code-input/ds-code-input-panel/ds-code-input-panel.tsx +61 -0
  20. package/src/components/ds-code-input/ds-code-input-panel/index.ts +1 -0
  21. package/src/components/ds-code-input/ds-code-input.module.scss +25 -0
  22. package/src/components/ds-code-input/ds-code-input.tsx +401 -0
  23. package/src/components/ds-code-input/ds-code-input.types.ts +166 -0
  24. package/src/components/ds-code-input/ds-code-input.utils.ts +94 -0
  25. package/src/components/ds-code-input/ds-code-input.utils.unit.test.ts +166 -0
  26. package/src/components/ds-code-input/index.ts +2 -0
  27. package/src/components/ds-code-input/use-collapsed-viewport.ts +289 -0
  28. package/src/components/ds-code-input/use-editor-host.ts +87 -0
  29. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.ts +11 -0
  30. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/ds-filters-bar-builder.types.ts +41 -0
  31. package/src/components/ds-filters-bar/components/ds-filters-bar-builder/index.ts +1 -0
  32. package/src/components/ds-filters-bar/components/ds-filters-bar-clear-all.ts +11 -0
  33. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.ts +11 -0
  34. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/ds-filters-bar-conditions.types.ts +30 -0
  35. package/src/components/ds-filters-bar/components/ds-filters-bar-conditions/index.ts +1 -0
  36. package/src/components/ds-filters-bar/components/ds-filters-bar-pinned.ts +31 -0
  37. package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.ts +11 -0
  38. package/src/components/ds-filters-bar/components/ds-filters-bar-query/ds-filters-bar-query.types.ts +22 -0
  39. package/src/components/ds-filters-bar/components/ds-filters-bar-query/index.ts +1 -0
  40. package/src/components/ds-filters-bar/components/ds-filters-bar-saved-filters.ts +19 -0
  41. package/src/components/ds-filters-bar/components/ds-filters-bar-search.ts +11 -0
  42. package/src/components/ds-filters-bar/components/ds-filters-bar-summary.ts +11 -0
  43. package/src/components/ds-filters-bar/components/ds-filters-bar-toolbar.ts +11 -0
  44. package/src/components/ds-filters-bar/components/ds-filters-bar-view-switch.ts +11 -0
  45. package/src/components/ds-filters-bar/components/ds-filters-bar-view.ts +11 -0
  46. package/src/components/ds-filters-bar/components/index.ts +11 -0
  47. package/src/components/ds-filters-bar/ds-filters-bar.context.ts +69 -0
  48. package/src/components/ds-filters-bar/ds-filters-bar.tsx +120 -0
  49. package/src/components/ds-filters-bar/ds-filters-bar.types.ts +440 -0
  50. package/src/components/ds-filters-bar/ds-filters-bar.unit.test.ts +216 -0
  51. package/src/components/ds-filters-bar/ds-filters-bar.utils.ts +141 -0
  52. package/src/components/ds-filters-bar/index.ts +1 -0
  53. package/src/components/ds-form-control/ds-form-control.module.scss +18 -0
  54. package/src/components/ds-form-control/ds-form-control.tsx +43 -9
  55. package/src/components/ds-form-control/ds-form-control.types.ts +2 -1
  56. package/src/components/ds-icon/custom-icons/index.ts +2 -0
  57. package/src/components/ds-icon/custom-icons/special-adv-filters.tsx +28 -0
  58. package/src/components/ds-pin-toggle/ds-pin-toggle.figma.ts +27 -0
  59. package/src/components/ds-pin-toggle/ds-pin-toggle.module.scss +41 -0
  60. package/src/components/ds-pin-toggle/ds-pin-toggle.tsx +43 -0
  61. package/src/components/ds-pin-toggle/ds-pin-toggle.types.ts +29 -0
  62. package/src/components/ds-pin-toggle/index.ts +2 -0
  63. package/src/components/ds-popover/components/popover-trigger/index.ts +1 -0
  64. package/src/components/ds-popover/components/popover-trigger/popover-trigger.tsx +15 -0
  65. package/src/components/ds-popover/ds-popover.context.ts +18 -0
  66. package/src/components/ds-popover/ds-popover.hover-intent.ts +65 -0
  67. package/src/components/ds-popover/ds-popover.tsx +163 -41
  68. package/src/components/ds-popover/ds-popover.types.ts +59 -1
  69. package/src/components/ds-popover/ds-popover.utils.ts +60 -0
  70. package/src/components/ds-popover/ds-popover.utils.unit.test.ts +25 -0
  71. package/src/components/ds-saved-filters/components/ds-saved-filters-delete-confirm.tsx +53 -0
  72. package/src/components/ds-saved-filters/components/ds-saved-filters-name-entry.tsx +64 -0
  73. package/src/components/ds-saved-filters/components/ds-saved-filters-name-modal.tsx +50 -0
  74. package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.module.scss +68 -0
  75. package/src/components/ds-saved-filters/components/ds-saved-filters-row/ds-saved-filters-row.tsx +83 -0
  76. package/src/components/ds-saved-filters/components/ds-saved-filters-row/index.ts +1 -0
  77. package/src/components/ds-saved-filters/components/ds-saved-filters-save.tsx +164 -0
  78. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.module.scss +21 -0
  79. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/ds-saved-filters-trigger.tsx +237 -0
  80. package/src/components/ds-saved-filters/components/ds-saved-filters-trigger/index.ts +1 -0
  81. package/src/components/ds-saved-filters/components/index.ts +2 -0
  82. package/src/components/ds-saved-filters/ds-saved-filters.ts +6 -0
  83. package/src/components/ds-saved-filters/ds-saved-filters.types.ts +106 -0
  84. package/src/components/ds-saved-filters/ds-saved-filters.utils.ts +35 -0
  85. package/src/components/ds-saved-filters/hooks/index.ts +1 -0
  86. package/src/components/ds-saved-filters/hooks/use-picker-outside-dismiss.ts +42 -0
  87. package/src/components/ds-saved-filters/index.ts +2 -0
  88. package/src/components/ds-segment-group/ds-segment-group.module.scss +65 -37
  89. package/src/components/ds-segment-group/ds-segment-group.tsx +17 -5
  90. package/src/components/ds-segment-group/ds-segment-group.types.ts +20 -5
  91. package/src/components/ds-segment-group/figma/ds-segment-group-v2-item.figma.ts +109 -0
  92. package/src/components/ds-segment-group/figma/ds-segment-group-v2.figma.ts +31 -0
  93. package/src/components/ds-tag/ds-tag.figma.ts +23 -3
  94. package/src/components/ds-tag/ds-tag.module.scss +71 -7
  95. package/src/components/ds-tag/ds-tag.tsx +115 -14
  96. package/src/components/ds-tag/ds-tag.types.ts +70 -3
  97. package/src/components/ds-tag/ds-tag.unit.test.ts +23 -4
  98. package/src/components/ds-tag/index.ts +2 -2
  99. package/src/components/ds-text-input/ds-input-field-v02.figma.ts +2 -0
  100. package/src/components/ds-toggle/ds-toggle.tsx +19 -15
  101. package/src/components/ds-toggle/ds-toggle.types.ts +6 -0
  102. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.module.scss +69 -0
  103. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.tsx +55 -0
  104. package/src/components/ds-toggle-filter-data/ds-toggle-filter-data.types.ts +43 -0
  105. package/src/components/ds-toggle-filter-data/index.ts +1 -0
  106. package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -0
  107. package/src/components/ds-tooltip/ds-tooltip.tsx +1 -1
  108. package/src/components/ds-tree/ds-tree.tsx +10 -10
  109. package/src/components/ds-tree/ds-tree.types.ts +40 -7
  110. 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
+ };
@@ -0,0 +1,2 @@
1
+ export { DsSavedFilters } from './ds-saved-filters';
2
+ export type { DsSavedFiltersSaveProps, DsSavedFiltersTriggerProps } from './ds-saved-filters.types';
@@ -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: 4px;
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
- padding: var(--4xs) var(--xs);
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
- padding: var(--3xs) var(--sm);
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
- border-radius: 4px;
33
-
34
- &[data-state='checked'] {
35
- .segmentItemText {
36
- color: var(--font-main);
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
- .segmentItemText {
45
- color: var(--background-action);
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-secondary);
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-contrast);
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
- background: var(--background-secondary);
89
+ color: var(--font-disabled);
61
90
 
62
- .segmentItemText {
63
- color: var(--font-disabled);
91
+ &[data-state='unchecked'] {
92
+ background: transparent;
64
93
  }
65
- }
66
94
 
67
- &:focus-visible {
68
- outline: 2px solid var(--border-action-secondary);
69
- outline-offset: 2px;
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 150ms ease-in-out;
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> = ({ value, label, className, style, children, ...props }) => {
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> = ({ className, children, ...props }) => (
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="body-sm-reg">{children}</DsTypography>
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 type DsSegmentGroupSize = 'small' | 'default';
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<SegmentGroupItemTextProps, 'className' | 'style' | 'children'>;
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 only for `key-value`) is emitted as a placeholder.
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
- variant === 'key-value' ? 'value="Value"' : '',
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: ["import { DsTag } from '@drivenets/design-system';"],
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;