@drivenets/design-system 0.12.1 → 0.14.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 +31 -0
- package/dist/index.cjs +1989 -735
- package/dist/index.d.cts +1378 -998
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1378 -998
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1978 -735
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/package.json +43 -41
- package/src/components/ds-button-v3/ds-button-v3.figma.ts +99 -0
- package/src/components/ds-button-v3/ds-button-v3.module.scss +22 -22
- package/src/components/ds-card/figma/ds-card-content.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card-footer.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card-header.figma.ts +15 -0
- package/src/components/ds-card/figma/ds-card.figma.ts +72 -0
- package/src/components/ds-date-input/ds-date-input.utils.ts +2 -2
- package/src/components/ds-date-picker/ds-date-picker.figma.ts +19 -0
- package/src/components/ds-drawer/ds-drawer.tsx +2 -0
- package/src/components/ds-form-control/ds-form-control.figma.ts +76 -0
- package/src/components/ds-form-control/ds-form-control.tsx +14 -11
- package/src/components/ds-form-control/ds-form-control.types.ts +4 -0
- package/src/components/ds-grid/ds-grid.tsx +1 -2
- package/src/components/ds-icon/figma/ds-icon-material.figma.batch.ts +10 -0
- package/src/components/ds-icon/figma/ds-icon-special.figma.batch.ts +18 -0
- package/src/components/ds-icon/figma/ds-icon.figma.batch.json +2873 -0
- package/src/components/ds-modal/ds-modal.tsx +3 -0
- package/src/components/ds-modal/figma/ds-modal-content.figma.ts +18 -0
- package/src/components/ds-modal/figma/ds-modal-footer.figma.ts +19 -0
- package/src/components/ds-modal/figma/ds-modal-header.figma.ts +21 -0
- package/src/components/ds-modal/figma/ds-modal.figma.ts +36 -0
- package/src/components/ds-popover/ds-popover.module.scss +58 -16
- package/src/components/ds-popover/ds-popover.tsx +148 -24
- package/src/components/ds-popover/ds-popover.types.ts +95 -12
- package/src/components/ds-popover/index.ts +1 -1
- package/src/components/ds-select/ds-select.figma.ts +53 -0
- package/src/components/ds-split-button/ds-split-button.figma.ts +95 -0
- package/src/components/ds-status-badge-v2/ds-status-badge-v2.figma.ts +41 -0
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +21 -2
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +22 -14
- package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +1 -6
- package/src/components/ds-table/components/ds-table-bulk-actions/ds-table-bulk-actions.module.scss +2 -2
- package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +2 -0
- package/src/components/ds-table/components/ds-table-cell/ds-table-cell.tsx +42 -11
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.module.scss +31 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.tsx +48 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/ds-table-group-header-cell.types.ts +12 -0
- package/src/components/ds-table/components/ds-table-group-header-cell/index.ts +2 -0
- package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +28 -4
- package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +65 -28
- package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +26 -0
- package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +3 -0
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +23 -0
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +4 -1
- package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.types.ts +1 -1
- package/src/components/ds-table/components/edit/cell-editors/cell-editors.module.scss +3 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-checkbox.tsx +27 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-date.tsx +50 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-number.tsx +51 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-select.tsx +36 -0
- package/src/components/ds-table/components/edit/cell-editors/ds-table-edit-cell-text.tsx +50 -0
- package/src/components/ds-table/components/edit/cell-editors/index.ts +14 -0
- package/src/components/ds-table/components/edit/cell-editors/table-edit-form-control.tsx +31 -0
- package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.module.scss +28 -0
- package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.tsx +39 -0
- package/src/components/ds-table/components/edit/ds-table-editable-cell/ds-table-editable-cell.types.ts +7 -0
- package/src/components/ds-table/components/edit/ds-table-editable-cell/index.ts +2 -0
- package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.module.scss +48 -0
- package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.tsx +76 -0
- package/src/components/ds-table/components/edit/ds-table-editing-cell/ds-table-editing-cell.types.ts +5 -0
- package/src/components/ds-table/components/edit/ds-table-editing-cell/index.ts +2 -0
- package/src/components/ds-table/context/ds-table-context.ts +83 -1
- package/src/components/ds-table/ds-table-column.types.ts +12 -0
- package/src/components/ds-table/ds-table.module.scss +100 -10
- package/src/components/ds-table/ds-table.tsx +38 -21
- package/src/components/ds-table/ds-table.types.ts +107 -0
- package/src/components/ds-table/figma/ds-table-expandable.figma.ts +17 -0
- package/src/components/ds-table/figma/ds-table.figma.ts +16 -0
- package/src/components/ds-table/filters/adapters/create-checkbox-filter-adapter.tsx +24 -40
- package/src/components/ds-table/filters/adapters/create-dual-range-filter-adapter.tsx +10 -31
- package/src/components/ds-table/filters/adapters/create-filter-adapter.ts +6 -98
- package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.module.scss +36 -0
- package/src/components/ds-table/filters/components/checkbox-filter/checkbox-filter.tsx +92 -13
- package/src/components/ds-table/filters/components/checkbox-filter/index.ts +1 -1
- package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.module.scss +42 -0
- package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.tsx +89 -0
- package/src/components/ds-table/filters/components/column-filter-popover/column-filter-popover.types.ts +25 -0
- package/src/components/ds-table/filters/components/column-filter-popover/index.ts +5 -0
- package/src/components/ds-table/filters/hooks/use-table-filters.ts +113 -78
- package/src/components/ds-table/filters/index.ts +2 -44
- package/src/components/ds-table/filters/types/column-meta.augmentation.ts +19 -0
- package/src/components/ds-table/filters/types/filter-adapter.types.ts +48 -81
- package/src/components/ds-table/grouping/hooks/use-column-groups.ts +73 -0
- package/src/components/ds-table/grouping/index.ts +5 -0
- package/src/components/ds-table/grouping/types/column-group-meta.augmentation.ts +26 -0
- package/src/components/ds-table/grouping/types/column-group.types.ts +35 -0
- package/src/components/ds-table/grouping/utils/column-group.ts +119 -0
- package/src/components/ds-table/hooks/use-cell-editor.ts +81 -0
- package/src/components/ds-table/index.ts +32 -0
- package/src/components/ds-table/styles/_variables.scss +1 -0
- package/src/components/ds-table/utils/column-size.ts +9 -8
- package/src/components/ds-tag/ds-tag.module.scss +22 -1
- package/src/components/ds-tag/ds-tag.tsx +18 -5
- package/src/components/ds-tag/ds-tag.types.ts +22 -8
- package/src/components/ds-tag/ds-tag.unit.test.ts +32 -0
- package/src/components/ds-tag-filter/ds-tag-filter.tsx +14 -22
- package/src/components/ds-text-input/ds-text-input.figma.ts +60 -0
- package/src/components/ds-time-picker/ds-time-picker.figma.ts +18 -0
- package/src/components/ds-workspace-layout/ds-workspace-layout.context.ts +19 -0
- package/src/components/ds-workspace-layout/ds-workspace-layout.module.scss +166 -0
- package/src/components/ds-workspace-layout/ds-workspace-layout.tsx +135 -0
- package/src/components/ds-workspace-layout/ds-workspace-layout.types.ts +52 -0
- package/src/components/ds-workspace-layout/index.ts +11 -0
- package/src/index.ts +1 -1
- package/src/styles/_root_new.scss +2 -0
- package/src/styles/_scrollbars.scss +36 -22
- package/src/utils/restore-document-body-modal-styles.ts +44 -0
- package/src/utils/use-ark-dialog-body-lock-cleanup.ts +36 -0
- package/src/components/ds-workspace/ds-workspace.module.scss +0 -55
- package/src/components/ds-workspace/ds-workspace.tsx +0 -37
- package/src/components/ds-workspace/ds-workspace.types.ts +0 -17
- package/src/components/ds-workspace/index.ts +0 -8
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
type Column,
|
|
4
|
+
type ColumnDef,
|
|
5
|
+
type VisibilityState,
|
|
6
|
+
defaultColumnSizing,
|
|
7
|
+
} from '@tanstack/react-table';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builds the flex style for a group header cell so it lines up with the combined
|
|
11
|
+
* width of its visible leaf columns.
|
|
12
|
+
*
|
|
13
|
+
* Fixed-width leaves contribute to `flexBasis`; default-sized leaves each add one
|
|
14
|
+
* unit of `flexGrow`, mirroring how {@link getColumnSizeStyle} sizes leaf cells so
|
|
15
|
+
* both header rows distribute free space identically.
|
|
16
|
+
*/
|
|
17
|
+
export const getGroupColumnSizeStyle = <TData, TValue>(
|
|
18
|
+
leafColumns: Column<TData, TValue>[],
|
|
19
|
+
): CSSProperties => {
|
|
20
|
+
let fixedWidth = 0;
|
|
21
|
+
let flexCount = 0;
|
|
22
|
+
|
|
23
|
+
for (const leaf of leafColumns) {
|
|
24
|
+
const size = leaf.getSize();
|
|
25
|
+
if (size !== defaultColumnSizing.size) {
|
|
26
|
+
fixedWidth += size;
|
|
27
|
+
} else {
|
|
28
|
+
flexCount += 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (flexCount === 0) {
|
|
33
|
+
return { width: fixedWidth, minWidth: fixedWidth, flexShrink: 0 };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { flexGrow: flexCount, flexShrink: 0, flexBasis: fixedWidth, minWidth: fixedWidth };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether a column is the first visible leaf of its parent group. Used to draw a
|
|
41
|
+
* single side border at the start of each group in both the header and body.
|
|
42
|
+
*/
|
|
43
|
+
export const isFirstLeafColumnOfGroup = <TData, TValue>(column: Column<TData, TValue>): boolean => {
|
|
44
|
+
const parent = column.parent;
|
|
45
|
+
if (!parent) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const visibleLeaves = parent.getLeafColumns().filter((leaf) => leaf.getIsVisible());
|
|
50
|
+
|
|
51
|
+
return visibleLeaves[0]?.id === column.id;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type GroupColumnDef<TData, TValue> = ColumnDef<TData, TValue> & {
|
|
55
|
+
id?: string;
|
|
56
|
+
accessorKey?: string;
|
|
57
|
+
columns?: ColumnDef<TData, TValue>[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getLeafColumnId = <TData, TValue>(def: GroupColumnDef<TData, TValue>): string | undefined =>
|
|
61
|
+
def.id ?? def.accessorKey;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Collects the ids of groups declared with `meta.group.defaultCollapsed`, used to
|
|
65
|
+
* seed the uncontrolled collapse state.
|
|
66
|
+
*/
|
|
67
|
+
export const getInitialCollapsedGroups = <TData, TValue>(columns: ColumnDef<TData, TValue>[]): string[] => {
|
|
68
|
+
const collapsed: string[] = [];
|
|
69
|
+
|
|
70
|
+
const visit = (defs: ColumnDef<TData, TValue>[]) => {
|
|
71
|
+
for (const def of defs as GroupColumnDef<TData, TValue>[]) {
|
|
72
|
+
if (def.columns) {
|
|
73
|
+
if (def.id && def.meta?.group?.defaultCollapsed) {
|
|
74
|
+
collapsed.push(def.id);
|
|
75
|
+
}
|
|
76
|
+
visit(def.columns);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
visit(columns);
|
|
82
|
+
|
|
83
|
+
return collapsed;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Maps collapsed groups to a {@link VisibilityState} that hides every leaf column
|
|
88
|
+
* inside a collapsed group except those flagged `meta.keepVisibleWhenCollapsed`.
|
|
89
|
+
* Layered on top of the consumer's own visibility so collapse never mutates it.
|
|
90
|
+
*/
|
|
91
|
+
export const getCollapsedColumnVisibility = <TData, TValue>(
|
|
92
|
+
columns: ColumnDef<TData, TValue>[],
|
|
93
|
+
collapsedGroupIds: string[],
|
|
94
|
+
): VisibilityState => {
|
|
95
|
+
const visibility: VisibilityState = {};
|
|
96
|
+
|
|
97
|
+
const visit = (defs: ColumnDef<TData, TValue>[], isInsideCollapsedGroup: boolean) => {
|
|
98
|
+
for (const def of defs as GroupColumnDef<TData, TValue>[]) {
|
|
99
|
+
if (def.columns) {
|
|
100
|
+
const isGroupCollapsed = isInsideCollapsedGroup || (!!def.id && collapsedGroupIds.includes(def.id));
|
|
101
|
+
visit(def.columns, isGroupCollapsed);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!isInsideCollapsedGroup || def.meta?.keepVisibleWhenCollapsed) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const columnId = getLeafColumnId(def);
|
|
110
|
+
if (columnId) {
|
|
111
|
+
visibility[columnId] = false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
visit(columns, false);
|
|
117
|
+
|
|
118
|
+
return visibility;
|
|
119
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type KeyboardEventHandler, type MouseEvent, type MouseEventHandler } from 'react';
|
|
2
|
+
import type { CellContext, RowData } from '@tanstack/react-table';
|
|
3
|
+
import { useDsTableContext } from '../context/ds-table-context';
|
|
4
|
+
|
|
5
|
+
export interface UseCellEditorOptions<TData extends RowData, TValue> {
|
|
6
|
+
/**
|
|
7
|
+
* The TanStack Table CellContext for this cell. Pass through
|
|
8
|
+
* the argument received in `ColumnDef.editCell`.
|
|
9
|
+
*/
|
|
10
|
+
cellContext: CellContext<TData, TValue>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseCellEditorContainerProps {
|
|
14
|
+
onKeyDown: KeyboardEventHandler<HTMLElement>;
|
|
15
|
+
onClick: MouseEventHandler;
|
|
16
|
+
onDoubleClick: MouseEventHandler;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UseCellEditorResult<TValue> {
|
|
20
|
+
/** The in-progress draft value for the active editor. */
|
|
21
|
+
value: TValue;
|
|
22
|
+
/** Validation error from the most recent commit attempt, if any. */
|
|
23
|
+
error: string | null;
|
|
24
|
+
/** Update the in-progress draft value. */
|
|
25
|
+
setValue: (value: TValue) => void;
|
|
26
|
+
/** Validate and commit the draft. */
|
|
27
|
+
commit: (overrideValue?: TValue) => void;
|
|
28
|
+
/** Discard the draft and exit edit mode. */
|
|
29
|
+
cancel: () => void;
|
|
30
|
+
/** Stop mouse events from triggering row-level handlers. */
|
|
31
|
+
stopPropagation: (event: MouseEvent) => void;
|
|
32
|
+
/** Spread on the custom `editCell` root for pointer isolation from row handlers. */
|
|
33
|
+
editorContainerProps: UseCellEditorContainerProps;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Wires a TanStack Table `editCell` renderer into DsTable's hoisted editing state.
|
|
38
|
+
* Only mount this hook inside `ColumnDef.editCell` — the table cell layer handles
|
|
39
|
+
* entering edit mode on double-click.
|
|
40
|
+
*/
|
|
41
|
+
export const useCellEditor = <TData extends RowData, TValue>({
|
|
42
|
+
cellContext,
|
|
43
|
+
}: UseCellEditorOptions<TData, TValue>): UseCellEditorResult<TValue> => {
|
|
44
|
+
const ctx = useDsTableContext<TData, TValue>();
|
|
45
|
+
const { row, column, getValue } = cellContext;
|
|
46
|
+
|
|
47
|
+
const isActiveCell =
|
|
48
|
+
ctx.editing !== null && ctx.editing.cell.row.id === row.id && ctx.editing.cell.column.id === column.id;
|
|
49
|
+
const value = isActiveCell && ctx.editing ? ctx.editing.draftValue : getValue();
|
|
50
|
+
const error = isActiveCell && ctx.editing ? ctx.editing.error : null;
|
|
51
|
+
|
|
52
|
+
const setValue = (next: TValue) => {
|
|
53
|
+
ctx.setDraft(next);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const commit = (overrideValue?: TValue) => {
|
|
57
|
+
ctx.commit(overrideValue);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const cancel = () => {
|
|
61
|
+
ctx.cancel();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const stopPropagation = (event: MouseEvent) => {
|
|
65
|
+
event.stopPropagation();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
value,
|
|
70
|
+
error,
|
|
71
|
+
setValue,
|
|
72
|
+
commit,
|
|
73
|
+
cancel,
|
|
74
|
+
stopPropagation,
|
|
75
|
+
editorContainerProps: {
|
|
76
|
+
onKeyDown: () => {},
|
|
77
|
+
onClick: stopPropagation,
|
|
78
|
+
onDoubleClick: stopPropagation,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -1,7 +1,39 @@
|
|
|
1
|
+
import './grouping/types/column-group-meta.augmentation';
|
|
2
|
+
|
|
1
3
|
export type { SecondaryRowAction, RowAction } from './components/ds-table-cell';
|
|
2
4
|
export type { ScrollParams } from './components/ds-table-body-virtualized';
|
|
3
5
|
|
|
4
6
|
export * from './filters';
|
|
7
|
+
export type { DsColumnGroupMeta } from './grouping';
|
|
5
8
|
|
|
6
9
|
export { default as DsTable } from './ds-table';
|
|
7
10
|
export * from './ds-table.types';
|
|
11
|
+
|
|
12
|
+
export { useCellEditor } from './hooks/use-cell-editor';
|
|
13
|
+
export type {
|
|
14
|
+
UseCellEditorOptions,
|
|
15
|
+
UseCellEditorResult,
|
|
16
|
+
UseCellEditorContainerProps,
|
|
17
|
+
} from './hooks/use-cell-editor';
|
|
18
|
+
|
|
19
|
+
export { DsTableEditableCell } from './components/edit/ds-table-editable-cell';
|
|
20
|
+
export type { DsTableEditableCellProps } from './components/edit/ds-table-editable-cell';
|
|
21
|
+
|
|
22
|
+
export { DsTableEditingCell } from './components/edit/ds-table-editing-cell';
|
|
23
|
+
export type { DsTableEditingCellProps } from './components/edit/ds-table-editing-cell';
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
DsTableEditCellText,
|
|
27
|
+
DsTableEditCellNumber,
|
|
28
|
+
DsTableEditCellSelect,
|
|
29
|
+
DsTableEditCellDate,
|
|
30
|
+
DsTableEditCellCheckbox,
|
|
31
|
+
} from './components/edit/cell-editors';
|
|
32
|
+
export type {
|
|
33
|
+
DsTableEditCellTextProps,
|
|
34
|
+
DsTableEditCellNumberProps,
|
|
35
|
+
DsTableEditCellSelectProps,
|
|
36
|
+
DsTableEditCellDateProps,
|
|
37
|
+
DsTableEditCellDateValue,
|
|
38
|
+
DsTableEditCellCheckboxProps,
|
|
39
|
+
} from './components/edit/cell-editors';
|
|
@@ -2,19 +2,20 @@ import type { CSSProperties } from 'react';
|
|
|
2
2
|
import { defaultColumnSizing } from '@tanstack/react-table';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Generates the appropriate style object for a table column/cell based on its size
|
|
5
|
+
* Generates the appropriate style object for a table column/cell based on its size
|
|
6
|
+
*
|
|
7
|
+
* Custom-sized columns get a fixed `width` plus `minWidth` and `flexShrink: 0` so
|
|
8
|
+
* they enforce horizontal overflow on the table container. Default-sized columns
|
|
9
|
+
* grow to fill the row evenly via `flex: 1`.
|
|
6
10
|
*
|
|
7
11
|
* @param columnSize - The size of the column
|
|
8
|
-
* @
|
|
9
|
-
* @returns Style object or undefined if no custom styling is needed
|
|
12
|
+
* @returns Style object for the column/cell
|
|
10
13
|
*/
|
|
11
|
-
export const getColumnSizeStyle = (columnSize: number
|
|
14
|
+
export const getColumnSizeStyle = (columnSize: number): CSSProperties => {
|
|
12
15
|
const hasCustomSize = columnSize !== defaultColumnSizing.size;
|
|
13
16
|
if (hasCustomSize) {
|
|
14
|
-
return { width: columnSize };
|
|
17
|
+
return { width: columnSize, minWidth: columnSize, flexShrink: 0 };
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
return { flex: 1 };
|
|
19
|
-
}
|
|
20
|
+
return { flex: 1, minWidth: 0 };
|
|
20
21
|
};
|
|
@@ -63,7 +63,9 @@
|
|
|
63
63
|
background: var(--background);
|
|
64
64
|
border-color: var(--border-contrast);
|
|
65
65
|
|
|
66
|
-
.label
|
|
66
|
+
.label,
|
|
67
|
+
.key,
|
|
68
|
+
.value {
|
|
67
69
|
color: var(--font-disabled);
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -105,6 +107,25 @@
|
|
|
105
107
|
@include typography.ellipsis;
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
.keyValue {
|
|
111
|
+
min-width: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.key {
|
|
115
|
+
flex-shrink: 0;
|
|
116
|
+
color: var(--font-main);
|
|
117
|
+
|
|
118
|
+
&::after {
|
|
119
|
+
content: ':';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.value {
|
|
124
|
+
@include typography.ellipsis;
|
|
125
|
+
|
|
126
|
+
color: var(--font-secondary);
|
|
127
|
+
}
|
|
128
|
+
|
|
108
129
|
.icon {
|
|
109
130
|
flex-shrink: 0;
|
|
110
131
|
display: flex;
|
|
@@ -3,6 +3,7 @@ import classNames from 'classnames';
|
|
|
3
3
|
import styles from './ds-tag.module.scss';
|
|
4
4
|
import type { DsTagProps } from './ds-tag.types';
|
|
5
5
|
import { DsIcon } from '../ds-icon';
|
|
6
|
+
import { DsStack } from '../ds-stack';
|
|
6
7
|
import { DsTypography } from '../ds-typography';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -11,6 +12,7 @@ import { DsTypography } from '../ds-typography';
|
|
|
11
12
|
const DsTag = ({
|
|
12
13
|
ref,
|
|
13
14
|
label,
|
|
15
|
+
value,
|
|
14
16
|
className,
|
|
15
17
|
style = {},
|
|
16
18
|
size = 'medium',
|
|
@@ -43,7 +45,7 @@ const DsTag = ({
|
|
|
43
45
|
const handleKeyDownTag = (event: KeyboardEvent<HTMLElement>) => {
|
|
44
46
|
if (onClick && (event.key === 'Enter' || event.key === ' ')) {
|
|
45
47
|
event.preventDefault();
|
|
46
|
-
onClick(event
|
|
48
|
+
onClick(event);
|
|
47
49
|
|
|
48
50
|
return;
|
|
49
51
|
}
|
|
@@ -66,7 +68,7 @@ const DsTag = ({
|
|
|
66
68
|
const handleClick = (event: MouseEvent<HTMLElement>) => {
|
|
67
69
|
if (onClick) {
|
|
68
70
|
event.preventDefault();
|
|
69
|
-
onClick(event
|
|
71
|
+
onClick(event);
|
|
70
72
|
}
|
|
71
73
|
};
|
|
72
74
|
|
|
@@ -102,9 +104,20 @@ const DsTag = ({
|
|
|
102
104
|
))}
|
|
103
105
|
</span>
|
|
104
106
|
)}
|
|
105
|
-
|
|
106
|
-
{
|
|
107
|
-
|
|
107
|
+
{variant === 'key-value' ? (
|
|
108
|
+
<DsStack direction="row" alignItems="center" gap="var(--3xs)" className={styles.keyValue}>
|
|
109
|
+
<DsTypography variant={size === 'small' ? 'body-xs-md' : 'body-sm-md'} className={styles.key}>
|
|
110
|
+
{label}
|
|
111
|
+
</DsTypography>
|
|
112
|
+
<DsTypography variant={size === 'small' ? 'body-xs-reg' : 'body-sm-reg'} className={styles.value}>
|
|
113
|
+
{value}
|
|
114
|
+
</DsTypography>
|
|
115
|
+
</DsStack>
|
|
116
|
+
) : (
|
|
117
|
+
<DsTypography variant={size === 'small' ? 'body-xs-reg' : 'body-sm-reg'} className={styles.label}>
|
|
118
|
+
{label}
|
|
119
|
+
</DsTypography>
|
|
120
|
+
)}
|
|
108
121
|
{onDelete && !disabled && (
|
|
109
122
|
<button
|
|
110
123
|
type="button"
|
|
@@ -3,16 +3,16 @@ import type { CSSProperties, KeyboardEvent, MouseEvent, ReactNode, Ref } from 'r
|
|
|
3
3
|
export const tagSizes = ['medium', 'small'] as const;
|
|
4
4
|
export type TagSize = (typeof tagSizes)[number];
|
|
5
5
|
|
|
6
|
-
export const tagVariants = ['default', 'include', 'exclude'] as const;
|
|
6
|
+
export const tagVariants = ['default', 'include', 'exclude', 'key-value'] as const;
|
|
7
7
|
export type TagVariant = (typeof tagVariants)[number];
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface DsTagBaseProps {
|
|
10
10
|
/**
|
|
11
11
|
* Ref to the tag element
|
|
12
12
|
*/
|
|
13
13
|
ref?: Ref<HTMLElement>;
|
|
14
14
|
/**
|
|
15
|
-
* The label text to display in the tag
|
|
15
|
+
* The label text to display in the tag. For the `key-value` variant this is the key.
|
|
16
16
|
*/
|
|
17
17
|
label: ReactNode;
|
|
18
18
|
/**
|
|
@@ -50,11 +50,6 @@ export interface DsTagProps {
|
|
|
50
50
|
* @default false
|
|
51
51
|
*/
|
|
52
52
|
selected?: boolean;
|
|
53
|
-
/**
|
|
54
|
-
* Variant of the tag
|
|
55
|
-
* @default 'default'
|
|
56
|
-
*/
|
|
57
|
-
variant?: TagVariant;
|
|
58
53
|
/**
|
|
59
54
|
* Whether the tag is disabled
|
|
60
55
|
* @default false
|
|
@@ -71,3 +66,22 @@ export interface DsTagProps {
|
|
|
71
66
|
icon?: ReactNode;
|
|
72
67
|
};
|
|
73
68
|
}
|
|
69
|
+
|
|
70
|
+
interface DsTagStandardProps extends DsTagBaseProps {
|
|
71
|
+
/**
|
|
72
|
+
* Variant of the tag
|
|
73
|
+
* @default 'default'
|
|
74
|
+
*/
|
|
75
|
+
variant?: 'default' | 'include' | 'exclude';
|
|
76
|
+
value?: never;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface DsTagKeyValueProps extends DsTagBaseProps {
|
|
80
|
+
variant: 'key-value';
|
|
81
|
+
/**
|
|
82
|
+
* Secondary-colored value rendered after the key (`label`). Required for the `key-value` variant.
|
|
83
|
+
*/
|
|
84
|
+
value: ReactNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type DsTagProps = DsTagStandardProps | DsTagKeyValueProps;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* eslint-disable vitest/expect-expect */
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
4
|
+
import type { DsTagProps, TagVariant } from './ds-tag.types';
|
|
5
|
+
|
|
6
|
+
describe('DsTagProps discriminated union', () => {
|
|
7
|
+
it('includes key-value in the variant union', () => {
|
|
8
|
+
expectTypeOf<TagVariant>().toEqualTypeOf<'default' | 'include' | 'exclude' | 'key-value'>();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('allows standard variants without a value', () => {
|
|
12
|
+
expectTypeOf<{ label: string }>().toExtend<DsTagProps>();
|
|
13
|
+
expectTypeOf<{ label: string; variant: 'default' }>().toExtend<DsTagProps>();
|
|
14
|
+
expectTypeOf<{ label: string; variant: 'include' }>().toExtend<DsTagProps>();
|
|
15
|
+
expectTypeOf<{ label: string; variant: 'exclude' }>().toExtend<DsTagProps>();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('requires a value for the key-value variant', () => {
|
|
19
|
+
expectTypeOf<{ label: string; variant: 'key-value'; value: string }>().toExtend<DsTagProps>();
|
|
20
|
+
expectTypeOf<{ label: string; variant: 'key-value' }>().not.toExtend<DsTagProps>();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('forbids a value on standard variants', () => {
|
|
24
|
+
expectTypeOf<{ label: string; value: string }>().not.toExtend<DsTagProps>();
|
|
25
|
+
expectTypeOf<{ label: string; variant: 'default'; value: string }>().not.toExtend<DsTagProps>();
|
|
26
|
+
expectTypeOf<{ label: string; variant: 'include'; value: string }>().not.toExtend<DsTagProps>();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('types the key-value value as ReactNode', () => {
|
|
30
|
+
expectTypeOf<Extract<DsTagProps, { variant: 'key-value' }>['value']>().toEqualTypeOf<ReactNode>();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -5,7 +5,7 @@ import styles from './ds-tag-filter.module.scss';
|
|
|
5
5
|
import type { DsTagFilterProps, TagFilterItem } from './ds-tag-filter.types';
|
|
6
6
|
import { useTagOverflowCalculation } from './hooks/use-tag-overflow-calculation';
|
|
7
7
|
import { DsTypography } from '../ds-typography';
|
|
8
|
-
import { DsTag } from '../ds-tag';
|
|
8
|
+
import { DsTag, type DsTagProps } from '../ds-tag';
|
|
9
9
|
import { DsButton } from '../ds-button';
|
|
10
10
|
import { DsIcon } from '../ds-icon';
|
|
11
11
|
|
|
@@ -51,20 +51,19 @@ const DsTagFilter = ({
|
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Merging per-item overrides with the filter-controlled props drops the
|
|
55
|
+
// discriminated-union narrowing of `DsTagProps` (a known TS spread limitation),
|
|
56
|
+
// so build the props once and assert the union type.
|
|
57
|
+
const buildTagProps = (item: TagFilterItem): DsTagProps =>
|
|
58
|
+
({
|
|
59
|
+
...item.slotProps?.tag,
|
|
60
|
+
label: item.label,
|
|
61
|
+
selected: item.selected,
|
|
62
|
+
onClick: onItemSelect ? () => onItemSelect(item) : undefined,
|
|
63
|
+
onDelete: onItemDelete ? () => onItemDelete(item) : undefined,
|
|
64
|
+
}) as DsTagProps;
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
<DsTag
|
|
59
|
-
{...tagProps}
|
|
60
|
-
key={item.id}
|
|
61
|
-
label={item.label}
|
|
62
|
-
selected={item.selected}
|
|
63
|
-
onClick={onItemSelect ? () => onItemSelect(item) : undefined}
|
|
64
|
-
onDelete={onItemDelete ? () => onItemDelete(item) : undefined}
|
|
65
|
-
/>
|
|
66
|
-
);
|
|
67
|
-
};
|
|
66
|
+
const renderTag = (item: TagFilterItem) => <DsTag key={item.id} {...buildTagProps(item)} />;
|
|
68
67
|
|
|
69
68
|
const handleExpandToggle = () => {
|
|
70
69
|
const newExpanded = !expanded;
|
|
@@ -75,14 +74,7 @@ const DsTagFilter = ({
|
|
|
75
74
|
const measurementContainer = (
|
|
76
75
|
<div ref={measurementRef} className={styles.measurementContainer} aria-hidden="true">
|
|
77
76
|
{items.map((item) => (
|
|
78
|
-
<DsTag
|
|
79
|
-
key={item.id}
|
|
80
|
-
data-measure-tag=""
|
|
81
|
-
label={item.label}
|
|
82
|
-
selected={item.selected}
|
|
83
|
-
onClick={onItemSelect ? () => onItemSelect(item) : undefined}
|
|
84
|
-
onDelete={onItemDelete ? () => onItemDelete(item) : undefined}
|
|
85
|
-
/>
|
|
77
|
+
<DsTag key={item.id} data-measure-tag="" {...buildTagProps(item)} />
|
|
86
78
|
))}
|
|
87
79
|
</div>
|
|
88
80
|
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=13346-18325
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-text-input
|
|
3
|
+
// component=DsTextInput
|
|
4
|
+
//
|
|
5
|
+
// The Figma component `DAP_input_v01` is a single multi-type input. Its `Type`
|
|
6
|
+
// variant selects which code component to render:
|
|
7
|
+
// Number -> DsNumberInput
|
|
8
|
+
// Password -> DsPasswordInput
|
|
9
|
+
// Standard/Error/Read -> DsTextInput
|
|
10
|
+
import figma from 'figma';
|
|
11
|
+
|
|
12
|
+
const instance = figma.selectedInstance;
|
|
13
|
+
|
|
14
|
+
const type = instance.getEnum('Type', {
|
|
15
|
+
Standard: 'standard',
|
|
16
|
+
Error: 'error',
|
|
17
|
+
'Read Only': 'readOnly',
|
|
18
|
+
Number: 'number',
|
|
19
|
+
Password: 'password',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const size = instance.getEnum('Size', {
|
|
23
|
+
Small: 'small',
|
|
24
|
+
Default: 'default',
|
|
25
|
+
Large: 'default',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const state = instance.getEnum('State', {
|
|
29
|
+
Default: 'default',
|
|
30
|
+
Hover: 'hover',
|
|
31
|
+
'Focus/ Typing': 'focus',
|
|
32
|
+
Disabled: 'disabled',
|
|
33
|
+
'Read only': 'readOnly',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const disabled = state === 'disabled';
|
|
37
|
+
const readOnly = type === 'readOnly' || state === 'readOnly';
|
|
38
|
+
|
|
39
|
+
const sizeAttr = size === 'small' ? 'size="small"' : '';
|
|
40
|
+
|
|
41
|
+
const componentName =
|
|
42
|
+
type === 'number' ? 'DsNumberInput' : type === 'password' ? 'DsPasswordInput' : 'DsTextInput';
|
|
43
|
+
|
|
44
|
+
// `DsFormControl` member name and attribute string, surfaced so a parent `DsFormControl`
|
|
45
|
+
// template can emit `<DsFormControl.${fcMember} ${fcProps} />`.
|
|
46
|
+
const fcMember = type === 'number' ? 'NumberInput' : type === 'password' ? 'PasswordInput' : 'TextInput';
|
|
47
|
+
|
|
48
|
+
const fcProps = [sizeAttr, disabled ? 'disabled' : '', readOnly ? 'readOnly' : ''].filter(Boolean).join(' ');
|
|
49
|
+
|
|
50
|
+
export default {
|
|
51
|
+
example:
|
|
52
|
+
type === 'number'
|
|
53
|
+
? figma.code`<DsNumberInput ${sizeAttr} ${disabled ? 'disabled' : ''} />`
|
|
54
|
+
: type === 'password'
|
|
55
|
+
? figma.code`<DsPasswordInput ${sizeAttr} ${disabled ? 'disabled' : ''} />`
|
|
56
|
+
: figma.code`<DsTextInput ${sizeAttr} ${disabled ? 'disabled' : ''} ${readOnly ? 'readOnly' : ''} />`,
|
|
57
|
+
imports: [`import { ${componentName} } from '@drivenets/design-system'`],
|
|
58
|
+
id: 'ds-text-input',
|
|
59
|
+
metadata: { nestable: true, props: { fcMember, fcProps } },
|
|
60
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=30102-172478
|
|
2
|
+
// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-time-picker
|
|
3
|
+
// component=DsTimePicker
|
|
4
|
+
//
|
|
5
|
+
// `DAP_hh_mm_drop_down_v1.0` exposes no component properties. The snippet is therefore an
|
|
6
|
+
// illustrative controlled-usage starter (props are not derived from Figma) showing the
|
|
7
|
+
// idiomatic `useState` + `value`/`onChange` pattern. `fcMember`/`fcProps` are surfaced so
|
|
8
|
+
// a parent `DsFormControl` template can render `<DsFormControl.TimePicker />`.
|
|
9
|
+
import figma from 'figma';
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
example: figma.code`const [value, setValue] = useState<Date | null>(null);
|
|
13
|
+
|
|
14
|
+
<DsTimePicker value={value} onChange={setValue} placeholder="hh:mm AM/PM" />`,
|
|
15
|
+
imports: ["import { useState } from 'react'", "import { DsTimePicker } from '@drivenets/design-system'"],
|
|
16
|
+
id: 'ds-time-picker',
|
|
17
|
+
metadata: { nestable: true, props: { fcMember: 'TimePicker', fcProps: '' } },
|
|
18
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface DsWorkspaceLayoutContextValue {
|
|
4
|
+
hasLeftSidePanel: boolean;
|
|
5
|
+
registerLeftSidePanel: () => void;
|
|
6
|
+
unregisterLeftSidePanel: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const DsWorkspaceLayoutContext = createContext<DsWorkspaceLayoutContextValue | null>(null);
|
|
10
|
+
|
|
11
|
+
export const useDsWorkspaceLayoutContext = () => {
|
|
12
|
+
const context = useContext(DsWorkspaceLayoutContext);
|
|
13
|
+
|
|
14
|
+
if (!context) {
|
|
15
|
+
throw new Error('DsWorkspaceLayout compound components must be used within DsWorkspaceLayout');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return context;
|
|
19
|
+
};
|