@drivenets/design-system 0.7.3 → 0.9.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 +47 -0
- package/dist/index.cjs +5730 -4569
- package/dist/index.d.cts +1768 -1065
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1768 -1065
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5718 -4577
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +14 -14
- package/src/components/ds-autocomplete/ds-autocomplete.module.scss +1 -2
- package/src/components/ds-autocomplete/ds-autocomplete.tsx +13 -8
- package/src/components/ds-autocomplete/ds-autocomplete.types.ts +2 -1
- package/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +25 -9
- package/src/components/ds-button/versions/ds-button-new/ds-button-new.tsx +5 -5
- package/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts +11 -1
- package/src/components/ds-button/versions/ds-button-new/index.ts +4 -1
- package/src/components/ds-button-v3/ds-button-v3.module.scss +391 -0
- package/src/components/ds-button-v3/ds-button-v3.tsx +55 -0
- package/src/components/ds-button-v3/ds-button-v3.types.ts +49 -0
- package/src/components/ds-button-v3/index.ts +2 -0
- package/src/components/ds-comment-bubble/ds-comment-bubble.module.scss +6 -2
- package/src/components/ds-comment-bubble/ds-comment-bubble.tsx +22 -15
- package/src/components/ds-comment-bubble/ds-comment-bubble.types.ts +1 -0
- package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx +1 -1
- package/src/components/ds-comments-drawer/ds-comments-drawer.tsx +3 -9
- package/src/components/ds-date-input/ds-date-input.tsx +3 -4
- package/src/components/ds-date-input/ds-date-input.types.ts +3 -1
- package/src/components/ds-date-input/index.ts +10 -0
- package/src/components/ds-date-picker/components/calendar-table.module.scss +93 -0
- package/src/components/ds-date-picker/components/calendar-view-control/calendar-view-control.module.scss +48 -0
- package/src/components/ds-date-picker/components/calendar-view-control/calendar-view-control.tsx +20 -0
- package/src/components/ds-date-picker/components/calendar-view-control/index.ts +1 -0
- package/src/components/ds-date-picker/components/day-view/day-view.module.scss +12 -0
- package/src/components/ds-date-picker/components/day-view/day-view.tsx +45 -0
- package/src/components/ds-date-picker/components/day-view/index.ts +1 -0
- package/src/components/ds-date-picker/components/month-view/index.ts +1 -0
- package/src/components/ds-date-picker/components/month-view/month-view.module.scss +4 -0
- package/src/components/ds-date-picker/components/month-view/month-view.tsx +35 -0
- package/src/components/ds-date-picker/components/year-view/index.ts +1 -0
- package/src/components/ds-date-picker/components/year-view/year-view.module.scss +4 -0
- package/src/components/ds-date-picker/components/year-view/year-view.tsx +35 -0
- package/src/components/ds-date-picker/ds-date-picker.module.scss +29 -0
- package/src/components/ds-date-picker/ds-date-picker.tsx +211 -0
- package/src/components/ds-date-picker/ds-date-picker.types.ts +94 -0
- package/src/components/ds-date-picker/ds-date-picker.utils.ts +87 -0
- package/src/components/ds-date-picker/ds-date-picker.utils.unit.test.ts +226 -0
- package/src/components/ds-date-picker/index.ts +2 -0
- package/src/components/ds-date-range-picker/ds-date-range-picker.module.scss +20 -0
- package/src/components/ds-date-range-picker/ds-date-range-picker.tsx +101 -0
- package/src/components/ds-date-range-picker/ds-date-range-picker.types.ts +95 -0
- package/src/components/ds-date-range-picker/ds-date-range-picker.utils.ts +19 -0
- package/src/components/ds-date-range-picker/ds-date-range-picker.utils.unit.test.ts +48 -0
- package/src/components/ds-date-range-picker/index.ts +9 -0
- package/src/components/ds-drawer/ds-drawer.tsx +6 -9
- package/src/components/ds-drawer/ds-drawer.types.ts +4 -2
- package/src/components/ds-file-upload/ds-file-upload-api.types.ts +1 -0
- package/src/components/ds-file-upload/hooks/use-file-upload.ts +1 -1
- package/src/components/ds-file-upload/index.ts +6 -1
- package/src/components/ds-file-upload/utils/file-factory.ts +1 -0
- package/src/components/ds-file-upload/utils/file-factory.unit.test.ts +37 -0
- package/src/components/ds-form-control/ds-form-control.tsx +7 -3
- package/src/components/ds-key-value-pair/ds-key-value-pair.module.scss +72 -0
- package/src/components/ds-key-value-pair/ds-key-value-pair.tsx +47 -0
- package/src/components/ds-key-value-pair/ds-key-value-pair.types.ts +24 -0
- package/src/components/ds-key-value-pair/index.ts +3 -0
- package/src/components/ds-loader/ds-loader.tsx +1 -1
- package/src/components/ds-panel/ds-panel.module.scss +4 -1
- package/src/components/ds-panel/ds-panel.tsx +8 -3
- package/src/components/ds-panel/ds-panel.types.ts +8 -1
- package/src/components/ds-panel/index.ts +4 -1
- package/src/components/ds-select/ds-select.module.scss +4 -1
- package/src/components/ds-split-button/ds-split-button.module.scss +99 -0
- package/src/components/ds-split-button/ds-split-button.tsx +49 -0
- package/src/components/ds-split-button/ds-split-button.types.ts +25 -0
- package/src/components/ds-split-button/index.ts +10 -0
- package/src/components/ds-stack/ds-stack.module.scss +4 -0
- package/src/components/ds-stack/ds-stack.tsx +35 -0
- package/src/components/ds-stack/ds-stack.types.ts +19 -0
- package/src/components/ds-stack/index.ts +18 -0
- package/src/components/ds-status-badge/ds-status-badge.module.scss +1 -1
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +6 -4
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +5 -8
- package/src/components/ds-table/ds-table.module.scss +6 -0
- package/src/components/ds-table/ds-table.tsx +3 -0
- package/src/components/ds-time-picker/components/time-item/index.ts +1 -0
- package/src/components/ds-time-picker/components/time-item/time-item.module.scss +37 -0
- package/src/components/ds-time-picker/components/time-item/time-item.tsx +26 -0
- package/src/components/ds-time-picker/components/time-item/time-item.types.ts +10 -0
- package/src/components/ds-time-picker/components/time-scroller/index.ts +2 -0
- package/src/components/ds-time-picker/components/time-scroller/time-scroller.module.scss +25 -0
- package/src/components/ds-time-picker/components/time-scroller/time-scroller.tsx +61 -0
- package/src/components/ds-time-picker/components/time-scroller/time-scroller.types.ts +17 -0
- package/src/components/ds-time-picker/components/time-scroller/time-scroller.utils.ts +44 -0
- package/src/components/ds-time-picker/ds-time-picker.module.scss +11 -0
- package/src/components/ds-time-picker/ds-time-picker.tsx +178 -0
- package/src/components/ds-time-picker/ds-time-picker.types.ts +54 -0
- package/src/components/ds-time-picker/ds-time-picker.utils.ts +176 -0
- package/src/components/ds-time-picker/ds-time-picker.utils.unit.test.ts +316 -0
- package/src/components/ds-time-picker/index.ts +3 -0
- package/src/components/ds-tooltip/ds-tooltip.module.scss +1 -7
- package/src/components/ds-tooltip/ds-tooltip.tsx +9 -9
- package/src/components/ds-tooltip/ds-tooltip.types.ts +7 -1
- package/src/index.ts +8 -0
- package/src/styles/_breakpoints.module.scss +5 -0
- package/src/styles/_responsive.scss +13 -0
- package/src/styles/_root_updated.scss +593 -0
- package/src/styles/_variables.scss +2 -0
- package/src/styles/shared/_button.scss +2 -0
- package/src/styles/shared/_dropdown.scss +4 -1
- package/src/styles/shared/_input.scss +3 -40
- package/src/utils/responsive.browser.test.ts +94 -0
- package/src/utils/responsive.ts +79 -0
- package/src/utils/responsive.unit.test.ts +54 -0
- package/src/utils/type-utils.ts +1 -0
- package/src/utils/use-controlled.browser.test.ts +79 -0
- package/src/utils/use-controlled.ts +18 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type * as Collapsible from '@radix-ui/react-collapsible';
|
|
2
2
|
|
|
3
|
+
import type { ResponsiveValue } from '../../utils/responsive';
|
|
4
|
+
|
|
3
5
|
export type DsPanelVariant = 'docked' | 'floating';
|
|
4
6
|
|
|
5
|
-
export type
|
|
7
|
+
export type DsPanelBaseProps = Omit<Collapsible.CollapsibleProps, 'onOpenChange'> & {
|
|
6
8
|
variant?: DsPanelVariant;
|
|
9
|
+
width?: number;
|
|
7
10
|
draggable?: boolean;
|
|
8
11
|
disablePadding?: boolean;
|
|
9
12
|
slotProps?: {
|
|
@@ -12,6 +15,10 @@ export type DsPanelProps = Omit<Collapsible.CollapsibleProps, 'onOpenChange'> &
|
|
|
12
15
|
onOpenChange?: (open: boolean) => void;
|
|
13
16
|
};
|
|
14
17
|
|
|
18
|
+
export type DsPanelProps = Omit<DsPanelBaseProps, 'width'> & {
|
|
19
|
+
width?: ResponsiveValue<number>;
|
|
20
|
+
};
|
|
21
|
+
|
|
15
22
|
export type DsPanelCollapseButtonProps = {
|
|
16
23
|
onClick?: () => void;
|
|
17
24
|
collapsed?: boolean;
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { withResponsiveProps } from '../../utils/responsive';
|
|
2
|
+
import { DsPanelBase } from './ds-panel';
|
|
3
|
+
|
|
4
|
+
export const DsPanel = withResponsiveProps(DsPanelBase, ['width']);
|
|
2
5
|
export type { DsPanelProps, DsPanelVariant, DsPanelCollapseButtonProps } from './ds-panel.types';
|
|
@@ -53,8 +53,11 @@
|
|
|
53
53
|
@include typography.ellipsis;
|
|
54
54
|
grid-column: 1;
|
|
55
55
|
grid-row: 1;
|
|
56
|
-
color: var(--color-font-secondary);
|
|
57
56
|
text-align: start;
|
|
57
|
+
|
|
58
|
+
.trigger:not([data-disabled]) & {
|
|
59
|
+
color: var(--color-font-secondary);
|
|
60
|
+
}
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
.multiSelectItemsContainer {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
@use '../../styles/shared/button' as button;
|
|
2
|
+
|
|
3
|
+
$highlighted-z-index: 1;
|
|
4
|
+
$divider-z-index: $highlighted-z-index + 1;
|
|
5
|
+
$divider-width: 1px;
|
|
6
|
+
$border-width: 1px;
|
|
7
|
+
|
|
8
|
+
@mixin when-button-disabled {
|
|
9
|
+
.root:has(.actionButton:disabled) & {
|
|
10
|
+
@content;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@mixin when-select-disabled {
|
|
15
|
+
.root:has(.select[data-disabled]) & {
|
|
16
|
+
@content;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@mixin when-button-highlighted {
|
|
21
|
+
.root:has(.actionButton:not(:disabled):is(:hover, :focus-visible, :active, [data-selected='true'])) & {
|
|
22
|
+
@content;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@mixin when-select-highlighted {
|
|
27
|
+
.root:has(
|
|
28
|
+
.select:not([data-disabled]):is(:hover, :active, [data-state='open']),
|
|
29
|
+
.select:not([data-disabled]) :focus-visible
|
|
30
|
+
)
|
|
31
|
+
& {
|
|
32
|
+
@content;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.root {
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
|
|
39
|
+
.actionButton {
|
|
40
|
+
border-top-right-radius: 0;
|
|
41
|
+
border-bottom-right-radius: 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.actionButton {
|
|
46
|
+
@include when-button-highlighted {
|
|
47
|
+
z-index: $highlighted-z-index;
|
|
48
|
+
transition: border-color button.$transition-duration-quick;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&:not(:disabled):is(:hover, :active, [data-selected='true']) {
|
|
52
|
+
border-right-color: var(--border-border-secondary-hover);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&:not(:disabled):focus-visible {
|
|
56
|
+
border-right-color: var(--border-border-inverse);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.select {
|
|
61
|
+
margin-left: -$divider-width;
|
|
62
|
+
border-top-left-radius: 0;
|
|
63
|
+
border-bottom-left-radius: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.dividerAnchor {
|
|
67
|
+
position: relative;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.dividerWrapper {
|
|
71
|
+
position: absolute;
|
|
72
|
+
top: $border-width;
|
|
73
|
+
bottom: $border-width;
|
|
74
|
+
left: -$divider-width;
|
|
75
|
+
z-index: $divider-z-index;
|
|
76
|
+
width: $divider-width;
|
|
77
|
+
padding: var(--spacing-3xs) 0;
|
|
78
|
+
background-color: var(--background-background);
|
|
79
|
+
|
|
80
|
+
@include when-button-highlighted {
|
|
81
|
+
display: none;
|
|
82
|
+
}
|
|
83
|
+
@include when-select-highlighted {
|
|
84
|
+
display: none;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.divider {
|
|
89
|
+
background-color: var(--color-border-secondary);
|
|
90
|
+
width: $divider-width;
|
|
91
|
+
height: 100%;
|
|
92
|
+
|
|
93
|
+
@include when-button-disabled {
|
|
94
|
+
background-color: var(--border-border-disabled);
|
|
95
|
+
}
|
|
96
|
+
@include when-select-disabled {
|
|
97
|
+
background-color: var(--border-border-disabled);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { DsSelect, type SelectSize } from '../ds-select';
|
|
3
|
+
import styles from './ds-split-button.module.scss';
|
|
4
|
+
import type { DsSplitButtonProps, SplitButtonSize } from './ds-split-button.types';
|
|
5
|
+
import { DsButtonV3 } from '../ds-button-v3';
|
|
6
|
+
|
|
7
|
+
const DsSplitButton = ({
|
|
8
|
+
ref,
|
|
9
|
+
className,
|
|
10
|
+
style,
|
|
11
|
+
size = 'medium',
|
|
12
|
+
disabled,
|
|
13
|
+
slotProps,
|
|
14
|
+
}: DsSplitButtonProps) => {
|
|
15
|
+
const { className: buttonClassName, disabled: buttonDisabled, ...buttonProps } = slotProps.button;
|
|
16
|
+
|
|
17
|
+
const { className: selectClassName, disabled: selectDisabled, ...selectProps } = slotProps.select;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div ref={ref} className={classNames(styles.root, className)} style={style}>
|
|
21
|
+
<DsButtonV3
|
|
22
|
+
{...buttonProps}
|
|
23
|
+
variant="secondary"
|
|
24
|
+
size={size}
|
|
25
|
+
disabled={buttonDisabled ?? disabled}
|
|
26
|
+
className={classNames(styles.actionButton, buttonClassName)}
|
|
27
|
+
/>
|
|
28
|
+
|
|
29
|
+
<div className={styles.dividerAnchor}>
|
|
30
|
+
<div className={styles.dividerWrapper}>
|
|
31
|
+
<div className={styles.divider} />
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<DsSelect
|
|
36
|
+
{...selectProps}
|
|
37
|
+
size={getSelectSize(size)}
|
|
38
|
+
disabled={selectDisabled ?? disabled}
|
|
39
|
+
className={classNames(styles.select, selectClassName)}
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const getSelectSize = (size: SplitButtonSize): SelectSize => {
|
|
46
|
+
return size === 'medium' ? 'default' : 'small';
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default DsSplitButton;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CSSProperties, Ref } from 'react';
|
|
2
|
+
import type { DsButtonV3Props } from '../ds-button-v3';
|
|
3
|
+
import type { DsSelectProps } from '../ds-select';
|
|
4
|
+
import type { DistributiveOmit } from '../../utils/type-utils';
|
|
5
|
+
|
|
6
|
+
export const splitButtonSizes = ['medium', 'small'] as const;
|
|
7
|
+
export type SplitButtonSize = (typeof splitButtonSizes)[number];
|
|
8
|
+
|
|
9
|
+
type ButtonSlotProps = Omit<DsButtonV3Props, 'variant' | 'color' | 'size'>;
|
|
10
|
+
|
|
11
|
+
type SelectSlotProps = DistributiveOmit<DsSelectProps, 'size'>;
|
|
12
|
+
|
|
13
|
+
export interface DsSplitButtonSlotProps {
|
|
14
|
+
button: ButtonSlotProps;
|
|
15
|
+
select: SelectSlotProps;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DsSplitButtonProps {
|
|
19
|
+
ref?: Ref<HTMLDivElement>;
|
|
20
|
+
className?: string;
|
|
21
|
+
style?: CSSProperties;
|
|
22
|
+
size?: SplitButtonSize;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
slotProps: DsSplitButtonSlotProps;
|
|
25
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import { withResponsiveProps } from '../../utils/responsive';
|
|
3
|
+
import DsSplitButtonBase from './ds-split-button';
|
|
4
|
+
export { type DsSplitButtonSlotProps, splitButtonSizes } from './ds-split-button.types';
|
|
5
|
+
|
|
6
|
+
export const DsSplitButton = withResponsiveProps(DsSplitButtonBase, ['size']);
|
|
7
|
+
|
|
8
|
+
DsSplitButton.displayName = 'DsSplitButton';
|
|
9
|
+
|
|
10
|
+
export type DsSplitButtonProps = ComponentProps<typeof DsSplitButton>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
|
|
3
|
+
import styles from './ds-stack.module.scss';
|
|
4
|
+
import type { DsStackProps } from './ds-stack.types';
|
|
5
|
+
|
|
6
|
+
export const DsStack = ({
|
|
7
|
+
direction,
|
|
8
|
+
gap,
|
|
9
|
+
alignItems,
|
|
10
|
+
justifyContent,
|
|
11
|
+
flex,
|
|
12
|
+
flexWrap,
|
|
13
|
+
width,
|
|
14
|
+
children,
|
|
15
|
+
className,
|
|
16
|
+
style,
|
|
17
|
+
ref,
|
|
18
|
+
}: DsStackProps) => {
|
|
19
|
+
const layoutStyle: React.CSSProperties = {
|
|
20
|
+
flexDirection: direction,
|
|
21
|
+
gap,
|
|
22
|
+
alignItems,
|
|
23
|
+
justifyContent,
|
|
24
|
+
flex,
|
|
25
|
+
flexWrap,
|
|
26
|
+
width,
|
|
27
|
+
...style,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div ref={ref} className={classNames(styles.root, className)} style={layoutStyle}>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Properties } from 'csstype';
|
|
2
|
+
|
|
3
|
+
type CSSProps = Properties<string | number>;
|
|
4
|
+
import type { CSSProperties, ReactNode, Ref } from 'react';
|
|
5
|
+
|
|
6
|
+
export interface DsStackProps {
|
|
7
|
+
direction?: CSSProps['flexDirection'];
|
|
8
|
+
gap?: CSSProps['gap'];
|
|
9
|
+
alignItems?: CSSProps['alignItems'];
|
|
10
|
+
justifyContent?: CSSProps['justifyContent'];
|
|
11
|
+
flex?: CSSProps['flex'];
|
|
12
|
+
flexWrap?: CSSProps['flexWrap'];
|
|
13
|
+
width?: CSSProps['width'];
|
|
14
|
+
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
className?: string;
|
|
17
|
+
style?: CSSProperties;
|
|
18
|
+
ref?: Ref<HTMLDivElement>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { withResponsiveProps } from '../../utils/responsive';
|
|
4
|
+
import { DsStack as DsStackBase } from './ds-stack';
|
|
5
|
+
|
|
6
|
+
export const DsStack = withResponsiveProps(DsStackBase, [
|
|
7
|
+
'direction',
|
|
8
|
+
'gap',
|
|
9
|
+
'alignItems',
|
|
10
|
+
'justifyContent',
|
|
11
|
+
'flex',
|
|
12
|
+
'flexWrap',
|
|
13
|
+
'width',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
DsStack.displayName = 'DsStack';
|
|
17
|
+
|
|
18
|
+
export type DsStackProps = ComponentProps<typeof DsStack>;
|
package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { useVirtualizer, type Virtualizer } from '@tanstack/react-virtual';
|
|
|
4
4
|
import styles from './ds-table-body-virtualized.module.scss';
|
|
5
5
|
import { DsTableRowVirtualized } from '../ds-table-row-virtualized';
|
|
6
6
|
import type { DsTableBodyVirtualizedProps } from './ds-table-body-virtualized.types';
|
|
7
|
-
import { TableBody } from '../core-table';
|
|
7
|
+
import { TableBody, TableRow, TableCell } from '../core-table';
|
|
8
8
|
import { EMPTY_TABLE_STATE_TEXT } from '../../utils/constants';
|
|
9
9
|
|
|
10
10
|
export const DsTableBodyVirtualized = <TData,>({
|
|
@@ -16,8 +16,8 @@ export const DsTableBodyVirtualized = <TData,>({
|
|
|
16
16
|
onScroll,
|
|
17
17
|
rowSelection,
|
|
18
18
|
}: DsTableBodyVirtualizedProps<TData>) => {
|
|
19
|
-
const rowsMapRef = useRef
|
|
20
|
-
const rowHeightsMapRef = useRef
|
|
19
|
+
const rowsMapRef = useRef(new Map<string, HTMLTableRowElement>());
|
|
20
|
+
const rowHeightsMapRef = useRef(new Map<string, number>());
|
|
21
21
|
|
|
22
22
|
const { rows } = table.getRowModel();
|
|
23
23
|
|
|
@@ -138,7 +138,9 @@ function DsTableBody<TData>({
|
|
|
138
138
|
);
|
|
139
139
|
})
|
|
140
140
|
) : (
|
|
141
|
-
<
|
|
141
|
+
<TableRow>
|
|
142
|
+
<TableCell className={styles.emptyState}>{emptyState || EMPTY_TABLE_STATE_TEXT}</TableCell>
|
|
143
|
+
</TableRow>
|
|
142
144
|
)}
|
|
143
145
|
</TableBody>
|
|
144
146
|
);
|
package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss
CHANGED
|
@@ -58,18 +58,15 @@
|
|
|
58
58
|
gap: 8px;
|
|
59
59
|
|
|
60
60
|
> .actionButton {
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
gap: var(--spacing-3xs);
|
|
61
66
|
border-right: 1px solid var(--neutral-6);
|
|
62
67
|
width: 68px;
|
|
63
68
|
height: 48px;
|
|
64
69
|
border-radius: 0;
|
|
65
|
-
|
|
66
|
-
> .actionButtonContent {
|
|
67
|
-
display: flex;
|
|
68
|
-
flex-direction: column;
|
|
69
|
-
align-items: center;
|
|
70
|
-
justify-content: center;
|
|
71
|
-
gap: 4px;
|
|
72
|
-
}
|
|
73
70
|
}
|
|
74
71
|
|
|
75
72
|
> .actionButton:first-child {
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
@use '../../styles/utilities' as utils;
|
|
3
3
|
@use './styles/variables' as vars;
|
|
4
4
|
|
|
5
|
+
$bulk-actions-height: 60px;
|
|
6
|
+
|
|
5
7
|
.container {
|
|
6
8
|
border-radius: vars.$border-radius;
|
|
7
9
|
border-width: vars.$border-width;
|
|
@@ -45,3 +47,7 @@
|
|
|
45
47
|
height: vars.$virtualized-height;
|
|
46
48
|
overflow: auto;
|
|
47
49
|
}
|
|
50
|
+
|
|
51
|
+
.bulkActionsVisible {
|
|
52
|
+
padding-bottom: $bulk-actions-height;
|
|
53
|
+
}
|
|
@@ -204,6 +204,8 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
204
204
|
.map(([key]) => rowsById[key]?.original)
|
|
205
205
|
.filter((v) => !!v);
|
|
206
206
|
|
|
207
|
+
const isBulkActionsVisible = selectable && actions.length > 0 && selectedRows.length > 0;
|
|
208
|
+
|
|
207
209
|
const contextValue: DsTableContextType<TData, TValue> = {
|
|
208
210
|
stickyHeader,
|
|
209
211
|
bordered,
|
|
@@ -230,6 +232,7 @@ const DsTable = <TData extends { id: string }, TValue>({
|
|
|
230
232
|
styles.container,
|
|
231
233
|
!virtualized && styles.dataTableContainer,
|
|
232
234
|
virtualized && styles.virtualizedContainer,
|
|
235
|
+
isBulkActionsVisible && styles.bulkActionsVisible,
|
|
233
236
|
className,
|
|
234
237
|
)}
|
|
235
238
|
>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './time-item';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
$time-item-height: 32px;
|
|
2
|
+
$time-item-width: 48px;
|
|
3
|
+
|
|
4
|
+
.container {
|
|
5
|
+
display: flex;
|
|
6
|
+
width: $time-item-width;
|
|
7
|
+
height: $time-item-height;
|
|
8
|
+
flex-shrink: 0;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
align-items: center;
|
|
12
|
+
border-radius: 8px;
|
|
13
|
+
background: transparent;
|
|
14
|
+
color: var(--color-font-main);
|
|
15
|
+
|
|
16
|
+
&.selected {
|
|
17
|
+
background: var(--color-background-selected);
|
|
18
|
+
color: var(--color-font-on-action);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&.disabled {
|
|
22
|
+
color: var(--color-font-disabled);
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:not(.selected, .disabled) {
|
|
27
|
+
&:hover {
|
|
28
|
+
border: 1px solid var(--color-border);
|
|
29
|
+
background: var(--color-background-action-hover-weak);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:focus-visible {
|
|
33
|
+
border: 2px solid var(--color-border-action-secondary);
|
|
34
|
+
background: var(--color-background-action-hover-weak);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import type { TimeItemProps } from './time-item.types';
|
|
3
|
+
import styles from './time-item.module.scss';
|
|
4
|
+
import { DsTypography } from '../../../ds-typography';
|
|
5
|
+
|
|
6
|
+
export const TimeItem = ({ ref, className, selected, disabled, onClick, label }: TimeItemProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<button
|
|
9
|
+
ref={ref}
|
|
10
|
+
type="button"
|
|
11
|
+
role="option"
|
|
12
|
+
aria-selected={selected}
|
|
13
|
+
aria-disabled={disabled || undefined}
|
|
14
|
+
disabled={disabled}
|
|
15
|
+
className={classNames(
|
|
16
|
+
styles.container,
|
|
17
|
+
selected && styles.selected,
|
|
18
|
+
disabled && styles.disabled,
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
onClick={onClick}
|
|
22
|
+
>
|
|
23
|
+
<DsTypography variant="body-md-md">{label}</DsTypography>
|
|
24
|
+
</button>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
$column-width: 56px;
|
|
2
|
+
$column-max-height: 280px;
|
|
3
|
+
|
|
4
|
+
.container {
|
|
5
|
+
display: flex;
|
|
6
|
+
padding: var(--spacing-xs);
|
|
7
|
+
background: var(--color-background);
|
|
8
|
+
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.22);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.column {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
align-items: center;
|
|
15
|
+
width: $column-width;
|
|
16
|
+
gap: var(--spacing-xs);
|
|
17
|
+
max-height: $column-max-height;
|
|
18
|
+
overflow-y: auto;
|
|
19
|
+
scroll-snap-type: y mandatory;
|
|
20
|
+
|
|
21
|
+
scrollbar-width: none;
|
|
22
|
+
&::-webkit-scrollbar {
|
|
23
|
+
display: none;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { TimeItem } from '../time-item';
|
|
3
|
+
import type { TimePeriod, TimeScrollerProps } from './time-scroller.types';
|
|
4
|
+
import styles from './time-scroller.module.scss';
|
|
5
|
+
import { useScrollToSelected } from './time-scroller.utils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* In AM/PM mode, hours start from 12 and then go from to 1 to 11.
|
|
9
|
+
* e.g. 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
|
10
|
+
*/
|
|
11
|
+
const HOURS = [12, ...Array.from({ length: 11 }, (_, i) => i + 1)];
|
|
12
|
+
|
|
13
|
+
const MINUTES = Array.from({ length: 60 }, (_, i) => i);
|
|
14
|
+
const PERIODS: TimePeriod[] = ['AM', 'PM'];
|
|
15
|
+
|
|
16
|
+
export const TimeScroller = ({ open, slotProps: { hour, minute, period }, className }: TimeScrollerProps) => {
|
|
17
|
+
const hourScroll = useScrollToSelected(hour.value, open);
|
|
18
|
+
const minuteScroll = useScrollToSelected(minute.value, open);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className={classNames(styles.container, className)}>
|
|
22
|
+
<div ref={hourScroll.columnRef} className={styles.column} role="listbox" aria-label="Hour">
|
|
23
|
+
{HOURS.map((h) => (
|
|
24
|
+
<TimeItem
|
|
25
|
+
key={h}
|
|
26
|
+
ref={h === hour.value ? hourScroll.selectedRef : undefined}
|
|
27
|
+
label={String(h).padStart(2, '0')}
|
|
28
|
+
selected={h === hour.value}
|
|
29
|
+
disabled={hour.isDisabled?.(h)}
|
|
30
|
+
onClick={() => hour.onChange?.(h)}
|
|
31
|
+
/>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div ref={minuteScroll.columnRef} className={styles.column} role="listbox" aria-label="Minute">
|
|
36
|
+
{MINUTES.map((m) => (
|
|
37
|
+
<TimeItem
|
|
38
|
+
key={m}
|
|
39
|
+
ref={m === minute.value ? minuteScroll.selectedRef : undefined}
|
|
40
|
+
label={String(m).padStart(2, '0')}
|
|
41
|
+
selected={m === minute.value}
|
|
42
|
+
disabled={minute.isDisabled?.(m)}
|
|
43
|
+
onClick={() => minute.onChange?.(m)}
|
|
44
|
+
/>
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div className={styles.column} role="listbox" aria-label="AM/PM">
|
|
49
|
+
{PERIODS.map((p) => (
|
|
50
|
+
<TimeItem
|
|
51
|
+
key={p}
|
|
52
|
+
label={p}
|
|
53
|
+
selected={p === period.value}
|
|
54
|
+
disabled={period.isDisabled?.(p)}
|
|
55
|
+
onClick={() => period.onChange?.(p)}
|
|
56
|
+
/>
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type TimePeriod = 'AM' | 'PM';
|
|
2
|
+
|
|
3
|
+
export interface TimeScrollerProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
open: boolean;
|
|
6
|
+
slotProps: {
|
|
7
|
+
hour: TimeScrollerSlotProps<number>;
|
|
8
|
+
minute: TimeScrollerSlotProps<number>;
|
|
9
|
+
period: TimeScrollerSlotProps<TimePeriod>;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TimeScrollerSlotProps<TValue> {
|
|
14
|
+
value?: TValue;
|
|
15
|
+
onChange?: (value: TValue) => void;
|
|
16
|
+
isDisabled?: (value: TValue) => boolean;
|
|
17
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useLayoutEffect, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useScrollToSelected = (value: unknown, open: boolean) => {
|
|
4
|
+
const columnRef = useRef<HTMLDivElement | null>(null);
|
|
5
|
+
const selectedRef = useRef<HTMLButtonElement | null>(null);
|
|
6
|
+
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
const column = columnRef.current;
|
|
9
|
+
const selected = selectedRef.current;
|
|
10
|
+
|
|
11
|
+
if (!column || !selected) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
scrollToCenter(column, selected, 'smooth');
|
|
16
|
+
}, [value]);
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!open) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Ark UI removes `display: none` from Popover.Content after React's commit phase,
|
|
24
|
+
// so layout measurements are only valid after the browser has painted.
|
|
25
|
+
requestAnimationFrame(() => {
|
|
26
|
+
const column = columnRef.current;
|
|
27
|
+
const selected = selectedRef.current;
|
|
28
|
+
|
|
29
|
+
if (!column || !selected) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
scrollToCenter(column, selected, 'instant');
|
|
34
|
+
});
|
|
35
|
+
}, [open]);
|
|
36
|
+
|
|
37
|
+
return { columnRef, selectedRef };
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const scrollToCenter = (column: HTMLDivElement, selected: HTMLButtonElement, behavior: ScrollBehavior) => {
|
|
41
|
+
const columnCenter = column.offsetHeight / 2;
|
|
42
|
+
const itemCenter = selected.offsetTop + selected.offsetHeight / 2;
|
|
43
|
+
column.scrollTo({ top: itemCenter - columnCenter, behavior });
|
|
44
|
+
};
|