@drivenets/design-system 0.0.1
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 +9 -0
- package/LICENSE +201 -0
- package/dist/index.cjs +5394 -0
- package/dist/index.css +1 -0
- package/dist/index.d.cts +9157 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +9157 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5335 -0
- package/dist/index.js.map +1 -0
- package/package.json +104 -0
- package/src/components/ds-alert-banner/ds-alert-banner.module.scss +116 -0
- package/src/components/ds-alert-banner/ds-alert-banner.tsx +115 -0
- package/src/components/ds-alert-banner/ds-alert-banner.types.tsx +47 -0
- package/src/components/ds-alert-banner/index.tsx +2 -0
- package/src/components/ds-breadcrumb/ds-breadcrumb.module.scss +99 -0
- package/src/components/ds-breadcrumb/ds-breadcrumb.tsx +94 -0
- package/src/components/ds-breadcrumb/ds-breadcrumb.types.tsx +32 -0
- package/src/components/ds-breadcrumb/index.tsx +2 -0
- package/src/components/ds-button/ds-button.tsx +12 -0
- package/src/components/ds-button/ds-button.types.ts +6 -0
- package/src/components/ds-button/index.ts +2 -0
- package/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.module.scss +257 -0
- package/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx +34 -0
- package/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.types.ts +41 -0
- package/src/components/ds-button/versions/ds-button-legacy/index.ts +2 -0
- package/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +580 -0
- package/src/components/ds-button/versions/ds-button-new/ds-button-new.tsx +49 -0
- package/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts +41 -0
- package/src/components/ds-button/versions/ds-button-new/index.ts +2 -0
- package/src/components/ds-checkbox/ds-checkbox.module.scss +130 -0
- package/src/components/ds-checkbox/ds-checkbox.tsx +55 -0
- package/src/components/ds-checkbox/ds-checkbox.types.tsx +27 -0
- package/src/components/ds-checkbox/index.tsx +2 -0
- package/src/components/ds-chip/ds-chip.module.scss +71 -0
- package/src/components/ds-chip/ds-chip.tsx +73 -0
- package/src/components/ds-chip/ds-chip.types.tsx +46 -0
- package/src/components/ds-chip/index.tsx +2 -0
- package/src/components/ds-chip-group/ds-chip-group.module.scss +59 -0
- package/src/components/ds-chip-group/ds-chip-group.tsx +122 -0
- package/src/components/ds-chip-group/ds-chip-group.types.ts +52 -0
- package/src/components/ds-chip-group/hooks/use-chip-row-calculation.ts +76 -0
- package/src/components/ds-chip-group/index.ts +2 -0
- package/src/components/ds-confirmation/ds-confirmation.module.scss +58 -0
- package/src/components/ds-confirmation/ds-confirmation.tsx +121 -0
- package/src/components/ds-confirmation/ds-confirmation.types.tsx +28 -0
- package/src/components/ds-confirmation/index.ts +2 -0
- package/src/components/ds-dialog/ds-dialog.module.scss +50 -0
- package/src/components/ds-dialog/ds-dialog.tsx +70 -0
- package/src/components/ds-dialog/ds-dialog.types.tsx +48 -0
- package/src/components/ds-dialog/index.tsx +2 -0
- package/src/components/ds-drawer/ds-drawer.module.scss +212 -0
- package/src/components/ds-drawer/ds-drawer.tsx +160 -0
- package/src/components/ds-drawer/ds-drawer.types.tsx +54 -0
- package/src/components/ds-drawer/index.ts +2 -0
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss +123 -0
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +396 -0
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.types.tsx +262 -0
- package/src/components/ds-dropdown-menu/index.tsx +2 -0
- package/src/components/ds-expandable-text-input/ds-expandable-text-input.module.scss +70 -0
- package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +106 -0
- package/src/components/ds-expandable-text-input/ds-expandable-text-input.types.tsx +19 -0
- package/src/components/ds-expandable-text-input/index.ts +2 -0
- package/src/components/ds-file-upload/components/file-upload/file-upload.module.scss +90 -0
- package/src/components/ds-file-upload/components/file-upload/file-upload.tsx +106 -0
- package/src/components/ds-file-upload/components/file-upload/file-upload.types.tsx +94 -0
- package/src/components/ds-file-upload/components/file-upload/index.ts +2 -0
- package/src/components/ds-file-upload/components/file-upload-item/file-upload-item.module.scss +125 -0
- package/src/components/ds-file-upload/components/file-upload-item/file-upload-item.tsx +143 -0
- package/src/components/ds-file-upload/components/file-upload-item/file-upload-item.types.tsx +26 -0
- package/src/components/ds-file-upload/components/file-upload-item/index.ts +2 -0
- package/src/components/ds-file-upload/ds-file-upload-api.types.ts +38 -0
- package/src/components/ds-file-upload/ds-file-upload.tsx +46 -0
- package/src/components/ds-file-upload/ds-file-upload.types.tsx +54 -0
- package/src/components/ds-file-upload/errors/file-upload-errors.ts +27 -0
- package/src/components/ds-file-upload/hooks/index.ts +2 -0
- package/src/components/ds-file-upload/hooks/use-file-upload.ts +350 -0
- package/src/components/ds-file-upload/index.ts +7 -0
- package/src/components/ds-file-upload/types/accept-types.ts +106 -0
- package/src/components/ds-file-upload/utils/accept-utils.ts +25 -0
- package/src/components/ds-file-upload/utils/constants.ts +21 -0
- package/src/components/ds-file-upload/utils/file-factory.ts +18 -0
- package/src/components/ds-file-upload/utils/format-file-size.ts +15 -0
- package/src/components/ds-file-upload/utils/generate-helper-text.ts +39 -0
- package/src/components/ds-file-upload/utils/get-error-message.ts +17 -0
- package/src/components/ds-file-upload/utils/is-file-equal.ts +8 -0
- package/src/components/ds-form-control/ds-form-control.module.scss +135 -0
- package/src/components/ds-form-control/ds-form-control.tsx +94 -0
- package/src/components/ds-form-control/ds-form-control.types.tsx +58 -0
- package/src/components/ds-form-control/index.tsx +2 -0
- package/src/components/ds-grid/ds-grid.module.scss +0 -0
- package/src/components/ds-grid/ds-grid.tsx +43 -0
- package/src/components/ds-grid/ds-grid.types.ts +55 -0
- package/src/components/ds-grid/index.ts +2 -0
- package/src/components/ds-icon/ds-icon.module.scss +25 -0
- package/src/components/ds-icon/ds-icon.tsx +33 -0
- package/src/components/ds-icon/ds-icon.types.ts +53 -0
- package/src/components/ds-icon/index.ts +2 -0
- package/src/components/ds-icon/material-icons.ts +6203 -0
- package/src/components/ds-modal/ds-modal.module.scss +100 -0
- package/src/components/ds-modal/ds-modal.tsx +136 -0
- package/src/components/ds-modal/ds-modal.types.tsx +44 -0
- package/src/components/ds-modal/index.tsx +2 -0
- package/src/components/ds-number-input/ds-number-input.module.scss +15 -0
- package/src/components/ds-number-input/ds-number-input.tsx +74 -0
- package/src/components/ds-number-input/ds-number-input.types.tsx +76 -0
- package/src/components/ds-number-input/index.tsx +2 -0
- package/src/components/ds-panel/ds-panel.module.scss +95 -0
- package/src/components/ds-panel/ds-panel.tsx +46 -0
- package/src/components/ds-panel/ds-panel.types.ts +17 -0
- package/src/components/ds-panel/index.tsx +2 -0
- package/src/components/ds-password-input/ds-password-input.module.scss +10 -0
- package/src/components/ds-password-input/ds-password-input.tsx +67 -0
- package/src/components/ds-password-input/ds-password-input.types.tsx +62 -0
- package/src/components/ds-password-input/index.tsx +2 -0
- package/src/components/ds-popover/ds-popover.module.scss +23 -0
- package/src/components/ds-popover/ds-popover.tsx +33 -0
- package/src/components/ds-popover/ds-popover.types.tsx +24 -0
- package/src/components/ds-popover/index.tsx +2 -0
- package/src/components/ds-radio-group/ds-radio-group.module.scss +138 -0
- package/src/components/ds-radio-group/ds-radio-group.tsx +115 -0
- package/src/components/ds-radio-group/ds-radio-group.types.tsx +93 -0
- package/src/components/ds-radio-group/index.tsx +2 -0
- package/src/components/ds-select/ds-select.module.scss +66 -0
- package/src/components/ds-select/ds-select.tsx +239 -0
- package/src/components/ds-select/ds-select.types.tsx +80 -0
- package/src/components/ds-select/index.tsx +2 -0
- package/src/components/ds-select/select-items-chips.tsx +56 -0
- package/src/components/ds-smart-tabs/ds-smart-tabs.module.scss +69 -0
- package/src/components/ds-smart-tabs/ds-smart-tabs.tsx +113 -0
- package/src/components/ds-smart-tabs/ds-smart-tabs.types.tsx +74 -0
- package/src/components/ds-smart-tabs/index.tsx +2 -0
- package/src/components/ds-spinner/ds-spinner.module.scss +39 -0
- package/src/components/ds-spinner/ds-spinner.tsx +51 -0
- package/src/components/ds-spinner/ds-spinner.types.tsx +20 -0
- package/src/components/ds-spinner/index.tsx +2 -0
- package/src/components/ds-status-badge/ds-status-badge.module.scss +79 -0
- package/src/components/ds-status-badge/ds-status-badge.tsx +42 -0
- package/src/components/ds-status-badge/ds-status-badge.types.tsx +46 -0
- package/src/components/ds-status-badge/index.tsx +2 -0
- package/src/components/ds-stepper/components/ds-step-content.tsx +32 -0
- package/src/components/ds-stepper/components/ds-step-description.tsx +6 -0
- package/src/components/ds-stepper/components/ds-step-label.tsx +6 -0
- package/src/components/ds-stepper/components/ds-step-next-button.tsx +13 -0
- package/src/components/ds-stepper/components/ds-step.tsx +59 -0
- package/src/components/ds-stepper/ds-stepper.module.scss +134 -0
- package/src/components/ds-stepper/ds-stepper.tsx +34 -0
- package/src/components/ds-stepper/ds-stepper.types.ts +12 -0
- package/src/components/ds-stepper/hooks/use-stepper.ts +20 -0
- package/src/components/ds-stepper/hooks/use-steps.ts +18 -0
- package/src/components/ds-stepper/index.tsx +6 -0
- package/src/components/ds-system-status/ds-system-status.module.scss +87 -0
- package/src/components/ds-system-status/ds-system-status.tsx +33 -0
- package/src/components/ds-system-status/ds-system-status.types.tsx +37 -0
- package/src/components/ds-system-status/index.tsx +10 -0
- package/src/components/ds-table/components/core-table/core-table.module.scss +32 -0
- package/src/components/ds-table/components/core-table/core-table.tsx +83 -0
- package/src/components/ds-table/components/core-table/index.ts +1 -0
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +115 -0
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.tsx +70 -0
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.types.ts +41 -0
- package/src/components/ds-table/components/ds-table-bulk-actions/index.ts +2 -0
- package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +53 -0
- package/src/components/ds-table/components/ds-table-cell/ds-table-cell.tsx +95 -0
- package/src/components/ds-table/components/ds-table-cell/ds-table-cell.types.ts +67 -0
- package/src/components/ds-table/components/ds-table-cell/index.ts +2 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +81 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +99 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.types.ts +11 -0
- package/src/components/ds-table/components/ds-table-header/index.ts +2 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +137 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +205 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.types.ts +22 -0
- package/src/components/ds-table/components/ds-table-row/index.ts +2 -0
- package/src/components/ds-table/context/ds-table-context.tsx +39 -0
- package/src/components/ds-table/ds-table.module.scss +60 -0
- package/src/components/ds-table/ds-table.tsx +312 -0
- package/src/components/ds-table/ds-table.types.ts +377 -0
- package/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx +116 -0
- package/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx +171 -0
- package/src/components/ds-table/filters/adapters/create-filter-adapter.ts +131 -0
- package/src/components/ds-table/filters/adapters/index.ts +12 -0
- package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.tsx +43 -0
- package/src/components/ds-table/filters/components/checkbox-filter/index.ts +2 -0
- package/src/components/ds-table/filters/components/range-filter/index.ts +2 -0
- package/src/components/ds-table/filters/components/range-filter/range-filter.module.scss +25 -0
- package/src/components/ds-table/filters/components/range-filter/range-filter.tsx +79 -0
- package/src/components/ds-table/filters/hooks/index.ts +1 -0
- package/src/components/ds-table/filters/hooks/use-table-filters.ts +234 -0
- package/src/components/ds-table/filters/index.ts +50 -0
- package/src/components/ds-table/filters/types/filter-adapter.types.ts +111 -0
- package/src/components/ds-table/hooks/use-drag-and-drop.tsx +107 -0
- package/src/components/ds-table/index.ts +4 -0
- package/src/components/ds-table/styles/_variables.scss +17 -0
- package/src/components/ds-table/styles/shared/ds-table-shared.module.scss +16 -0
- package/src/components/ds-table/utils/column-styling.ts +30 -0
- package/src/components/ds-table/utils/create-columns-grid-template.ts +23 -0
- package/src/components/ds-table/utils/merge-refs.ts +15 -0
- package/src/components/ds-table/utils/story-data-generator.ts +91 -0
- package/src/components/ds-text-input/ds-text-input.module.scss +27 -0
- package/src/components/ds-text-input/ds-text-input.tsx +75 -0
- package/src/components/ds-text-input/ds-text-input.types.tsx +110 -0
- package/src/components/ds-text-input/index.ts +2 -0
- package/src/components/ds-textarea/ds-textarea.module.scss +5 -0
- package/src/components/ds-textarea/ds-textarea.tsx +49 -0
- package/src/components/ds-textarea/ds-textarea.types.tsx +67 -0
- package/src/components/ds-textarea/index.tsx +2 -0
- package/src/components/ds-toast/ds-toast-context.tsx +79 -0
- package/src/components/ds-toast/ds-toast.module.scss +134 -0
- package/src/components/ds-toast/ds-toast.tsx +54 -0
- package/src/components/ds-toast/ds-toast.types.tsx +53 -0
- package/src/components/ds-toast/index.ts +3 -0
- package/src/components/ds-tooltip/ds-tooltip.module.scss +19 -0
- package/src/components/ds-tooltip/ds-tooltip.tsx +33 -0
- package/src/components/ds-tooltip/ds-tooltip.types.tsx +12 -0
- package/src/components/ds-tooltip/index.tsx +2 -0
- package/src/components/ds-typography/ds-typography.config.ts +32 -0
- package/src/components/ds-typography/ds-typography.module.scss +93 -0
- package/src/components/ds-typography/ds-typography.tsx +27 -0
- package/src/components/ds-typography/ds-typography.types.tsx +27 -0
- package/src/components/ds-typography/index.tsx +2 -0
- package/src/components/ds-vertical-tabs/ds-vertical-tabs.module.scss +60 -0
- package/src/components/ds-vertical-tabs/ds-vertical-tabs.tsx +80 -0
- package/src/components/ds-vertical-tabs/ds-vertical-tabs.types.ts +69 -0
- package/src/components/ds-vertical-tabs/index.ts +7 -0
- package/src/index.ts +34 -0
- package/src/styles/_colors.scss +65 -0
- package/src/styles/_grid.scss +111 -0
- package/src/styles/_mixin.scss +1 -0
- package/src/styles/_reset.scss +38 -0
- package/src/styles/_root.scss +390 -0
- package/src/styles/_scrollbars.scss +41 -0
- package/src/styles/_spacing.scss +47 -0
- package/src/styles/_typography.scss +301 -0
- package/src/styles/_utilities.scss +33 -0
- package/src/styles/_variables.scss +44 -0
- package/src/styles/mixins/_bounding-box.scss +53 -0
- package/src/styles/mixins/_list.scss +74 -0
- package/src/styles/shared/_dropdown.scss +89 -0
- package/src/styles/shared/_input.scss +365 -0
- package/src/styles/shared/_modal.scss +123 -0
- package/src/styles/styles.scss +12 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { createContext, Fragment, useContext, useState } from 'react';
|
|
3
|
+
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
4
|
+
import { Menu } from '@ark-ui/react/menu';
|
|
5
|
+
import { Portal } from '@ark-ui/react/portal';
|
|
6
|
+
import classNames from 'classnames';
|
|
7
|
+
import styles from './ds-dropdown-menu.module.scss';
|
|
8
|
+
import { DsIcon } from '../ds-icon';
|
|
9
|
+
import DsTypography from '../ds-typography/ds-typography';
|
|
10
|
+
import DsTextInput from '../ds-text-input/ds-text-input';
|
|
11
|
+
import type {
|
|
12
|
+
DsDropdownMenuActionsProps,
|
|
13
|
+
DsDropdownMenuContentProps,
|
|
14
|
+
DsDropdownMenuHeaderProps,
|
|
15
|
+
DsDropdownMenuItemGroupContentProps,
|
|
16
|
+
DsDropdownMenuItemGroupLabelProps,
|
|
17
|
+
DsDropdownMenuItemGroupProps,
|
|
18
|
+
DsDropdownMenuItemIndicatorProps,
|
|
19
|
+
DsDropdownMenuItemProps,
|
|
20
|
+
DsDropdownMenuLegacyProps,
|
|
21
|
+
DsDropdownMenuPositioning,
|
|
22
|
+
DsDropdownMenuRootProps,
|
|
23
|
+
DsDropdownMenuSeparatorProps,
|
|
24
|
+
DsDropdownMenuTriggerProps,
|
|
25
|
+
} from './ds-dropdown-menu.types';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Context to share state between Group and GroupLabel
|
|
29
|
+
*/
|
|
30
|
+
interface GroupContextValue {
|
|
31
|
+
collapsed: boolean;
|
|
32
|
+
toggle: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const GroupContext = createContext<GroupContextValue | null>(null);
|
|
36
|
+
|
|
37
|
+
const DEFAULT_POSITIONING: DsDropdownMenuPositioning = {
|
|
38
|
+
placement: 'bottom-start' as const,
|
|
39
|
+
gutter: 4,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Root component - manages dropdown state
|
|
44
|
+
*/
|
|
45
|
+
const Root: React.FC<DsDropdownMenuRootProps> = ({
|
|
46
|
+
open,
|
|
47
|
+
onOpenChange,
|
|
48
|
+
onSelect,
|
|
49
|
+
onHighlightChange,
|
|
50
|
+
positioning: customPositioning,
|
|
51
|
+
preventCloseOnSelect = false,
|
|
52
|
+
children,
|
|
53
|
+
}) => {
|
|
54
|
+
const positioning = {
|
|
55
|
+
...DEFAULT_POSITIONING,
|
|
56
|
+
...customPositioning,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Menu.Root
|
|
61
|
+
open={open}
|
|
62
|
+
onOpenChange={(details) => onOpenChange?.(details.open)}
|
|
63
|
+
onSelect={(details) => onSelect?.(details.value)}
|
|
64
|
+
onHighlightChange={(details) => onHighlightChange?.(details.highlightedValue)}
|
|
65
|
+
positioning={positioning}
|
|
66
|
+
closeOnSelect={!preventCloseOnSelect}
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
</Menu.Root>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Trigger component - wraps the trigger element
|
|
75
|
+
*/
|
|
76
|
+
const Trigger: React.FC<DsDropdownMenuTriggerProps> = Menu.Trigger;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Content component - wraps the dropdown content
|
|
80
|
+
*/
|
|
81
|
+
const Content: React.FC<DsDropdownMenuContentProps> = ({
|
|
82
|
+
disablePortal = false,
|
|
83
|
+
children,
|
|
84
|
+
className,
|
|
85
|
+
style,
|
|
86
|
+
}) => {
|
|
87
|
+
const Wrapper = disablePortal ? Fragment : Portal;
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Wrapper>
|
|
91
|
+
<Menu.Positioner>
|
|
92
|
+
<Menu.Content className={classNames(styles.content, className)} style={style}>
|
|
93
|
+
{children}
|
|
94
|
+
</Menu.Content>
|
|
95
|
+
</Menu.Positioner>
|
|
96
|
+
</Wrapper>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Item component - renders a menu item
|
|
102
|
+
*/
|
|
103
|
+
const Item: React.FC<DsDropdownMenuItemProps> = ({
|
|
104
|
+
disabled,
|
|
105
|
+
selected,
|
|
106
|
+
value,
|
|
107
|
+
asChild,
|
|
108
|
+
onClick,
|
|
109
|
+
onSelect,
|
|
110
|
+
children,
|
|
111
|
+
className,
|
|
112
|
+
style,
|
|
113
|
+
}) => {
|
|
114
|
+
return (
|
|
115
|
+
<Menu.Item
|
|
116
|
+
disabled={disabled}
|
|
117
|
+
className={classNames(
|
|
118
|
+
styles.item,
|
|
119
|
+
{
|
|
120
|
+
[styles.selected]: selected,
|
|
121
|
+
},
|
|
122
|
+
className,
|
|
123
|
+
)}
|
|
124
|
+
style={style}
|
|
125
|
+
value={value}
|
|
126
|
+
asChild={asChild}
|
|
127
|
+
onClick={onClick}
|
|
128
|
+
onSelect={onSelect}
|
|
129
|
+
>
|
|
130
|
+
{children}
|
|
131
|
+
</Menu.Item>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* ItemIndicator component - renders a selection indicator (check icon by default)
|
|
137
|
+
*/
|
|
138
|
+
const ItemIndicator: React.FC<DsDropdownMenuItemIndicatorProps> = ({ children, className, style }) => {
|
|
139
|
+
return (
|
|
140
|
+
<span className={classNames(styles.indicator, className)} style={style}>
|
|
141
|
+
{children ?? <DsIcon icon="check" />}
|
|
142
|
+
</span>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Header component - sticky header container for search, filters, or other controls
|
|
148
|
+
*/
|
|
149
|
+
const Header: React.FC<DsDropdownMenuHeaderProps> = ({ children, className, style }) => {
|
|
150
|
+
return (
|
|
151
|
+
<div className={classNames(styles.header, className)} style={style}>
|
|
152
|
+
{children}
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Actions component - footer container for action buttons
|
|
159
|
+
*/
|
|
160
|
+
const Actions: React.FC<DsDropdownMenuActionsProps> = ({ children, className, style }) => {
|
|
161
|
+
return (
|
|
162
|
+
<div
|
|
163
|
+
className={classNames(styles.actions, className)}
|
|
164
|
+
style={style}
|
|
165
|
+
role="menu"
|
|
166
|
+
aria-label="Menu actions"
|
|
167
|
+
>
|
|
168
|
+
{children}
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* ItemGroup component - collapsible group container
|
|
175
|
+
*/
|
|
176
|
+
const ItemGroup: React.FC<DsDropdownMenuItemGroupProps> = ({
|
|
177
|
+
children,
|
|
178
|
+
collapsed: controlledCollapsed,
|
|
179
|
+
onCollapsedChange,
|
|
180
|
+
className,
|
|
181
|
+
style,
|
|
182
|
+
}) => {
|
|
183
|
+
const [internalCollapsed, setInternalCollapsed] = useState(false);
|
|
184
|
+
|
|
185
|
+
const isControlled = controlledCollapsed !== undefined;
|
|
186
|
+
const collapsed = isControlled ? controlledCollapsed : internalCollapsed;
|
|
187
|
+
|
|
188
|
+
const toggle = () => {
|
|
189
|
+
const newCollapsed = !collapsed;
|
|
190
|
+
if (!isControlled) {
|
|
191
|
+
setInternalCollapsed(newCollapsed);
|
|
192
|
+
}
|
|
193
|
+
onCollapsedChange?.(newCollapsed);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<GroupContext.Provider value={{ collapsed, toggle }}>
|
|
198
|
+
<Menu.ItemGroup className={classNames(styles.group, className)} style={style}>
|
|
199
|
+
{children}
|
|
200
|
+
</Menu.ItemGroup>
|
|
201
|
+
</GroupContext.Provider>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* ItemGroupLabel component - clickable group header with collapse indicator
|
|
207
|
+
*/
|
|
208
|
+
const ItemGroupLabel: React.FC<DsDropdownMenuItemGroupLabelProps> = ({ children, className, style }) => {
|
|
209
|
+
const context = useContext(GroupContext);
|
|
210
|
+
|
|
211
|
+
if (!context) {
|
|
212
|
+
// If not inside a Group, just render static label
|
|
213
|
+
return (
|
|
214
|
+
<Menu.ItemGroupLabel className={classNames(styles.groupLabel, className)} style={style}>
|
|
215
|
+
<DsTypography variant="body-sm-md">{children}</DsTypography>
|
|
216
|
+
</Menu.ItemGroupLabel>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const { collapsed, toggle } = context;
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<Menu.ItemGroupLabel asChild>
|
|
224
|
+
<button
|
|
225
|
+
type="button"
|
|
226
|
+
className={classNames(styles.groupLabel, styles.groupLabelCollapsible, className)}
|
|
227
|
+
style={style}
|
|
228
|
+
onClick={toggle}
|
|
229
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
230
|
+
>
|
|
231
|
+
<DsTypography variant="body-sm-md">{children}</DsTypography>
|
|
232
|
+
<DsIcon
|
|
233
|
+
icon="keyboard_arrow_down"
|
|
234
|
+
className={classNames(styles.groupLabelIcon, {
|
|
235
|
+
[styles.groupLabelIconRotated]: !collapsed,
|
|
236
|
+
})}
|
|
237
|
+
/>
|
|
238
|
+
</button>
|
|
239
|
+
</Menu.ItemGroupLabel>
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* ItemGroupContent component - collapsible content container
|
|
245
|
+
*/
|
|
246
|
+
const ItemGroupContent: React.FC<DsDropdownMenuItemGroupContentProps> = ({ children, className, style }) => {
|
|
247
|
+
const context = useContext(GroupContext);
|
|
248
|
+
|
|
249
|
+
if (context?.collapsed) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<div className={className} style={style}>
|
|
255
|
+
{children}
|
|
256
|
+
</div>
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Separator component - renders a visual divider
|
|
262
|
+
*/
|
|
263
|
+
const Separator: React.FC<DsDropdownMenuSeparatorProps> = ({ className, style }) => {
|
|
264
|
+
return <Menu.Separator className={classNames(styles.separator, className)} style={style} />;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* DEPRECATED: Legacy DsDropdownMenu component with options array
|
|
269
|
+
* Use compound component pattern instead
|
|
270
|
+
* @deprecated
|
|
271
|
+
*/
|
|
272
|
+
export const DsDropdownMenuLegacy: React.FC<DsDropdownMenuLegacyProps> = ({
|
|
273
|
+
options,
|
|
274
|
+
children,
|
|
275
|
+
contentGap = 0,
|
|
276
|
+
className,
|
|
277
|
+
style,
|
|
278
|
+
align = 'center',
|
|
279
|
+
side = 'bottom',
|
|
280
|
+
disablePortal = false,
|
|
281
|
+
disableSearch = true,
|
|
282
|
+
selected,
|
|
283
|
+
onSelect,
|
|
284
|
+
}) => {
|
|
285
|
+
const [open, setOpen] = useState(false);
|
|
286
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
287
|
+
|
|
288
|
+
const Wrapper = disablePortal ? Fragment : DropdownMenu.Portal;
|
|
289
|
+
|
|
290
|
+
const filteredOptions = disableSearch
|
|
291
|
+
? options
|
|
292
|
+
: options.filter((option) => option.label.toLowerCase().includes(searchTerm.toLowerCase()));
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
|
296
|
+
<DropdownMenu.Trigger asChild>{children}</DropdownMenu.Trigger>
|
|
297
|
+
<Wrapper>
|
|
298
|
+
<DropdownMenu.Content
|
|
299
|
+
className={classNames(styles.contentLegacy, styles.viewportLegacy)}
|
|
300
|
+
sideOffset={contentGap}
|
|
301
|
+
align={align}
|
|
302
|
+
side={side}
|
|
303
|
+
>
|
|
304
|
+
{!disableSearch && (
|
|
305
|
+
<DsTextInput
|
|
306
|
+
placeholder="Search"
|
|
307
|
+
value={searchTerm}
|
|
308
|
+
onValueChange={setSearchTerm}
|
|
309
|
+
slots={{
|
|
310
|
+
startAdornment: <DsIcon icon="search" size="tiny" />,
|
|
311
|
+
}}
|
|
312
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
313
|
+
/>
|
|
314
|
+
)}
|
|
315
|
+
{filteredOptions.map((option, i) => (
|
|
316
|
+
<DropdownMenu.Item
|
|
317
|
+
key={i}
|
|
318
|
+
disabled={option.disabled}
|
|
319
|
+
className={classNames(
|
|
320
|
+
styles.itemLegacy,
|
|
321
|
+
{
|
|
322
|
+
[styles.selected]: selected === option.value,
|
|
323
|
+
},
|
|
324
|
+
className,
|
|
325
|
+
)}
|
|
326
|
+
style={style}
|
|
327
|
+
onClick={(e) => {
|
|
328
|
+
e.stopPropagation();
|
|
329
|
+
|
|
330
|
+
if (!option.disabled) {
|
|
331
|
+
if (option.value) {
|
|
332
|
+
onSelect?.(option.value);
|
|
333
|
+
}
|
|
334
|
+
option.onClick?.(e);
|
|
335
|
+
setOpen(false);
|
|
336
|
+
}
|
|
337
|
+
}}
|
|
338
|
+
>
|
|
339
|
+
{option.icon && <DsIcon icon={option.icon} className={styles.itemIcon} />}
|
|
340
|
+
<span className={styles.label}>{option.label}</span>
|
|
341
|
+
{option.value && selected === option.value && (
|
|
342
|
+
<span className={styles.indicator}>
|
|
343
|
+
<DsIcon icon="check" />
|
|
344
|
+
</span>
|
|
345
|
+
)}
|
|
346
|
+
</DropdownMenu.Item>
|
|
347
|
+
))}
|
|
348
|
+
</DropdownMenu.Content>
|
|
349
|
+
</Wrapper>
|
|
350
|
+
</DropdownMenu.Root>
|
|
351
|
+
);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Design system DsDropdownMenu component
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* <DsDropdownMenu.Root>
|
|
359
|
+
* <DsDropdownMenu.Trigger>
|
|
360
|
+
* <button>Open</button>
|
|
361
|
+
* </DsDropdownMenu.Trigger>
|
|
362
|
+
* <DsDropdownMenu.Content>
|
|
363
|
+
* <DsDropdownMenu.Header>
|
|
364
|
+
* <DsTextInput placeholder="Search..." />
|
|
365
|
+
* </DsDropdownMenu.Header>
|
|
366
|
+
* <DsDropdownMenu.Item value="item1">
|
|
367
|
+
* Item 1
|
|
368
|
+
* <DsDropdownMenu.ItemIndicator />
|
|
369
|
+
* </DsDropdownMenu.Item>
|
|
370
|
+
* <DsDropdownMenu.ItemGroup>
|
|
371
|
+
* <DsDropdownMenu.ItemGroupLabel>Group Name</DsDropdownMenu.ItemGroupLabel>
|
|
372
|
+
* <DsDropdownMenu.ItemGroupContent>
|
|
373
|
+
* <DsDropdownMenu.Item value="item2">Item 2</DsDropdownMenu.Item>
|
|
374
|
+
* </DsDropdownMenu.ItemGroupContent>
|
|
375
|
+
* </DsDropdownMenu.ItemGroup>
|
|
376
|
+
* <DsDropdownMenu.Separator />
|
|
377
|
+
* <DsDropdownMenu.Actions>
|
|
378
|
+
* <DsButton>Cancel</DsButton>
|
|
379
|
+
* <DsButton>Apply</DsButton>
|
|
380
|
+
* </DsDropdownMenu.Actions>
|
|
381
|
+
* </DsDropdownMenu.Content>
|
|
382
|
+
* </DsDropdownMenu.Root>
|
|
383
|
+
*/
|
|
384
|
+
export const DsDropdownMenu = {
|
|
385
|
+
Root,
|
|
386
|
+
Trigger,
|
|
387
|
+
Content,
|
|
388
|
+
Item,
|
|
389
|
+
ItemIndicator,
|
|
390
|
+
Header,
|
|
391
|
+
Actions,
|
|
392
|
+
ItemGroup,
|
|
393
|
+
ItemGroupLabel,
|
|
394
|
+
ItemGroupContent,
|
|
395
|
+
Separator,
|
|
396
|
+
};
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import type { CSSProperties, MouseEvent, ReactNode } from 'react';
|
|
2
|
+
import type { Menu } from '@ark-ui/react/menu';
|
|
3
|
+
import type { IconType } from '../ds-icon';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base positioning type
|
|
7
|
+
*/
|
|
8
|
+
type BasePositioning = NonNullable<Menu.RootProps['positioning']>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Positioning options for dropdown menu
|
|
12
|
+
*/
|
|
13
|
+
export type DsDropdownMenuPositioning = Pick<BasePositioning, 'placement' | 'gutter' | 'sameWidth'>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* DEPRECATED: Legacy dropdown menu option configuration
|
|
17
|
+
* Use compound component pattern instead
|
|
18
|
+
* @deprecated
|
|
19
|
+
*/
|
|
20
|
+
export interface DsDropdownMenuOptionLegacy {
|
|
21
|
+
/**
|
|
22
|
+
* Display label for the menu option
|
|
23
|
+
*/
|
|
24
|
+
label: string;
|
|
25
|
+
/**
|
|
26
|
+
* Optional icon to display
|
|
27
|
+
*/
|
|
28
|
+
icon?: IconType;
|
|
29
|
+
/**
|
|
30
|
+
* Whether this option is disabled
|
|
31
|
+
*/
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Click handler for the option
|
|
35
|
+
*/
|
|
36
|
+
onClick?: (e: MouseEvent<HTMLElement>) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Optional value for selection tracking
|
|
39
|
+
*/
|
|
40
|
+
value?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* DEPRECATED: Legacy props for DsDropdownMenuLegacy component
|
|
45
|
+
* Use compound component pattern instead
|
|
46
|
+
* @deprecated
|
|
47
|
+
*/
|
|
48
|
+
export interface DsDropdownMenuLegacyProps {
|
|
49
|
+
/**
|
|
50
|
+
* The options to be displayed in the dropdown menu
|
|
51
|
+
*/
|
|
52
|
+
options: DsDropdownMenuOptionLegacy[];
|
|
53
|
+
/**
|
|
54
|
+
* Optional children to be rendered inside the component
|
|
55
|
+
* Typically used for the trigger element
|
|
56
|
+
*/
|
|
57
|
+
children?: ReactNode | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* The gap between the trigger and dropdown content in pixels
|
|
60
|
+
* @default 0
|
|
61
|
+
*/
|
|
62
|
+
contentGap?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Additional CSS class names
|
|
65
|
+
*/
|
|
66
|
+
className?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Optional inline styles to apply to the component
|
|
69
|
+
*/
|
|
70
|
+
style?: CSSProperties;
|
|
71
|
+
/**
|
|
72
|
+
* The alignment of the dropdown content
|
|
73
|
+
* @default 'center'
|
|
74
|
+
*/
|
|
75
|
+
align?: 'start' | 'center' | 'end';
|
|
76
|
+
/**
|
|
77
|
+
* The side of the dropdown content
|
|
78
|
+
* @default 'bottom'
|
|
79
|
+
*/
|
|
80
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
81
|
+
/**
|
|
82
|
+
* Whether to render in place instead of using portals
|
|
83
|
+
* @default false
|
|
84
|
+
*/
|
|
85
|
+
disablePortal?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Whether to disable the search functionality
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
disableSearch?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Currently selected value (for selection tracking)
|
|
93
|
+
*/
|
|
94
|
+
selected?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Callback when an option with a value is selected
|
|
97
|
+
*/
|
|
98
|
+
onSelect?: (value: string) => void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Props for the DsDropdownMenu Root component
|
|
103
|
+
*/
|
|
104
|
+
export interface DsDropdownMenuRootProps {
|
|
105
|
+
/**
|
|
106
|
+
* Whether the dropdown is open (controlled mode)
|
|
107
|
+
*/
|
|
108
|
+
open?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Callback when open state changes
|
|
111
|
+
*/
|
|
112
|
+
onOpenChange?: (open: boolean) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Callback when an item is selected
|
|
115
|
+
*/
|
|
116
|
+
onSelect?: (value: string) => void;
|
|
117
|
+
/**
|
|
118
|
+
* Callback when the highlighted item changes
|
|
119
|
+
*/
|
|
120
|
+
onHighlightChange?: (value: string | null) => void;
|
|
121
|
+
/**
|
|
122
|
+
* Positioning options for the dropdown
|
|
123
|
+
*/
|
|
124
|
+
positioning?: DsDropdownMenuPositioning;
|
|
125
|
+
/**
|
|
126
|
+
* Prevents the menu from closing when any item is selected
|
|
127
|
+
* @default false
|
|
128
|
+
*/
|
|
129
|
+
preventCloseOnSelect?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Root content
|
|
132
|
+
*/
|
|
133
|
+
children?: ReactNode;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Props for the DsDropdownMenu Trigger component
|
|
138
|
+
*/
|
|
139
|
+
export type DsDropdownMenuTriggerProps = Menu.TriggerProps;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Props for the DsDropdownMenu Content component
|
|
143
|
+
*/
|
|
144
|
+
export interface DsDropdownMenuContentProps extends Pick<
|
|
145
|
+
Menu.ContentProps,
|
|
146
|
+
'className' | 'style' | 'children'
|
|
147
|
+
> {
|
|
148
|
+
/**
|
|
149
|
+
* Whether to render in place instead of using portals
|
|
150
|
+
* @default false
|
|
151
|
+
*/
|
|
152
|
+
disablePortal?: boolean;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Props for the DsDropdownMenu Item component
|
|
157
|
+
*/
|
|
158
|
+
export interface DsDropdownMenuItemProps extends Pick<
|
|
159
|
+
Menu.ItemProps,
|
|
160
|
+
'disabled' | 'asChild' | 'className' | 'style' | 'onSelect' | 'onClick' | 'value' | 'children'
|
|
161
|
+
> {
|
|
162
|
+
/**
|
|
163
|
+
* Whether the item is selected (applies selected styling)
|
|
164
|
+
*/
|
|
165
|
+
selected?: boolean;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Props for the DsDropdownMenu ItemIndicator component
|
|
170
|
+
*/
|
|
171
|
+
export interface DsDropdownMenuItemIndicatorProps {
|
|
172
|
+
/**
|
|
173
|
+
* Custom content to render as the indicator (defaults to check icon)
|
|
174
|
+
*/
|
|
175
|
+
children?: ReactNode;
|
|
176
|
+
/**
|
|
177
|
+
* Optional CSS class name
|
|
178
|
+
*/
|
|
179
|
+
className?: string;
|
|
180
|
+
/**
|
|
181
|
+
* Optional inline styles
|
|
182
|
+
*/
|
|
183
|
+
style?: CSSProperties;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Props for the DsDropdownMenu Header component
|
|
188
|
+
*/
|
|
189
|
+
export interface DsDropdownMenuHeaderProps {
|
|
190
|
+
/**
|
|
191
|
+
* The header content (typically a search input or filter controls)
|
|
192
|
+
*/
|
|
193
|
+
children: ReactNode;
|
|
194
|
+
/**
|
|
195
|
+
* Optional CSS class name
|
|
196
|
+
*/
|
|
197
|
+
className?: string;
|
|
198
|
+
/**
|
|
199
|
+
* Optional inline styles
|
|
200
|
+
*/
|
|
201
|
+
style?: CSSProperties;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Props for the DsDropdownMenu Actions component
|
|
206
|
+
*/
|
|
207
|
+
export interface DsDropdownMenuActionsProps {
|
|
208
|
+
/**
|
|
209
|
+
* The action buttons or elements
|
|
210
|
+
*/
|
|
211
|
+
children: ReactNode;
|
|
212
|
+
/**
|
|
213
|
+
* Optional CSS class name
|
|
214
|
+
*/
|
|
215
|
+
className?: string;
|
|
216
|
+
/**
|
|
217
|
+
* Optional inline styles
|
|
218
|
+
*/
|
|
219
|
+
style?: CSSProperties;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Props for the DsDropdownMenu ItemGroup component
|
|
224
|
+
*/
|
|
225
|
+
export interface DsDropdownMenuItemGroupProps extends Menu.ItemGroupProps {
|
|
226
|
+
/**
|
|
227
|
+
* Whether the group is collapsed
|
|
228
|
+
*/
|
|
229
|
+
collapsed?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Callback when collapse state changes
|
|
232
|
+
*/
|
|
233
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Props for the DsDropdownMenu ItemGroupLabel component
|
|
238
|
+
*/
|
|
239
|
+
export type DsDropdownMenuItemGroupLabelProps = Menu.ItemGroupLabelProps;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Props for the DsDropdownMenu ItemGroupContent component
|
|
243
|
+
*/
|
|
244
|
+
export interface DsDropdownMenuItemGroupContentProps {
|
|
245
|
+
/**
|
|
246
|
+
* The content to show/hide based on collapsed state
|
|
247
|
+
*/
|
|
248
|
+
children: ReactNode;
|
|
249
|
+
/**
|
|
250
|
+
* Optional CSS class name
|
|
251
|
+
*/
|
|
252
|
+
className?: string;
|
|
253
|
+
/**
|
|
254
|
+
* Optional inline styles
|
|
255
|
+
*/
|
|
256
|
+
style?: CSSProperties;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Props for the DsDropdownMenu Separator component
|
|
261
|
+
*/
|
|
262
|
+
export type DsDropdownMenuSeparatorProps = Menu.SeparatorProps;
|