@ceed/ads 1.25.1-next.3 → 1.27.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/ProfileMenu/ProfileMenu.d.ts +1 -1
- package/dist/components/data-display/Badge.md +71 -39
- package/dist/components/data-display/DataTable.md +177 -1
- package/dist/components/data-display/InfoSign.md +74 -98
- package/dist/components/data-display/Typography.md +363 -97
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Dialog.md +76 -62
- package/dist/components/feedback/Modal.md +259 -44
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/inputs/Autocomplete.md +356 -107
- package/dist/components/inputs/ButtonGroup.md +115 -106
- package/dist/components/inputs/Calendar.md +98 -459
- 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/FilterMenu.md +169 -19
- package/dist/components/inputs/FilterableCheckboxGroup.md +123 -23
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/Input.md +5 -0
- 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 +150 -61
- package/dist/components/inputs/Select.md +222 -326
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +136 -376
- package/dist/components/inputs/Textarea.md +213 -10
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +3 -0
- package/dist/components/navigation/Breadcrumbs.md +80 -322
- package/dist/components/navigation/Dropdown.md +92 -221
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +68 -738
- package/dist/components/navigation/Link.md +39 -298
- 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/ProfileMenu.md +45 -268
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Sheet.md +151 -334
- package/dist/guides/ThemeProvider.md +116 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/index.browser.js +16 -18
- package/dist/index.browser.js.map +4 -4
- package/dist/index.cjs +381 -244
- package/dist/index.js +459 -378
- package/dist/llms.txt +8 -0
- package/framer/index.js +1 -166
- package/package.json +15 -16
|
@@ -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
|
export 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>;
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
Badge
|
|
5
|
+
Badge is a small visual indicator that overlays another UI element to display a count, status, or label. It is commonly used to show unread notification counts, online/offline status indicators, and "NEW" labels on menu items or navigation elements. The Badge wraps a child component (such as an icon button or avatar) and positions itself in the top-right corner by default.
|
|
6
|
+
|
|
7
|
+
Badge supports multiple colors, variants, and sizes for flexible visual customization. It also provides utility features like a `max` prop for truncating large numbers (e.g., "99+"), a `showZero` prop for controlling zero-count visibility, and a dot-only mode when no `badgeContent` is provided.
|
|
6
8
|
|
|
7
9
|
```tsx
|
|
8
10
|
<Badge {...args}>
|
|
@@ -26,7 +28,7 @@ Badge 컴포넌트는 다른 UI 요소 위에 작은 카운트, 상태, 또는
|
|
|
26
28
|
|
|
27
29
|
```tsx
|
|
28
30
|
import { Badge, IconButton } from '@ceed/ads';
|
|
29
|
-
import
|
|
31
|
+
import NotificationsIcon from '@mui/icons-material/Notifications';
|
|
30
32
|
|
|
31
33
|
function MyComponent() {
|
|
32
34
|
return (
|
|
@@ -39,11 +41,9 @@ function MyComponent() {
|
|
|
39
41
|
}
|
|
40
42
|
```
|
|
41
43
|
|
|
42
|
-
##
|
|
43
|
-
|
|
44
|
-
### Basic Usage
|
|
44
|
+
## Basic
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Basic Badge usage showing numeric counts, max overflow, and text content.
|
|
47
47
|
|
|
48
48
|
```tsx
|
|
49
49
|
<div style={{
|
|
@@ -70,9 +70,9 @@ function MyComponent() {
|
|
|
70
70
|
</div>
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
## Colors
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Apply semantic colors to convey the badge's meaning: `primary` for general counts, `danger` for urgent notifications, `success` for online status, and `warning` for attention items.
|
|
76
76
|
|
|
77
77
|
```tsx
|
|
78
78
|
<div style={{
|
|
@@ -111,9 +111,9 @@ function MyComponent() {
|
|
|
111
111
|
</div>
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
## Variants
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
Four style variants are available: `solid` (filled), `soft` (subtle background), `outlined` (border only), and `plain` (minimal).
|
|
117
117
|
|
|
118
118
|
```tsx
|
|
119
119
|
<div style={{
|
|
@@ -146,9 +146,9 @@ function MyComponent() {
|
|
|
146
146
|
</div>
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
## Sizes
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
Three sizes -- `sm`, `md`, and `lg` -- allow the badge to scale proportionally with its wrapped element.
|
|
152
152
|
|
|
153
153
|
```tsx
|
|
154
154
|
<div style={{
|
|
@@ -176,9 +176,9 @@ function MyComponent() {
|
|
|
176
176
|
</div>
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
## With Avatar
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
Badge pairs naturally with Avatar to indicate user status (e.g., notification count or online indicator).
|
|
182
182
|
|
|
183
183
|
```tsx
|
|
184
184
|
<div style={{
|
|
@@ -195,9 +195,9 @@ function MyComponent() {
|
|
|
195
195
|
</div>
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
-
|
|
198
|
+
## Dot Badge
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
When no `badgeContent` is provided, Badge renders as a small dot. This is useful for simple presence or status indicators without a specific count.
|
|
201
201
|
|
|
202
202
|
```tsx
|
|
203
203
|
<div style={{
|
|
@@ -216,9 +216,9 @@ function MyComponent() {
|
|
|
216
216
|
</div>
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
## Max Count
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
Use the `max` prop to cap the displayed number. When `badgeContent` exceeds `max`, it renders as "max+" (e.g., "99+").
|
|
222
222
|
|
|
223
223
|
```tsx
|
|
224
224
|
<div style={{
|
|
@@ -245,9 +245,9 @@ function MyComponent() {
|
|
|
245
245
|
</div>
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
|
|
248
|
+
## Show Zero
|
|
249
249
|
|
|
250
|
-
|
|
250
|
+
By default, Badge hides when `badgeContent` is `0`. Use the `showZero` prop to force it to remain visible.
|
|
251
251
|
|
|
252
252
|
```tsx
|
|
253
253
|
<div style={{
|
|
@@ -276,40 +276,72 @@ function MyComponent() {
|
|
|
276
276
|
|
|
277
277
|
## Common Use Cases
|
|
278
278
|
|
|
279
|
-
### Notification
|
|
279
|
+
### Notification Bell
|
|
280
280
|
|
|
281
281
|
```tsx
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
282
|
+
import { Badge, IconButton } from '@ceed/ads';
|
|
283
|
+
import NotificationsIcon from '@mui/icons-material/Notifications';
|
|
284
|
+
|
|
285
|
+
function NotificationBell({ unreadCount }) {
|
|
286
|
+
return (
|
|
287
|
+
<Badge badgeContent={unreadCount} max={99} color="danger">
|
|
288
|
+
<IconButton aria-label={`${unreadCount} unread notifications`}>
|
|
289
|
+
<NotificationsIcon />
|
|
290
|
+
</IconButton>
|
|
291
|
+
</Badge>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
287
294
|
```
|
|
288
295
|
|
|
289
|
-
### Status
|
|
296
|
+
### Online Status on Avatar
|
|
290
297
|
|
|
291
298
|
```tsx
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
299
|
+
import { Badge, Avatar } from '@ceed/ads';
|
|
300
|
+
|
|
301
|
+
function UserAvatar({ name, avatarUrl, isOnline }) {
|
|
302
|
+
return (
|
|
303
|
+
<Badge
|
|
304
|
+
color={isOnline ? 'success' : 'neutral'}
|
|
305
|
+
variant="solid"
|
|
306
|
+
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
|
307
|
+
>
|
|
308
|
+
<Avatar src={avatarUrl} alt={name} />
|
|
309
|
+
</Badge>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
295
312
|
```
|
|
296
313
|
|
|
297
|
-
### New
|
|
314
|
+
### New Feature Label
|
|
298
315
|
|
|
299
316
|
```tsx
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
317
|
+
import { Badge, Button } from '@ceed/ads';
|
|
318
|
+
|
|
319
|
+
function FeatureButton() {
|
|
320
|
+
return (
|
|
321
|
+
<Badge badgeContent="NEW" color="warning" size="sm">
|
|
322
|
+
<Button variant="outlined">Analytics</Button>
|
|
323
|
+
</Badge>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
303
326
|
```
|
|
304
327
|
|
|
305
328
|
## Best Practices
|
|
306
329
|
|
|
307
|
-
|
|
330
|
+
- **Use semantic colors consistently.** Reserve `danger` for urgent notifications, `success` for positive status (e.g., online), `warning` for attention-needed items, and `primary` for neutral counts.
|
|
331
|
+
- ✔ `color="danger"` for unread message count
|
|
332
|
+
- ✘ `color="primary"` for critical alert count
|
|
333
|
+
|
|
334
|
+
- **Set a `max` value for numeric badges.** Avoid displaying very large numbers like "1,247" in a tiny badge. Use `max={99}` or `max={999}` to keep the badge compact and readable.
|
|
335
|
+
|
|
336
|
+
- **Keep badge content short.** Badges work best with numbers (1-3 digits) or very short text ("NEW", "HOT"). Long text will overflow and look broken.
|
|
308
337
|
|
|
309
|
-
|
|
338
|
+
- **Use dot badges for binary states.** When the specific count does not matter (e.g., "has unread items" vs. "12 unread items"), use a dot badge by omitting `badgeContent`.
|
|
310
339
|
|
|
311
|
-
|
|
340
|
+
- **Do not badge everything.** Only add badges to elements where the count or status information is genuinely useful. Overuse diminishes their attention-grabbing effect.
|
|
312
341
|
|
|
313
|
-
|
|
342
|
+
## Accessibility
|
|
314
343
|
|
|
315
|
-
|
|
344
|
+
- Badge automatically applies `aria-label` or equivalent attributes so screen readers can announce the badge content alongside the wrapped element.
|
|
345
|
+
- When using Badge on an IconButton, always provide an `aria-label` on the IconButton that includes the badge context (e.g., `aria-label="5 unread notifications"`).
|
|
346
|
+
- Avoid relying solely on color to convey badge meaning. Pair colored badges with icons, text, or positional context so color-blind users can also interpret the status.
|
|
347
|
+
- Dot badges (without `badgeContent`) should have an accessible label on the parent element that describes the status (e.g., "User is online").
|
|
@@ -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>
|