@drivenets/design-system 0.15.0 → 0.16.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 +19 -0
- package/dist/index.cjs +868 -200
- package/dist/index.d.cts +810 -1181
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +810 -1181
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +852 -196
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +15 -13
- package/src/components/ds-alert-banner/ds-alert-banner.tsx +11 -2
- package/src/components/ds-autocomplete/ds-autocomplete.figma.ts +36 -0
- package/src/components/ds-autocomplete/ds-autocomplete.tsx +2 -0
- package/src/components/ds-avatar/ds-avatar.figma.ts +30 -0
- package/src/components/ds-avatar/ds-avatar.tsx +6 -1
- package/src/components/ds-avatar/ds-avatar.types.ts +5 -2
- package/src/components/ds-avatar-group/ds-avatar-group.figma.ts +27 -0
- package/src/components/ds-avatar-group/ds-avatar-group.tsx +6 -1
- package/src/components/ds-avatar-group/ds-avatar-group.types.ts +3 -1
- package/src/components/ds-breadcrumb/ds-breadcrumb.figma.ts +19 -0
- package/src/components/ds-breadcrumb/ds-breadcrumb.tsx +2 -0
- package/src/components/ds-button/ds-button.tsx +2 -0
- package/src/components/ds-button-v3/ds-button-v3.figma.ts +1 -1
- package/src/components/ds-button-v3/ds-button-v3.tsx +6 -0
- package/src/components/ds-button-v3/index.ts +2 -0
- package/src/components/ds-card/ds-card.tsx +7 -3
- package/src/components/ds-catalog-layout/ds-catalog-layout.tsx +8 -0
- package/src/components/ds-checkbox/ds-checkbox-level-2.figma.ts +40 -0
- package/src/components/ds-checkbox/ds-checkbox.figma.ts +45 -0
- package/src/components/ds-checkbox/ds-checkbox.module.scss +1 -1
- package/src/components/ds-checkbox/ds-checkbox.tsx +9 -1
- package/src/components/ds-checkbox-group/ds-checkbox-group.figma.ts +15 -0
- package/src/components/ds-checkbox-group/ds-checkbox-group.tsx +2 -0
- package/src/components/ds-chip/ds-chip.tsx +2 -0
- package/src/components/ds-chip-group/ds-chip-group.tsx +2 -0
- package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.tsx +1 -0
- package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx +1 -0
- package/src/components/ds-comment-bubble/ds-comment-bubble.tsx +1 -0
- package/src/components/ds-comment-card/ds-comment-card.tsx +1 -0
- package/src/components/ds-comment-indicator/ds-comment-indicator.tsx +1 -0
- package/src/components/ds-comments-drawer/ds-comments-drawer.tsx +1 -0
- package/src/components/ds-confirmation/ds-confirmation.tsx +1 -0
- package/src/components/ds-date-input/ds-date-input.tsx +2 -0
- package/src/components/ds-date-picker/ds-date-picker.tsx +9 -0
- package/src/components/ds-date-picker/ds-date-picker.types.ts +1 -1
- package/src/components/ds-date-range-picker/ds-date-range-picker.tsx +2 -0
- package/src/components/ds-dialog/ds-dialog.tsx +9 -0
- package/src/components/ds-dialog/ds-dialog.types.ts +1 -1
- package/src/components/ds-divider/ds-divider.figma.ts +19 -0
- package/src/components/ds-divider/ds-divider.tsx +2 -0
- package/src/components/ds-drawer/ds-drawer.tsx +10 -0
- package/src/components/ds-drawer/figma/ds-drawer-content.figma.ts +15 -0
- package/src/components/ds-drawer/figma/ds-drawer-footer.figma.ts +19 -0
- package/src/components/ds-drawer/figma/ds-drawer-header.figma.ts +16 -0
- package/src/components/ds-drawer/figma/ds-drawer.figma.ts +36 -0
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss +12 -3
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +24 -6
- package/src/components/ds-dropdown-menu/ds-dropdown-menu.types.ts +10 -1
- package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-footer.figma.ts +11 -0
- package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-group.figma.ts +23 -0
- package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-item.figma.ts +29 -0
- package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu-separator.figma.ts +11 -0
- package/src/components/ds-dropdown-menu/figma/ds-dropdown-menu.figma.ts +24 -0
- package/src/components/ds-expandable-text-input/ds-expandable-text-input.figma.ts +24 -0
- package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +1 -0
- package/src/components/ds-file-upload/ds-file-upload.figma.ts +29 -0
- package/src/components/ds-file-upload/ds-file-upload.tsx +2 -0
- package/src/components/ds-filter-status-icon/ds-filter-status-icon.figma.ts +22 -0
- package/src/components/ds-filter-status-icon/ds-filter-status-icon.tsx +1 -0
- package/src/components/ds-form-control/ds-form-control.tsx +3 -0
- package/src/components/ds-grid/index.ts +3 -0
- package/src/components/ds-icon/ds-icon.tsx +2 -0
- package/src/components/ds-key-value-pair/ds-key-value-pair.figma.ts +24 -0
- package/src/components/ds-key-value-pair/ds-key-value-pair.tsx +2 -0
- package/src/components/ds-loader/ds-loader.figma.ts +11 -0
- package/src/components/ds-loader/ds-loader.tsx +2 -0
- package/src/components/ds-modal/ds-modal.tsx +9 -0
- package/src/components/ds-number-input/ds-number-input.tsx +2 -0
- package/src/components/ds-panel/ds-panel.figma.ts +16 -0
- package/src/components/ds-panel/index.ts +2 -0
- package/src/components/ds-password-input/ds-password-input.tsx +2 -0
- package/src/components/ds-popover/ds-popover.tsx +9 -0
- package/src/components/ds-popover/figma/ds-popover-content-item.figma.ts +18 -0
- package/src/components/ds-popover/figma/ds-popover-content.figma.ts +15 -0
- package/src/components/ds-popover/figma/ds-popover-footer.figma.ts +19 -0
- package/src/components/ds-popover/figma/ds-popover-header.figma.ts +16 -0
- package/src/components/ds-popover/figma/ds-popover.figma.ts +34 -0
- package/src/components/ds-progress-arc/ds-progress-arc.figma.ts +23 -0
- package/src/components/ds-progress-arc/ds-progress-arc.tsx +2 -0
- package/src/components/ds-progress-donut/ds-progress-donut.figma.ts +45 -0
- package/src/components/ds-progress-donut/ds-progress-donut.tsx +2 -0
- package/src/components/ds-progress-linear/ds-progress-linear.figma.ts +26 -0
- package/src/components/ds-progress-linear/ds-progress-linear.tsx +2 -0
- package/src/components/ds-progress-task-bar/ds-progress-task-bar.tsx +2 -0
- package/src/components/ds-radio-group/ds-radio-group.tsx +3 -0
- package/src/components/ds-radio-group/figma/ds-radio-group-item.figma.ts +24 -0
- package/src/components/ds-radio-group/figma/ds-radio-group.figma.ts +14 -0
- package/src/components/ds-segment-group/ds-segment-group.tsx +3 -0
- package/src/components/ds-segment-group/figma/ds-segment-group-item.figma.ts +25 -0
- package/src/components/ds-segment-group/figma/ds-segment-group.figma.ts +23 -0
- package/src/components/ds-select/ds-select.tsx +2 -0
- package/src/components/ds-site-node/ds-site-node.figma.ts +27 -0
- package/src/components/ds-site-node/ds-site-node.module.scss +59 -0
- package/src/components/ds-site-node/ds-site-node.tsx +73 -0
- package/src/components/ds-site-node/ds-site-node.types.ts +36 -0
- package/src/components/ds-site-node/hooks/use-is-overflowing.ts +31 -0
- package/src/components/ds-site-node/index.ts +3 -0
- package/src/components/ds-skeleton/ds-skeleton-circle.tsx +2 -0
- package/src/components/ds-skeleton/ds-skeleton-rect.tsx +2 -0
- package/src/components/ds-skeleton/ds-skeleton-text.tsx +2 -0
- package/src/components/ds-skeleton/ds-skeleton.ts +7 -1
- package/src/components/ds-smart-tabs/ds-smart-tabs.tsx +4 -0
- package/src/components/ds-spinner/ds-spinner.figma.ts +19 -0
- package/src/components/ds-spinner/ds-spinner.tsx +2 -0
- package/src/components/ds-split-button/ds-split-button.tsx +6 -0
- package/src/components/ds-status-badge/ds-status-badge.tsx +2 -0
- package/src/components/ds-status-badge-v2/index.ts +2 -0
- package/src/components/ds-stepper/components/ds-step-content.tsx +2 -0
- package/src/components/ds-stepper/components/ds-step-next-button.tsx +2 -0
- package/src/components/ds-stepper/components/ds-step.tsx +2 -0
- package/src/components/ds-stepper/ds-stepper.figma.ts +38 -0
- package/src/components/ds-stepper/ds-stepper.tsx +1 -0
- package/src/components/ds-system-status/ds-system-status.tsx +2 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +1 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +1 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +1 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +1 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +1 -0
- package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +1 -0
- package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +1 -0
- package/src/components/ds-table/ds-table.tsx +2 -0
- package/src/components/ds-table/index.ts +3 -0
- package/src/components/ds-tabs/components/ds-tabs-content/ds-tabs-content.tsx +2 -0
- package/src/components/ds-tabs/components/ds-tabs-list/ds-tabs-list.tsx +2 -0
- package/src/components/ds-tabs/components/ds-tabs-tab/ds-tabs-tab.tsx +2 -0
- package/src/components/ds-tabs/ds-tabs.tsx +2 -0
- package/src/components/ds-tag/ds-tag.tsx +2 -0
- package/src/components/ds-tag-filter/ds-tag-filter.tsx +2 -0
- package/src/components/ds-text-input/ds-input-field-v02.figma.ts +87 -0
- package/src/components/ds-text-input/ds-text-input.tsx +2 -0
- package/src/components/ds-textarea/ds-textarea.tsx +2 -0
- package/src/components/ds-time-picker/ds-time-picker.tsx +2 -0
- package/src/components/ds-toast/ds-toast-context.tsx +1 -0
- package/src/components/ds-toast/ds-toast.figma.ts +34 -0
- package/src/components/ds-toast/ds-toast.tsx +3 -0
- package/src/components/ds-toggle/ds-toggle.figma.ts +39 -0
- package/src/components/ds-toggle/ds-toggle.tsx +2 -0
- package/src/components/ds-tooltip/ds-tooltip.figma.ts +16 -0
- package/src/components/ds-tooltip/ds-tooltip.module.scss +8 -1
- package/src/components/ds-tooltip/ds-tooltip.tsx +35 -15
- package/src/components/ds-tooltip/ds-tooltip.types.ts +40 -0
- package/src/components/ds-tree/ds-tree.tsx +16 -0
- package/src/components/ds-typography/ds-typography.config.ts +1 -1
- package/src/components/ds-typography/ds-typography.figma.ts +63 -0
- package/src/components/ds-typography/ds-typography.module.scss +16 -0
- package/src/components/ds-typography/ds-typography.tsx +64 -7
- package/src/components/ds-typography/ds-typography.types.ts +13 -0
- package/src/components/ds-typography/hooks/use-truncation.ts +151 -0
- package/src/components/ds-user-card/ds-user-card.figma.ts +16 -0
- package/src/components/ds-user-card/ds-user-card.module.scss +25 -0
- package/src/components/ds-user-card/ds-user-card.tsx +31 -0
- package/src/components/ds-user-card/ds-user-card.types.ts +26 -0
- package/src/components/ds-user-card/index.ts +2 -0
- package/src/components/ds-vertical-tabs/ds-vertical-tabs.tsx +6 -0
- package/src/components/ds-workspace-layout/ds-workspace-layout.tsx +10 -0
- package/src/index.ts +2 -0
- package/src/styles/_root.scss +468 -363
- package/src/styles/shared/_dropdown.scss +2 -2
- package/src/styles/styles.scss +0 -3
- package/src/styles/_colors.scss +0 -65
- package/src/styles/_root_new.scss +0 -451
- package/src/styles/_spacing.scss +0 -47
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=39324-248
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-typography
|
|
3
|
+
// component=DsTypography
|
|
4
|
+
import figma from 'figma';
|
|
5
|
+
|
|
6
|
+
const instance = figma.selectedInstance;
|
|
7
|
+
|
|
8
|
+
const variant = instance.getEnum('font-style', {
|
|
9
|
+
heading1: 'heading1',
|
|
10
|
+
heading2: 'heading2',
|
|
11
|
+
heading3: 'heading3',
|
|
12
|
+
heading4: 'heading4',
|
|
13
|
+
'body-md-reg': 'body-md-reg',
|
|
14
|
+
'body-md-md': 'body-md-md',
|
|
15
|
+
'body-md-semi-bold': 'body-md-semi-bold',
|
|
16
|
+
'body-md-bold': 'body-md-bold',
|
|
17
|
+
'body-md-link': 'body-md-link',
|
|
18
|
+
'body-sm-reg': 'body-sm-reg',
|
|
19
|
+
'body-sm-md': 'body-sm-md',
|
|
20
|
+
'body-sm-semi-bold': 'body-sm-semi-bold',
|
|
21
|
+
'body-sm-bold': 'body-sm-bold',
|
|
22
|
+
'body-sm-link': 'body-sm-link',
|
|
23
|
+
'body-xs-reg': 'body-xs-reg',
|
|
24
|
+
'body-xs-md': 'body-xs-md',
|
|
25
|
+
'body-xs-semi-bold': 'body-xs-semi-bold',
|
|
26
|
+
'body-xs-bold': 'body-xs-bold',
|
|
27
|
+
'body-xs-link': 'body-xs-link',
|
|
28
|
+
'code-sm-reg': 'code-sm-reg',
|
|
29
|
+
'code-sm-semi-bold': 'code-sm-semi-bold',
|
|
30
|
+
'code-xs-reg': 'code-xs-reg',
|
|
31
|
+
'code-xs-semi-bold': 'code-xs-semi-bold',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const color = instance.getEnum('color', {
|
|
35
|
+
main: 'main',
|
|
36
|
+
secondary: 'secondary',
|
|
37
|
+
action: 'action',
|
|
38
|
+
'action-hover': 'action-hover',
|
|
39
|
+
'action-secondary': 'action-secondary',
|
|
40
|
+
'action-secondary-hover': 'action-secondary-hover',
|
|
41
|
+
disabled: 'disabled',
|
|
42
|
+
'light-disabled': 'light-disabled',
|
|
43
|
+
'on-action': 'on-action',
|
|
44
|
+
'on-disabled': 'on-disabled',
|
|
45
|
+
placeholder: 'placeholder',
|
|
46
|
+
highlight: 'highlight',
|
|
47
|
+
success: 'success',
|
|
48
|
+
warning: 'warning',
|
|
49
|
+
error: 'error',
|
|
50
|
+
code: 'code',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const textNode = instance.children.find((child): child is figma.TextHandle => child.type === 'TEXT');
|
|
54
|
+
const text = textNode?.textContent ?? 'Text';
|
|
55
|
+
|
|
56
|
+
const colorProp = color === 'main' ? '' : figma.code` color="${color}"`;
|
|
57
|
+
|
|
58
|
+
export default {
|
|
59
|
+
example: figma.code`<DsTypography variant="${variant}"${colorProp}>${text}</DsTypography>`,
|
|
60
|
+
imports: ["import { DsTypography } from '@drivenets/design-system'"],
|
|
61
|
+
id: 'ds-typography',
|
|
62
|
+
metadata: { nestable: true },
|
|
63
|
+
};
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
@use '../../styles/typography';
|
|
2
2
|
|
|
3
|
+
.truncateSingle {
|
|
4
|
+
display: block;
|
|
5
|
+
max-width: 100%;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
white-space: nowrap;
|
|
8
|
+
text-overflow: ellipsis;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.truncateMultiline {
|
|
12
|
+
display: -webkit-box;
|
|
13
|
+
-webkit-box-orient: vertical;
|
|
14
|
+
-webkit-line-clamp: var(--ds-typography-line-clamp);
|
|
15
|
+
line-clamp: var(--ds-typography-line-clamp);
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
}
|
|
18
|
+
|
|
3
19
|
.heading1 {
|
|
4
20
|
@include typography.heading1;
|
|
5
21
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { type CSSProperties, useRef } from 'react';
|
|
1
2
|
import classNames from 'classnames';
|
|
2
3
|
import type { DsTypographyProps } from './ds-typography.types';
|
|
3
4
|
import { typographyColors, typographyVariantConfig } from './ds-typography.config';
|
|
5
|
+
import { useTruncation } from './hooks/use-truncation';
|
|
6
|
+
import { mergeRefs } from '../../utils/merge-refs';
|
|
7
|
+
import { DsTooltip } from '../ds-tooltip';
|
|
4
8
|
import styles from './ds-typography.module.scss';
|
|
5
9
|
|
|
6
10
|
const semanticColorSet = new Set<string>(typographyColors);
|
|
@@ -8,6 +12,9 @@ const semanticColorSet = new Set<string>(typographyColors);
|
|
|
8
12
|
const resolveColor = (color: string): string =>
|
|
9
13
|
semanticColorSet.has(color) ? `var(--font-${color})` : color;
|
|
10
14
|
|
|
15
|
+
const resolveLineClamp = (truncate: boolean | number): number =>
|
|
16
|
+
typeof truncate === 'number' ? Math.max(1, Math.floor(truncate)) : 1;
|
|
17
|
+
|
|
11
18
|
/*
|
|
12
19
|
* Design system Typography component that provides consistent text styling
|
|
13
20
|
*/
|
|
@@ -18,25 +25,75 @@ const DsTypography = ({
|
|
|
18
25
|
className,
|
|
19
26
|
style,
|
|
20
27
|
color,
|
|
28
|
+
truncate = false,
|
|
29
|
+
tooltip = false,
|
|
30
|
+
tooltipContent,
|
|
21
31
|
children,
|
|
22
32
|
...props
|
|
23
33
|
}: DsTypographyProps) => {
|
|
24
34
|
const Component = typographyVariantConfig[variant].component;
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
const internalRef = useRef<HTMLElement>(null);
|
|
37
|
+
|
|
38
|
+
const truncationEnabled = truncate !== false;
|
|
39
|
+
const lineClamp = resolveLineClamp(truncate);
|
|
40
|
+
const isMultiline = truncationEnabled && lineClamp > 1;
|
|
41
|
+
|
|
42
|
+
const tooltipEnabled = truncationEnabled && tooltip;
|
|
43
|
+
const { isTruncated, getAnchorRect } = useTruncation(internalRef, tooltipEnabled, [children, truncate]);
|
|
44
|
+
|
|
45
|
+
let resolvedStyle: CSSProperties | undefined;
|
|
46
|
+
|
|
47
|
+
if (color || isMultiline || style) {
|
|
48
|
+
resolvedStyle = {};
|
|
49
|
+
|
|
50
|
+
if (color) {
|
|
51
|
+
resolvedStyle.color = resolveColor(color);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (isMultiline) {
|
|
55
|
+
Object.assign(resolvedStyle, { '--ds-typography-line-clamp': lineClamp });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (style) {
|
|
59
|
+
Object.assign(resolvedStyle, style);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const truncateClass = truncationEnabled
|
|
64
|
+
? isMultiline
|
|
65
|
+
? styles.truncateMultiline
|
|
66
|
+
: styles.truncateSingle
|
|
67
|
+
: undefined;
|
|
68
|
+
|
|
69
|
+
const element = (
|
|
27
70
|
<Component
|
|
28
|
-
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
30
|
-
// @ts-expect-error
|
|
31
|
-
ref={ref}
|
|
71
|
+
ref={mergeRefs(ref, internalRef)}
|
|
32
72
|
asChild={asChild}
|
|
33
|
-
className={classNames(styles[variant], className)}
|
|
34
|
-
style={
|
|
73
|
+
className={classNames(styles[variant], truncateClass, className)}
|
|
74
|
+
style={resolvedStyle}
|
|
35
75
|
{...props}
|
|
36
76
|
>
|
|
37
77
|
{children}
|
|
38
78
|
</Component>
|
|
39
79
|
);
|
|
80
|
+
|
|
81
|
+
if (!tooltipEnabled) {
|
|
82
|
+
return element;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<DsTooltip
|
|
87
|
+
content={tooltipContent ?? children}
|
|
88
|
+
placement="top"
|
|
89
|
+
disabled={!isTruncated}
|
|
90
|
+
getAnchorRect={getAnchorRect}
|
|
91
|
+
>
|
|
92
|
+
{element}
|
|
93
|
+
</DsTooltip>
|
|
94
|
+
);
|
|
40
95
|
};
|
|
41
96
|
|
|
97
|
+
DsTypography.displayName = 'DsTypography';
|
|
98
|
+
|
|
42
99
|
export default DsTypography;
|
|
@@ -31,6 +31,19 @@ export interface DsTypographyProps {
|
|
|
31
31
|
* When true, composes typography styles onto the child element without extra DOM nodes
|
|
32
32
|
*/
|
|
33
33
|
asChild?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Truncates overflowing text with an ellipsis. A number clamps to that many lines
|
|
36
|
+
* (`true` is a single line). Needs a width-constrained layout context.
|
|
37
|
+
*/
|
|
38
|
+
truncate?: boolean | number;
|
|
39
|
+
/**
|
|
40
|
+
* Reveals the full text in a tooltip on hover/focus, but only while it overflows.
|
|
41
|
+
*/
|
|
42
|
+
tooltip?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Tooltip content override; defaults to `children`.
|
|
45
|
+
*/
|
|
46
|
+
tooltipContent?: React.ReactNode;
|
|
34
47
|
/**
|
|
35
48
|
* The content of the component
|
|
36
49
|
*/
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { type RefObject, useCallback, useLayoutEffect, useState } from 'react';
|
|
2
|
+
import type { TooltipAnchorRect } from '../../ds-tooltip';
|
|
3
|
+
|
|
4
|
+
const OVERFLOW_TOLERANCE_PX = 1;
|
|
5
|
+
const FALLBACK_LINE_HEIGHT_RATIO = 1.2;
|
|
6
|
+
const DEFAULT_FONT_SIZE_PX = 16;
|
|
7
|
+
const ELLIPSIS = '\u2026';
|
|
8
|
+
|
|
9
|
+
interface Truncation {
|
|
10
|
+
isTruncated: boolean;
|
|
11
|
+
getAnchorRect: () => TooltipAnchorRect | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ellipsisWidthCache = new Map<string, number>();
|
|
15
|
+
let ellipsisMeasureSpan: HTMLSpanElement | null = null;
|
|
16
|
+
|
|
17
|
+
const measureEllipsisWidth = (computed: CSSStyleDeclaration): number => {
|
|
18
|
+
const cacheKey = `${computed.fontStyle}|${computed.fontWeight}|${computed.fontSize}|${computed.fontFamily}|${computed.letterSpacing}`;
|
|
19
|
+
const cached = ellipsisWidthCache.get(cacheKey);
|
|
20
|
+
|
|
21
|
+
if (cached !== undefined) {
|
|
22
|
+
return cached;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
ellipsisMeasureSpan ??= document.createElement('span');
|
|
26
|
+
const span = ellipsisMeasureSpan;
|
|
27
|
+
|
|
28
|
+
span.textContent = ELLIPSIS;
|
|
29
|
+
span.style.position = 'absolute';
|
|
30
|
+
span.style.visibility = 'hidden';
|
|
31
|
+
span.style.whiteSpace = 'pre';
|
|
32
|
+
span.style.fontStyle = computed.fontStyle;
|
|
33
|
+
span.style.fontWeight = computed.fontWeight;
|
|
34
|
+
span.style.fontSize = computed.fontSize;
|
|
35
|
+
span.style.fontFamily = computed.fontFamily;
|
|
36
|
+
span.style.letterSpacing = computed.letterSpacing;
|
|
37
|
+
|
|
38
|
+
document.body.appendChild(span);
|
|
39
|
+
const width = span.getBoundingClientRect().width;
|
|
40
|
+
span.remove();
|
|
41
|
+
|
|
42
|
+
ellipsisWidthCache.set(cacheKey, width);
|
|
43
|
+
return width;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let measureRange: Range | null = null;
|
|
47
|
+
|
|
48
|
+
const measureFittingPrefixRight = (textNode: Text, maxRight: number): number => {
|
|
49
|
+
measureRange ??= document.createRange();
|
|
50
|
+
const range = measureRange;
|
|
51
|
+
|
|
52
|
+
let low = 0;
|
|
53
|
+
let high = textNode.length;
|
|
54
|
+
let fittingLength = 0;
|
|
55
|
+
|
|
56
|
+
while (low <= high) {
|
|
57
|
+
const mid = (low + high) >>> 1;
|
|
58
|
+
range.setStart(textNode, 0);
|
|
59
|
+
range.setEnd(textNode, mid);
|
|
60
|
+
|
|
61
|
+
if (range.getBoundingClientRect().right <= maxRight) {
|
|
62
|
+
fittingLength = mid;
|
|
63
|
+
low = mid + 1;
|
|
64
|
+
} else {
|
|
65
|
+
high = mid - 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
range.setStart(textNode, 0);
|
|
70
|
+
range.setEnd(textNode, fittingLength);
|
|
71
|
+
return range.getBoundingClientRect().right;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const getFirstTextNode = (element: HTMLElement): Text | null =>
|
|
75
|
+
element.firstChild instanceof Text ? element.firstChild : null;
|
|
76
|
+
|
|
77
|
+
export const useTruncation = (
|
|
78
|
+
ref: RefObject<HTMLElement | null>,
|
|
79
|
+
enabled: boolean,
|
|
80
|
+
deps: readonly unknown[] = [],
|
|
81
|
+
): Truncation => {
|
|
82
|
+
const [isTruncated, setIsTruncated] = useState(false);
|
|
83
|
+
|
|
84
|
+
useLayoutEffect(() => {
|
|
85
|
+
const element = ref.current;
|
|
86
|
+
|
|
87
|
+
if (!enabled || !element) {
|
|
88
|
+
setIsTruncated(false);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const measure = () => {
|
|
93
|
+
const overflowsHorizontally = element.scrollWidth - element.clientWidth > OVERFLOW_TOLERANCE_PX;
|
|
94
|
+
const overflowsVertically = element.scrollHeight - element.clientHeight > OVERFLOW_TOLERANCE_PX;
|
|
95
|
+
|
|
96
|
+
setIsTruncated(overflowsHorizontally || overflowsVertically);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const rafId = requestAnimationFrame(measure);
|
|
100
|
+
|
|
101
|
+
const resizeObserver = new ResizeObserver(() => requestAnimationFrame(measure));
|
|
102
|
+
resizeObserver.observe(element);
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
cancelAnimationFrame(rafId);
|
|
106
|
+
resizeObserver.disconnect();
|
|
107
|
+
};
|
|
108
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
109
|
+
}, [ref, enabled, ...deps]);
|
|
110
|
+
|
|
111
|
+
const getAnchorRect = useCallback((): TooltipAnchorRect | null => {
|
|
112
|
+
const element = ref.current;
|
|
113
|
+
|
|
114
|
+
if (!element) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const rect = element.getBoundingClientRect();
|
|
119
|
+
const computed = getComputedStyle(element);
|
|
120
|
+
|
|
121
|
+
const paddingRight = parseFloat(computed.paddingRight) || 0;
|
|
122
|
+
const borderRight = parseFloat(computed.borderRightWidth) || 0;
|
|
123
|
+
const paddingBottom = parseFloat(computed.paddingBottom) || 0;
|
|
124
|
+
const borderBottom = parseFloat(computed.borderBottomWidth) || 0;
|
|
125
|
+
|
|
126
|
+
const fontSize = parseFloat(computed.fontSize) || DEFAULT_FONT_SIZE_PX;
|
|
127
|
+
const lineHeight = parseFloat(computed.lineHeight) || fontSize * FALLBACK_LINE_HEIGHT_RATIO;
|
|
128
|
+
|
|
129
|
+
const contentRight = rect.right - paddingRight - borderRight;
|
|
130
|
+
const ellipsisWidth = measureEllipsisWidth(computed);
|
|
131
|
+
|
|
132
|
+
const textNode = getFirstTextNode(element);
|
|
133
|
+
const isSingleLine = computed.whiteSpace === 'nowrap';
|
|
134
|
+
|
|
135
|
+
const ellipsisCenterX =
|
|
136
|
+
isSingleLine && textNode
|
|
137
|
+
? measureFittingPrefixRight(textNode, contentRight - ellipsisWidth) + ellipsisWidth / 2
|
|
138
|
+
: contentRight - ellipsisWidth / 2;
|
|
139
|
+
|
|
140
|
+
const lastLineBottom = rect.bottom - paddingBottom - borderBottom;
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
x: ellipsisCenterX,
|
|
144
|
+
y: lastLineBottom - lineHeight,
|
|
145
|
+
width: 0,
|
|
146
|
+
height: lineHeight,
|
|
147
|
+
};
|
|
148
|
+
}, [ref]);
|
|
149
|
+
|
|
150
|
+
return { isTruncated, getAnchorRect };
|
|
151
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=38540-44159
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-user-card
|
|
3
|
+
// component=DsUserCard
|
|
4
|
+
//
|
|
5
|
+
// `DAP_user-menu-header_v01` (#38540:44159) is a plain composed instance with no
|
|
6
|
+
// variant props, so it maps directly to `DsUserCard`. `name` and `subtitle` are text
|
|
7
|
+
// content and `src` is a photo URL — none are derivable from Figma — so they are
|
|
8
|
+
// emitted as placeholders for the developer to fill in (see `ds-select.figma.ts`).
|
|
9
|
+
import figma from 'figma';
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
example: figma.code`<DsUserCard name="Full Name" subtitle="subtitle" />`,
|
|
13
|
+
imports: ["import { DsUserCard } from '@drivenets/design-system'"],
|
|
14
|
+
id: 'ds-user-card',
|
|
15
|
+
metadata: { nestable: true },
|
|
16
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.userCard {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--xs);
|
|
5
|
+
padding: var(--sm);
|
|
6
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.text {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
flex: 1;
|
|
13
|
+
gap: var(--3xs);
|
|
14
|
+
padding-top: var(--4xs);
|
|
15
|
+
min-width: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.name,
|
|
19
|
+
.subtitle {
|
|
20
|
+
display: block;
|
|
21
|
+
max-width: 100%;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
white-space: nowrap;
|
|
24
|
+
text-overflow: ellipsis;
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import styles from './ds-user-card.module.scss';
|
|
4
|
+
import type { DsUserCardProps } from './ds-user-card.types';
|
|
5
|
+
import { DsAvatar } from '../ds-avatar';
|
|
6
|
+
import { DsTypography } from '../ds-typography';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @summary Compact identity header pairing an avatar with a name and optional subtitle.
|
|
10
|
+
*
|
|
11
|
+
* Presentational card used as the header of a user menu. Renders a medium
|
|
12
|
+
* `DsAvatar` (initials fallback derived from `name`) beside a two-line text
|
|
13
|
+
* block; both lines truncate with an ellipsis when they overflow.
|
|
14
|
+
*/
|
|
15
|
+
export const DsUserCard: FC<DsUserCardProps> = ({ name, subtitle, src, className, style }) => {
|
|
16
|
+
return (
|
|
17
|
+
<div className={classNames(styles.userCard, className)} style={style}>
|
|
18
|
+
<DsAvatar name={name} src={src} size="md" />
|
|
19
|
+
<div className={styles.text}>
|
|
20
|
+
<DsTypography variant="heading4" color="main" className={styles.name}>
|
|
21
|
+
{name}
|
|
22
|
+
</DsTypography>
|
|
23
|
+
{subtitle && (
|
|
24
|
+
<DsTypography variant="body-xs-reg" color="secondary" className={styles.subtitle}>
|
|
25
|
+
{subtitle}
|
|
26
|
+
</DsTypography>
|
|
27
|
+
)}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface DsUserCardProps {
|
|
4
|
+
/**
|
|
5
|
+
* Full name shown as the title. Also drives the avatar initials fallback,
|
|
6
|
+
* image alt text, and hover tooltip.
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Secondary line rendered under the name, such as an email address or role.
|
|
11
|
+
*/
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Avatar image URL. Falls back to initials derived from `name` when omitted
|
|
15
|
+
* or when the image fails to load.
|
|
16
|
+
*/
|
|
17
|
+
src?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Custom class name applied to the root element.
|
|
20
|
+
*/
|
|
21
|
+
className?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Custom inline styles applied to the root element.
|
|
24
|
+
*/
|
|
25
|
+
style?: CSSProperties;
|
|
26
|
+
}
|
|
@@ -73,8 +73,14 @@ const DsVerticalTabsContent = ({ value, className, children }: DsVerticalTabsCon
|
|
|
73
73
|
);
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
+
DsVerticalTabsList.displayName = 'DsVerticalTabs.List';
|
|
77
|
+
DsVerticalTabsTab.displayName = 'DsVerticalTabs.Tab';
|
|
78
|
+
DsVerticalTabsContent.displayName = 'DsVerticalTabs.Content';
|
|
79
|
+
|
|
76
80
|
DsVerticalTabs.List = DsVerticalTabsList;
|
|
77
81
|
DsVerticalTabs.Tab = DsVerticalTabsTab;
|
|
78
82
|
DsVerticalTabs.Content = DsVerticalTabsContent;
|
|
79
83
|
|
|
84
|
+
DsVerticalTabs.displayName = 'DsVerticalTabs';
|
|
85
|
+
|
|
80
86
|
export default DsVerticalTabs;
|
|
@@ -124,6 +124,14 @@ const LeftPanel = ({ className, ...rest }: DsWorkspaceLayoutLeftPanelProps) => {
|
|
|
124
124
|
return <aside {...rest} className={classNames(styles.leftPanel, className)} />;
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
+
Header.displayName = 'DsWorkspaceLayout.Header';
|
|
128
|
+
SubHeader.displayName = 'DsWorkspaceLayout.SubHeader';
|
|
129
|
+
Content.displayName = 'DsWorkspaceLayout.Content';
|
|
130
|
+
Footer.displayName = 'DsWorkspaceLayout.Footer';
|
|
131
|
+
Body.displayName = 'DsWorkspaceLayout.Body';
|
|
132
|
+
SideMenu.displayName = 'DsWorkspaceLayout.SideMenu';
|
|
133
|
+
LeftPanel.displayName = 'DsWorkspaceLayout.LeftPanel';
|
|
134
|
+
|
|
127
135
|
DsWorkspaceLayout.Header = Header;
|
|
128
136
|
DsWorkspaceLayout.SubHeader = SubHeader;
|
|
129
137
|
DsWorkspaceLayout.Content = Content;
|
|
@@ -132,4 +140,6 @@ DsWorkspaceLayout.Body = Body;
|
|
|
132
140
|
DsWorkspaceLayout.SideMenu = SideMenu;
|
|
133
141
|
DsWorkspaceLayout.LeftPanel = LeftPanel;
|
|
134
142
|
|
|
143
|
+
DsWorkspaceLayout.displayName = 'DsWorkspaceLayout';
|
|
144
|
+
|
|
135
145
|
export default DsWorkspaceLayout;
|
package/src/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ export * from './components/ds-progress-task-bar';
|
|
|
43
43
|
export * from './components/ds-radio-group';
|
|
44
44
|
export * from './components/ds-segment-group';
|
|
45
45
|
export * from './components/ds-select';
|
|
46
|
+
export * from './components/ds-site-node';
|
|
46
47
|
export * from './components/ds-skeleton';
|
|
47
48
|
export * from './components/ds-smart-tabs';
|
|
48
49
|
export * from './components/ds-spinner';
|
|
@@ -64,6 +65,7 @@ export * from './components/ds-toggle';
|
|
|
64
65
|
export * from './components/ds-tooltip';
|
|
65
66
|
export * from './components/ds-tree';
|
|
66
67
|
export * from './components/ds-typography';
|
|
68
|
+
export * from './components/ds-user-card';
|
|
67
69
|
export * from './components/ds-vertical-tabs';
|
|
68
70
|
export * from './components/ds-workspace-layout';
|
|
69
71
|
export * from './utils/responsive';
|