@ceed/cds 1.24.1-next.3 → 1.26.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/dist/chunks/rehype-accent-FZRUD7VI.js +39 -0
- package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
- package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
- package/dist/components/DataTable/components.d.ts +2 -1
- package/dist/components/DataTable/hooks.d.ts +1 -1
- package/dist/components/DataTable/styled.d.ts +3 -1
- package/dist/components/DataTable/types.d.ts +11 -0
- package/dist/components/DataTable/utils.d.ts +2 -2
- package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
- package/dist/components/RadioTileGroup/index.d.ts +3 -0
- package/dist/components/data-display/DataTable.md +177 -1
- package/dist/components/data-display/InfoSign.md +74 -91
- package/dist/components/data-display/Typography.md +411 -94
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Dialog.md +76 -62
- package/dist/components/feedback/Modal.md +430 -138
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/inputs/Autocomplete.md +356 -107
- package/dist/components/inputs/ButtonGroup.md +115 -104
- package/dist/components/inputs/CurrencyInput.md +183 -5
- package/dist/components/inputs/DatePicker.md +108 -431
- package/dist/components/inputs/DateRangePicker.md +131 -492
- package/dist/components/inputs/FilterableCheckboxGroup.md +145 -19
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/Input.md +204 -73
- package/dist/components/inputs/MonthPicker.md +95 -422
- package/dist/components/inputs/MonthRangePicker.md +89 -466
- package/dist/components/inputs/PercentageInput.md +185 -16
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +507 -0
- package/dist/components/inputs/Select.md +222 -326
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +143 -376
- package/dist/components/inputs/Textarea.md +213 -10
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +4 -0
- package/dist/components/navigation/Breadcrumbs.md +57 -308
- package/dist/components/navigation/Drawer.md +180 -0
- package/dist/components/navigation/Dropdown.md +98 -215
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +281 -650
- package/dist/components/navigation/Link.md +31 -348
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Accordions.md +49 -804
- package/dist/components/surfaces/Card.md +97 -157
- package/dist/components/surfaces/Divider.md +83 -234
- package/dist/components/surfaces/Sheet.md +153 -328
- package/dist/guides/ThemeProvider.md +89 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/index.browser.js +224 -0
- package/dist/index.browser.js.map +7 -0
- package/dist/index.cjs +726 -425
- package/dist/index.d.ts +1 -1
- package/dist/index.js +641 -396
- package/dist/llms.txt +9 -0
- package/framer/index.js +1 -163
- package/package.json +22 -17
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/libs/rehype-accent/index.ts
|
|
2
|
+
import { visit } from "unist-util-visit";
|
|
3
|
+
function rehypeAccent(options) {
|
|
4
|
+
const { accentColor } = options;
|
|
5
|
+
return (tree) => {
|
|
6
|
+
visit(tree, "text", (node, index, parent) => {
|
|
7
|
+
const value = node.value;
|
|
8
|
+
const regex = /\|\|.*?\|\|/g;
|
|
9
|
+
let match;
|
|
10
|
+
let lastIndex = 0;
|
|
11
|
+
const newNodes = [];
|
|
12
|
+
while ((match = regex.exec(value)) !== null) {
|
|
13
|
+
if (match.index > lastIndex) {
|
|
14
|
+
newNodes.push({
|
|
15
|
+
type: "text",
|
|
16
|
+
value: value.slice(lastIndex, match.index)
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const innerText = match[0].split("||")[1];
|
|
20
|
+
newNodes.push({
|
|
21
|
+
type: "element",
|
|
22
|
+
tagName: "span",
|
|
23
|
+
properties: { textColor: accentColor },
|
|
24
|
+
children: [{ type: "text", value: innerText }]
|
|
25
|
+
});
|
|
26
|
+
lastIndex = match.index + match[0].length;
|
|
27
|
+
}
|
|
28
|
+
if (lastIndex < value.length) {
|
|
29
|
+
newNodes.push({ type: "text", value: value.slice(lastIndex) });
|
|
30
|
+
}
|
|
31
|
+
if (newNodes.length) {
|
|
32
|
+
parent.children.splice(index, 1, ...newNodes);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
rehypeAccent
|
|
39
|
+
};
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { InputProps } from '@mui/joy';
|
|
3
3
|
import { MotionProps } from 'framer-motion';
|
|
4
4
|
interface CurrencyInputProps {
|
|
5
|
-
currency?: 'USD' | 'KRW';
|
|
5
|
+
currency?: 'USD' | 'KRW' | 'CAD';
|
|
6
6
|
max?: number;
|
|
7
7
|
value?: number;
|
|
8
8
|
defaultValue?: number;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export declare const useCurrencySetting: (props: {
|
|
2
|
-
currency?: "USD" | "KRW" | "BHD" | undefined;
|
|
2
|
+
currency?: "USD" | "KRW" | "BHD" | "CAD" | undefined;
|
|
3
3
|
placeholder?: string | undefined;
|
|
4
4
|
}) => {
|
|
5
5
|
symbol: string;
|
|
6
6
|
thousandSeparator: string;
|
|
7
|
-
decimalSeparator: string;
|
|
7
|
+
decimalSeparator: string | undefined;
|
|
8
8
|
placeholder: string;
|
|
9
9
|
fixedDecimalScale: boolean;
|
|
10
10
|
decimalScale: number;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
import type { ObjectLike, ColumnDef, Sort } from './types';
|
|
3
|
-
export declare const TextEllipsis: ({ children }: {
|
|
3
|
+
export declare const TextEllipsis: ({ children, lineClamp }: {
|
|
4
4
|
children: ReactNode;
|
|
5
|
+
lineClamp?: number | undefined;
|
|
5
6
|
}) => React.JSX.Element;
|
|
6
7
|
export declare const CellTextEllipsis: ({ children }: {
|
|
7
8
|
children: ReactNode;
|
|
@@ -5,7 +5,7 @@ export declare function useColumnWidths<T>(columnsByField: {
|
|
|
5
5
|
headerRef: React.RefObject<HTMLTableCellElement>;
|
|
6
6
|
};
|
|
7
7
|
}): Record<string, number>;
|
|
8
|
-
export declare function useDataTableRenderer<T extends Record<PropertyKey, unknown>, GetId extends ((row: T) => any) | undefined = undefined>({ rows: _rows, columns: columnsProp, pinnedColumns, rowCount: totalRowsProp, initialState, pagination, paginationMode, paginationModel, onPaginationModelChange, sortModel: controlledSortModel, sortOrder: _sortOrder, selectionModel, onSortModelChange, onSelectionModelChange, editMode, getId: _getId, isTotalSelected: _isTotalSelected, isRowSelectable, columnGroupingModel, }: DataTableProps<T, GetId>): {
|
|
8
|
+
export declare function useDataTableRenderer<T extends Record<PropertyKey, unknown>, GetId extends ((row: T) => any) | undefined = undefined>({ rows: _rows, columns: columnsProp, pinnedColumns, rowCount: totalRowsProp, initialState, pagination, paginationMode, paginationModel, onPaginationModelChange, sortModel: controlledSortModel, sortOrder: _sortOrder, selectionModel, onSortModelChange, onSelectionModelChange, editMode, getId: _getId, isTotalSelected: _isTotalSelected, isRowSelectable, columnGroupingModel, columnVisibilityModel, onColumnVisibilityModelChange, }: DataTableProps<T, GetId>): {
|
|
9
9
|
rowCount: number;
|
|
10
10
|
selectableRowCount: number;
|
|
11
11
|
page: number;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React, { RefObject } from 'react';
|
|
2
|
-
export declare const EllipsisDiv: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme
|
|
2
|
+
export declare const EllipsisDiv: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme> & {
|
|
3
|
+
lineClamp?: number | undefined;
|
|
4
|
+
}, React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
3
5
|
export declare const OverlayWrapper: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement>, keyof React.ClassAttributes<HTMLTableRowElement> | keyof React.HTMLAttributes<HTMLTableRowElement>>, {}>;
|
|
4
6
|
export declare const VirtualizedTableBody: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableSectionElement>, HTMLTableSectionElement>, keyof React.ClassAttributes<HTMLTableSectionElement> | keyof React.HTMLAttributes<HTMLTableSectionElement>>, {}>;
|
|
5
7
|
export declare const StyledTableRow: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme> & {
|
|
@@ -103,6 +103,7 @@ export type BaseColumnDef<T extends Record<PropertyKey, V>, V, ID> = {
|
|
|
103
103
|
description?: string;
|
|
104
104
|
cellClassName?: TableCellClassNamePropType<T, T[K]>;
|
|
105
105
|
headerClassName?: TableColumnHeaderClassNamePropType<T, T[K]>;
|
|
106
|
+
headerLineClamp?: 1 | 2;
|
|
106
107
|
};
|
|
107
108
|
}[keyof T];
|
|
108
109
|
export type AutocompleteColumnDef<T extends Record<PropertyKey, string>, ID> = BaseColumnDef<T, string, ID> & {
|
|
@@ -147,6 +148,7 @@ export type ActionsColumnDef<T extends Record<PropertyKey, string>, ID> = BaseCo
|
|
|
147
148
|
}) => ReactNode[];
|
|
148
149
|
};
|
|
149
150
|
export type ColumnDef<T extends Record<PropertyKey, any>, ID = unknown> = AutocompleteColumnDef<T, ID> | CurrencyColumnDef<T, ID> | DateColumnDef<T, ID> | NumberColumnDef<T, ID> | TextColumnDef<T, ID> | LongTextColumnDef<T, ID> | LinkColumnDef<T, ID> | SelectColumnDef<T, ID> | ActionsColumnDef<T, ID>;
|
|
151
|
+
export type ColumnVisibilityModel = Record<string, boolean>;
|
|
150
152
|
export type Sort = 'asc' | 'desc' | null;
|
|
151
153
|
export type SortModel<T extends Record<PropertyKey, any>> = {
|
|
152
154
|
[K in keyof T]: {
|
|
@@ -164,6 +166,12 @@ export type DataTableProps<T extends Record<PropertyKey, any>, GetId extends ((r
|
|
|
164
166
|
columns: ColumnDef<T, InferredIdType<T, GetId>>[];
|
|
165
167
|
pinnedColumns?: PinnedColumns;
|
|
166
168
|
columnGroupingModel?: ColumnGroupingModel<T, InferredIdType<T, GetId>>;
|
|
169
|
+
/**
|
|
170
|
+
* 컬럼의 표시/숨김 상태를 제어한다. field를 key로, boolean을 value로 사용한다.
|
|
171
|
+
* false로 설정된 컬럼만 숨겨지며, 모델에 포함되지 않은 컬럼은 기본적으로 표시된다.
|
|
172
|
+
*/
|
|
173
|
+
columnVisibilityModel?: ColumnVisibilityModel;
|
|
174
|
+
onColumnVisibilityModelChange?: (model: ColumnVisibilityModel) => void;
|
|
167
175
|
editMode?: boolean;
|
|
168
176
|
/**
|
|
169
177
|
* 체크박스가 있는 경우, 체크박스를 클릭했을 때 선택된 row의 index를 지정한다.
|
|
@@ -187,6 +195,9 @@ export type DataTableProps<T extends Record<PropertyKey, any>, GetId extends ((r
|
|
|
187
195
|
sorting: Partial<{
|
|
188
196
|
sortModel: SortModel<T>[];
|
|
189
197
|
}>;
|
|
198
|
+
columns: Partial<{
|
|
199
|
+
columnVisibilityModel: ColumnVisibilityModel;
|
|
200
|
+
}>;
|
|
190
201
|
}>;
|
|
191
202
|
pagination?: boolean;
|
|
192
203
|
paginationMode?: 'client' | 'server';
|
|
@@ -3,8 +3,8 @@ export declare function extractFieldsFromGroupingModel<T extends Record<Property
|
|
|
3
3
|
export declare function reorderColumnsByGroupingModel<T extends Record<PropertyKey, any>, ID>(columns: ColumnDef<T, ID>[], columnGroupingModel: ColumnGroupingModel<T, ID>): ColumnDef<T, ID>[];
|
|
4
4
|
export declare function flattenColumnGroups<T extends Record<PropertyKey, any>, ID>(items: ColumnGroupingModel<T, ID> | ColumnDef<T, ID>[], groupPath?: string[], columnIndex?: {
|
|
5
5
|
current: number;
|
|
6
|
-
}): FlattenedColumn<T, ID>[];
|
|
7
|
-
export declare function calculateColumnGroups<T extends Record<PropertyKey, any>, ID>(columnGroupingModel: ColumnGroupingModel<T, ID>, columns: ColumnDef<T, ID>[]): {
|
|
6
|
+
}, visibleFields?: Set<string>): FlattenedColumn<T, ID>[];
|
|
7
|
+
export declare function calculateColumnGroups<T extends Record<PropertyKey, any>, ID>(columnGroupingModel: ColumnGroupingModel<T, ID>, columns: ColumnDef<T, ID>[], visibleFields?: Set<string>): {
|
|
8
8
|
groups: ProcessedColumnGroup[][];
|
|
9
9
|
maxLevel: number;
|
|
10
10
|
fieldsInGroupingModel: Set<keyof T>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import type { SxProps } from '@mui/joy/styles/types';
|
|
3
|
+
export interface RadioTileOption<T = string> {
|
|
4
|
+
value: T;
|
|
5
|
+
label: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
startDecorator?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export interface RadioTileGroupProps<T = string> {
|
|
10
|
+
/**
|
|
11
|
+
* @default 'sm'
|
|
12
|
+
*/
|
|
13
|
+
size?: 'sm' | 'md' | 'lg';
|
|
14
|
+
options: RadioTileOption<T>[];
|
|
15
|
+
value?: T;
|
|
16
|
+
defaultValue?: T;
|
|
17
|
+
name?: string;
|
|
18
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
19
|
+
sx?: SxProps;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
className?: string;
|
|
22
|
+
useIndicator?: boolean;
|
|
23
|
+
flex?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* 지정하지 않으면 한 row에 모든 옵션이 렌더링된다.
|
|
26
|
+
* - 특정 rows 이내로 렌더링 하고 싶으면 `columns: Math.ceil(options.length / 원하는 rows)`로 설정할 수 있다.
|
|
27
|
+
* - 수직으로 렌더링 하고 싶으면 `columns: 1`로 설정할 수 있다.
|
|
28
|
+
* - `flex` 옵션과 함께 사용할수 있다.
|
|
29
|
+
*/
|
|
30
|
+
columns?: number;
|
|
31
|
+
/**
|
|
32
|
+
* @default 'center'
|
|
33
|
+
*/
|
|
34
|
+
textAlign?: 'start' | 'center';
|
|
35
|
+
/**
|
|
36
|
+
* Label for the RadioTileGroup
|
|
37
|
+
*/
|
|
38
|
+
label?: React.ReactNode;
|
|
39
|
+
/**
|
|
40
|
+
* Helper text for the RadioTileGroup
|
|
41
|
+
*/
|
|
42
|
+
helperText?: React.ReactNode;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the RadioTileGroup has an error
|
|
45
|
+
*/
|
|
46
|
+
error?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the RadioTileGroup is required
|
|
49
|
+
*/
|
|
50
|
+
required?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare function RadioTileGroup<T extends string | number = string>(props: RadioTileGroupProps<T>): React.JSX.Element;
|
|
53
|
+
declare namespace RadioTileGroup {
|
|
54
|
+
var displayName: string;
|
|
55
|
+
}
|
|
56
|
+
export { RadioTileGroup };
|
|
@@ -865,6 +865,106 @@ const columnGroupingModel = [
|
|
|
865
865
|
<DataTable rows={rows} columns={columns} columnGroupingModel={columnGroupingModel} />;
|
|
866
866
|
```
|
|
867
867
|
|
|
868
|
+
### Column Visibility
|
|
869
|
+
|
|
870
|
+
You can control which columns are visible using `columnVisibilityModel`.
|
|
871
|
+
Columns set to `false` are hidden. Columns not in the model are visible by default.
|
|
872
|
+
Hidden columns are completely removed from the DOM for optimal rendering performance.
|
|
873
|
+
|
|
874
|
+
```tsx
|
|
875
|
+
<Stack spacing={2}>
|
|
876
|
+
<Stack direction="row" spacing={1} sx={{
|
|
877
|
+
flexWrap: 'wrap'
|
|
878
|
+
}}>
|
|
879
|
+
{allColumns.map(col => <label key={col.field as string} style={{
|
|
880
|
+
display: 'flex',
|
|
881
|
+
alignItems: 'center',
|
|
882
|
+
gap: 4
|
|
883
|
+
}}>
|
|
884
|
+
<input type="checkbox" checked={visibilityModel[col.field as string] !== false} onChange={e => setVisibilityModel(prev => ({
|
|
885
|
+
...prev,
|
|
886
|
+
[col.field as string]: e.target.checked
|
|
887
|
+
}))} />
|
|
888
|
+
{col.headerName}
|
|
889
|
+
</label>)}
|
|
890
|
+
</Stack>
|
|
891
|
+
<DataTable rows={rows2} columns={allColumns} columnVisibilityModel={visibilityModel} onColumnVisibilityModelChange={setVisibilityModel} noWrap />
|
|
892
|
+
</Stack>
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
#### Controlled Mode
|
|
896
|
+
|
|
897
|
+
```tsx
|
|
898
|
+
const [visibilityModel, setVisibilityModel] = useState({ fat: false });
|
|
899
|
+
|
|
900
|
+
<DataTable
|
|
901
|
+
rows={rows}
|
|
902
|
+
columns={columns}
|
|
903
|
+
columnVisibilityModel={visibilityModel}
|
|
904
|
+
onColumnVisibilityModelChange={setVisibilityModel}
|
|
905
|
+
/>
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
#### Uncontrolled Mode (initialState)
|
|
909
|
+
|
|
910
|
+
```tsx
|
|
911
|
+
<DataTable
|
|
912
|
+
rows={rows}
|
|
913
|
+
columns={columns}
|
|
914
|
+
initialState={{
|
|
915
|
+
columns: { columnVisibilityModel: { fat: false, carbs: false } },
|
|
916
|
+
}}
|
|
917
|
+
/>
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
#### Column Visibility with Column Groups
|
|
921
|
+
|
|
922
|
+
```tsx
|
|
923
|
+
<Stack spacing={2}>
|
|
924
|
+
<Stack direction="row" spacing={1} sx={{
|
|
925
|
+
flexWrap: 'wrap'
|
|
926
|
+
}}>
|
|
927
|
+
{columns.map(col => <label key={col.field as string} style={{
|
|
928
|
+
display: 'flex',
|
|
929
|
+
alignItems: 'center',
|
|
930
|
+
gap: 4
|
|
931
|
+
}}>
|
|
932
|
+
<input type="checkbox" checked={visibilityModel[col.field as string] !== false} onChange={e => setVisibilityModel(prev => ({
|
|
933
|
+
...prev,
|
|
934
|
+
[col.field as string]: e.target.checked
|
|
935
|
+
}))} />
|
|
936
|
+
{col.headerName}
|
|
937
|
+
</label>)}
|
|
938
|
+
</Stack>
|
|
939
|
+
<DataTable rows={rows4} columns={columns} columnGroupingModel={[{
|
|
940
|
+
groupId: 'group1',
|
|
941
|
+
headerName: 'Group 1',
|
|
942
|
+
children: [{
|
|
943
|
+
field: 'id'
|
|
944
|
+
}, {
|
|
945
|
+
field: 'number'
|
|
946
|
+
}, {
|
|
947
|
+
field: 'string'
|
|
948
|
+
}]
|
|
949
|
+
}, {
|
|
950
|
+
groupId: 'group2',
|
|
951
|
+
headerName: 'Group 2',
|
|
952
|
+
children: [{
|
|
953
|
+
field: 'date'
|
|
954
|
+
}, {
|
|
955
|
+
field: 'object'
|
|
956
|
+
}]
|
|
957
|
+
}]} columnVisibilityModel={visibilityModel} onColumnVisibilityModelChange={setVisibilityModel} noWrap />
|
|
958
|
+
</Stack>
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
#### Notes
|
|
962
|
+
|
|
963
|
+
- Hidden columns are completely removed from the DOM for optimal performance.
|
|
964
|
+
- Sorting on hidden columns is preserved — row order is maintained even if the sort column is hidden.
|
|
965
|
+
- Works with column grouping: hidden columns reduce group colspan, and fully hidden groups disappear.
|
|
966
|
+
- Works with pinned columns: hidden pinned columns are excluded from sticky position calculations.
|
|
967
|
+
|
|
868
968
|
### Column Resizing
|
|
869
969
|
|
|
870
970
|
Setting `resizable: true` on a column allows you to adjust the column width by dragging.
|
|
@@ -895,6 +995,82 @@ const columns = [
|
|
|
895
995
|
];
|
|
896
996
|
```
|
|
897
997
|
|
|
998
|
+
### Two-Line Headers (headerLineClamp)
|
|
999
|
+
|
|
1000
|
+
Setting `headerLineClamp: 2` on a column allows the header text to wrap up to 2 lines. Text exceeding 2 lines is truncated with an ellipsis, and a tooltip is displayed on hover when truncation occurs.
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
<Box sx={{
|
|
1004
|
+
width: 500
|
|
1005
|
+
}}>
|
|
1006
|
+
<DataTable rows={manyRows} columns={[{
|
|
1007
|
+
field: 'dessert',
|
|
1008
|
+
headerName: 'Dessert Name (100g serving)',
|
|
1009
|
+
headerLineClamp: 2,
|
|
1010
|
+
width: '120px'
|
|
1011
|
+
}, {
|
|
1012
|
+
field: 'calories',
|
|
1013
|
+
headerName: 'Calories (kcal)',
|
|
1014
|
+
headerLineClamp: 2,
|
|
1015
|
+
type: 'number',
|
|
1016
|
+
width: '80px'
|
|
1017
|
+
}, {
|
|
1018
|
+
field: 'fat',
|
|
1019
|
+
headerName: 'Fat Content (grams)',
|
|
1020
|
+
headerLineClamp: 2,
|
|
1021
|
+
type: 'number',
|
|
1022
|
+
width: '80px'
|
|
1023
|
+
}, {
|
|
1024
|
+
field: 'carbs',
|
|
1025
|
+
headerName: 'Total Carbohydrates (g)',
|
|
1026
|
+
headerLineClamp: 2,
|
|
1027
|
+
type: 'number',
|
|
1028
|
+
width: '90px'
|
|
1029
|
+
}, {
|
|
1030
|
+
field: 'protein',
|
|
1031
|
+
headerName: 'Protein Amount (g)',
|
|
1032
|
+
type: 'number',
|
|
1033
|
+
width: '120px'
|
|
1034
|
+
}]} stickyHeader slotProps={{
|
|
1035
|
+
background: {
|
|
1036
|
+
style: {
|
|
1037
|
+
height: '400px'
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}} />
|
|
1041
|
+
</Box>
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
```tsx
|
|
1045
|
+
const columns = [
|
|
1046
|
+
{
|
|
1047
|
+
field: 'calories',
|
|
1048
|
+
headerName: 'Calories (kcal)',
|
|
1049
|
+
headerLineClamp: 2,
|
|
1050
|
+
type: 'number',
|
|
1051
|
+
width: '80px',
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
field: 'fat',
|
|
1055
|
+
headerName: 'Fat Content (grams)',
|
|
1056
|
+
headerLineClamp: 2,
|
|
1057
|
+
type: 'number',
|
|
1058
|
+
width: '80px',
|
|
1059
|
+
},
|
|
1060
|
+
{
|
|
1061
|
+
field: 'protein',
|
|
1062
|
+
headerName: 'Protein (g)', // no headerLineClamp — single line (default)
|
|
1063
|
+
type: 'number',
|
|
1064
|
+
},
|
|
1065
|
+
];
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
#### Notes
|
|
1069
|
+
|
|
1070
|
+
- `headerLineClamp` is opt-in per column. Columns without it remain single-line.
|
|
1071
|
+
- Supported values: `1 | 2`.
|
|
1072
|
+
- Works with all existing features: sorting, pinning, resizing, and column grouping.
|
|
1073
|
+
|
|
898
1074
|
## Editing Features
|
|
899
1075
|
|
|
900
1076
|
### Inline Editing
|
|
@@ -1038,7 +1214,7 @@ You can use `onCellEditStart` and `onCellEditStop` to detect when editing starts
|
|
|
1038
1214
|
</Typography>
|
|
1039
1215
|
{editLog.length === 0 ? <Typography level="body-xs" textColor="text.secondary">
|
|
1040
1216
|
No edits yet. Click on a cell to edit.
|
|
1041
|
-
</Typography> : editLog.map(
|
|
1217
|
+
</Typography> : editLog.map(log => <Typography key={log} level="body-xs">
|
|
1042
1218
|
{log}
|
|
1043
1219
|
</Typography>)}
|
|
1044
1220
|
</Box>
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
InfoSign
|
|
5
|
+
InfoSign is a compact tooltip-trigger component that displays a small question-mark icon. When users hover over or focus on the icon, a tooltip appears with supplementary information. It is designed to provide contextual help without cluttering the interface -- ideal for explaining form fields, settings options, table column headers, and complex metrics.
|
|
6
|
+
|
|
7
|
+
The component accepts a `message` prop for the tooltip content and a `placement` prop to control where the tooltip appears relative to the icon. InfoSign keeps the main UI clean while ensuring that detailed guidance is always one interaction away.
|
|
6
8
|
|
|
7
9
|
```tsx
|
|
8
10
|
<InfoSign
|
|
@@ -22,21 +24,19 @@ import { InfoSign } from '@ceed/cds';
|
|
|
22
24
|
|
|
23
25
|
function MyComponent() {
|
|
24
26
|
return (
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
</
|
|
27
|
+
<label>
|
|
28
|
+
Complex Setting
|
|
29
|
+
<InfoSign
|
|
30
|
+
message="This setting affects overall system performance. Consult an administrator before making changes."
|
|
31
|
+
/>
|
|
32
|
+
</label>
|
|
31
33
|
);
|
|
32
34
|
}
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
### Basic Usage
|
|
37
|
+
## Basic
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
The default InfoSign displays a question-mark icon that reveals a tooltip on hover or focus.
|
|
40
40
|
|
|
41
41
|
```tsx
|
|
42
42
|
<InfoSign
|
|
@@ -50,104 +50,87 @@ function MyComponent() {
|
|
|
50
50
|
### Form Field Help
|
|
51
51
|
|
|
52
52
|
```tsx
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
비밀번호 복잡도
|
|
56
|
-
<InfoSign
|
|
57
|
-
message="비밀번호는 최소 8자 이상이며, 대문자, 소문자, 숫자, 특수문자를 포함해야 합니다."
|
|
58
|
-
placement="top"
|
|
59
|
-
/>
|
|
60
|
-
</FormLabel>
|
|
61
|
-
<Input type="password" />
|
|
62
|
-
</FormControl>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Settings Explanation
|
|
53
|
+
import { InfoSign } from '@ceed/cds';
|
|
54
|
+
import { FormControl, FormLabel, Input } from '@ceed/cds';
|
|
66
55
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</
|
|
56
|
+
function PasswordField() {
|
|
57
|
+
return (
|
|
58
|
+
<FormControl>
|
|
59
|
+
<FormLabel>
|
|
60
|
+
Password
|
|
61
|
+
<InfoSign
|
|
62
|
+
message="Must be at least 8 characters and include uppercase, lowercase, number, and special character."
|
|
63
|
+
placement="top"
|
|
64
|
+
/>
|
|
65
|
+
</FormLabel>
|
|
66
|
+
<Input type="password" />
|
|
67
|
+
</FormControl>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
77
70
|
```
|
|
78
71
|
|
|
79
|
-
###
|
|
72
|
+
### Table Column Header
|
|
80
73
|
|
|
81
74
|
```tsx
|
|
82
|
-
|
|
83
|
-
<CardContent>
|
|
84
|
-
<Typography level="title-md">
|
|
85
|
-
처리량 지표
|
|
86
|
-
<InfoSign
|
|
87
|
-
message="처리량은 최근 24시간 동안의 평균 요청 수를 나타냅니다. 이 값이 높을수록 시스템이 더 많은 작업을 처리하고 있다는 의미입니다."
|
|
88
|
-
placement="bottom"
|
|
89
|
-
/>
|
|
90
|
-
</Typography>
|
|
91
|
-
<Typography level="h2">1,234 req/min</Typography>
|
|
92
|
-
</CardContent>
|
|
93
|
-
</Card>
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Table Header Help
|
|
75
|
+
import { InfoSign } from '@ceed/cds';
|
|
97
76
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
77
|
+
function TableHeader() {
|
|
78
|
+
return (
|
|
79
|
+
<thead>
|
|
80
|
+
<tr>
|
|
81
|
+
<th>
|
|
82
|
+
User ID
|
|
83
|
+
<InfoSign message="Auto-generated unique identifier assigned by the system." />
|
|
84
|
+
</th>
|
|
85
|
+
<th>
|
|
86
|
+
Last Activity
|
|
87
|
+
<InfoSign message="The most recent login or action timestamp for this user." />
|
|
88
|
+
</th>
|
|
89
|
+
<th>Status</th>
|
|
90
|
+
</tr>
|
|
91
|
+
</thead>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
114
94
|
```
|
|
115
95
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
### placement
|
|
119
|
-
|
|
120
|
-
툴팁이 나타나는 위치를 설정할 수 있습니다.
|
|
96
|
+
### Dashboard Metric Explanation
|
|
121
97
|
|
|
122
98
|
```tsx
|
|
123
|
-
|
|
124
|
-
<InfoSign placement="bottom" message="아래쪽에 표시" />
|
|
125
|
-
<InfoSign placement="left" message="왼쪽에 표시" />
|
|
126
|
-
<InfoSign placement="right" message="오른쪽에 표시" />
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### message
|
|
99
|
+
import { InfoSign, Typography, Stack } from '@ceed/cds';
|
|
130
100
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<
|
|
101
|
+
function MetricCard() {
|
|
102
|
+
return (
|
|
103
|
+
<Stack spacing={1}>
|
|
104
|
+
<Typography level="title-md">
|
|
105
|
+
Throughput
|
|
106
|
+
<InfoSign
|
|
107
|
+
message="Average requests per minute over the last 24 hours. Higher values indicate greater system load."
|
|
108
|
+
placement="right"
|
|
109
|
+
/>
|
|
110
|
+
</Typography>
|
|
111
|
+
<Typography level="h2">1,234 req/min</Typography>
|
|
112
|
+
</Stack>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
135
115
|
```
|
|
136
116
|
|
|
137
117
|
## Best Practices
|
|
138
118
|
|
|
139
|
-
1
|
|
119
|
+
- **Keep messages concise.** Tooltip messages should be 1-2 sentences that quickly answer "what does this mean?" Avoid paragraphs of text.
|
|
120
|
+
- ✔ "Maximum file size per upload. Files exceeding this limit will be rejected."
|
|
121
|
+
- ✘ A multi-paragraph explanation with examples and edge cases
|
|
140
122
|
|
|
141
|
-
|
|
123
|
+
- **Place InfoSign directly adjacent to the element it explains.** Position it immediately after a label or header text so users can easily associate the help icon with the relevant field.
|
|
142
124
|
|
|
143
|
-
|
|
125
|
+
- **Do not overuse InfoSign.** Adding a help icon to every single field creates visual noise and suggests the interface itself is too confusing. Reserve it for genuinely complex or ambiguous items.
|
|
144
126
|
|
|
145
|
-
|
|
127
|
+
- **Choose placement that avoids overlap.** Use `placement="top"` or `placement="right"` for fields near the bottom of a viewport, and `placement="bottom"` for elements near the top.
|
|
146
128
|
|
|
147
|
-
|
|
129
|
+
- **Use InfoSign for supplementary information only.** Critical instructions or warnings should be displayed inline (e.g., with FormHelperText or Alert), not hidden behind a tooltip.
|
|
148
130
|
|
|
149
|
-
|
|
150
|
-
- 스크린 리더가 메시지 내용을 읽을 수 있습니다
|
|
151
|
-
- ARIA 라벨이 자동으로 적용됩니다
|
|
131
|
+
## Accessibility
|
|
152
132
|
|
|
153
|
-
InfoSign
|
|
133
|
+
- InfoSign is keyboard-focusable, allowing users to trigger the tooltip using **Tab** navigation without requiring a mouse.
|
|
134
|
+
- The tooltip content is announced by screen readers when the InfoSign receives focus, thanks to built-in ARIA attributes.
|
|
135
|
+
- The question-mark icon serves as a universally recognized visual cue for "more information," making it intuitive for all users.
|
|
136
|
+
- Ensure that the `message` text provides the same information available through other channels -- do not make the tooltip the only way to learn critical information.
|