@bug-on/md3-react 3.0.1 → 3.0.3
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/.turbo/turbo-build.log +42 -42
- package/CHANGELOG.md +10 -0
- package/dist/index.css +107 -0
- package/dist/index.d.mts +1491 -1053
- package/dist/index.d.ts +1491 -1053
- package/dist/index.js +4457 -3156
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4394 -3109
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -6
- package/scripts/copy-assets.js +113 -8
- package/src/index.ts +66 -18
- package/src/test/button.test.tsx +1 -1
- package/src/ui/app-bar/app-bar.tokens.ts +5 -24
- package/src/ui/badge.tsx +2 -1
- package/src/ui/buttons/button/button-tokens.ts +118 -0
- package/src/ui/{button.test.tsx → buttons/button/button.test.tsx} +0 -21
- package/src/ui/buttons/button/button.tsx +381 -0
- package/src/ui/buttons/button/index.ts +3 -0
- package/src/ui/buttons/button/types.ts +90 -0
- package/src/ui/buttons/button-group/button-group-defaults.ts +95 -0
- package/src/ui/buttons/button-group/button-group-tokens.ts +20 -0
- package/src/ui/{button-group.test.tsx → buttons/button-group/button-group.test.tsx} +9 -10
- package/src/ui/buttons/button-group/button-group.tsx +699 -0
- package/src/ui/buttons/button-group/index.ts +8 -0
- package/src/ui/buttons/button-group/types.ts +77 -0
- package/src/ui/{fab.tsx → buttons/fabs/fab/fab.tsx} +6 -6
- package/src/ui/buttons/fabs/fab/index.ts +1 -0
- package/src/ui/{fab-menu.tsx → buttons/fabs/fab-menu/fab-menu.tsx} +7 -4
- package/src/ui/buttons/fabs/fab-menu/index.ts +1 -0
- package/src/ui/buttons/fabs/index.ts +2 -0
- package/src/ui/{icon-button.tsx → buttons/icon-button/icon-button.tsx} +6 -6
- package/src/ui/buttons/icon-button/index.ts +1 -0
- package/src/ui/buttons/index.ts +4 -0
- package/src/ui/code-block.tsx +1 -1
- package/src/ui/dialog.tsx +4 -7
- package/src/ui/drawer.tsx +4 -7
- package/src/ui/menu/menu-animations.ts +14 -20
- package/src/ui/menu/menu-tokens.ts +7 -5
- package/src/ui/menu/menu.test.tsx +9 -4
- package/src/ui/navigation-bar.test.tsx +111 -0
- package/src/ui/navigation-bar.tsx +464 -0
- package/src/ui/navigation-rail.test.tsx +5 -4
- package/src/ui/navigation-rail.tsx +32 -23
- package/src/ui/scroll-area.tsx +4 -0
- package/src/ui/search/search-view-fullscreen.tsx +1 -1
- package/src/ui/search/search.tokens.ts +9 -43
- package/src/ui/search/trailing-action.tsx +1 -1
- package/src/ui/shared/constants.ts +25 -27
- package/src/ui/shared/motion-tokens.ts +238 -0
- package/src/ui/snackbar/snackbar.tsx +4 -6
- package/src/ui/switch/switch.tsx +12 -18
- package/src/ui/text-field/text-field.tokens.ts +12 -12
- package/src/ui/text-field/text-field.tsx +31 -19
- package/src/ui/theme-provider/index.tsx +1 -5
- package/src/ui/toc.tsx +1 -1
- package/src/ui/toolbar/__snapshots__/bottom-docked-toolbar.test.tsx.snap +51 -0
- package/src/ui/toolbar/__snapshots__/floating-toolbar-with-fab.test.tsx.snap +113 -0
- package/src/ui/toolbar/__snapshots__/floating-toolbar.test.tsx.snap +169 -0
- package/src/ui/toolbar/bottom-docked-toolbar.test.tsx +114 -0
- package/src/ui/toolbar/docked-toolbar.tsx +186 -0
- package/src/ui/toolbar/floating-toolbar-with-fab.test.tsx +139 -0
- package/src/ui/toolbar/floating-toolbar-with-fab.tsx +199 -0
- package/src/ui/toolbar/floating-toolbar.test.tsx +230 -0
- package/src/ui/toolbar/floating-toolbar.tsx +344 -0
- package/src/ui/toolbar/index.ts +35 -0
- package/src/ui/toolbar/toolbar-colors.ts +37 -0
- package/src/ui/toolbar/toolbar-context.tsx +13 -0
- package/src/ui/toolbar/toolbar-divider.test.tsx +54 -0
- package/src/ui/toolbar/toolbar-divider.tsx +73 -0
- package/src/ui/toolbar/toolbar-icon-button.test.tsx +68 -0
- package/src/ui/toolbar/toolbar-icon-button.tsx +136 -0
- package/src/ui/toolbar/toolbar-scroll-behavior.ts +140 -0
- package/src/ui/toolbar/toolbar-tokens.ts +51 -0
- package/test-clip.html +31 -0
- package/test-shadow.html +5 -1
- package/test-width.html +34 -0
- package/src/ui/button-group.tsx +0 -350
- package/src/ui/button.tsx +0 -665
- package/test-render.tsx +0 -4
- package/test_output.txt +0 -164
- package/test_output_v2.txt +0 -5
- /package/src/ui/{fab.test.tsx → buttons/fabs/fab/fab.test.tsx} +0 -0
- /package/src/ui/{fab-menu.test.tsx → buttons/fabs/fab-menu/fab-menu.test.tsx} +0 -0
- /package/src/ui/{icon-button.test.tsx → buttons/icon-button/icon-button.test.tsx} +0 -0
- /package/src/ui/{Text.tsx → text.tsx} +0 -0
package/src/ui/button-group.tsx
DELETED
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { cn } from "../lib/utils";
|
|
3
|
-
import type { ButtonProps } from "./button";
|
|
4
|
-
import { Icon } from "./icon";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Thuộc tính truyền vào cho thành phần nhóm nút (Button Group).
|
|
8
|
-
*/
|
|
9
|
-
export interface ButtonGroupProps
|
|
10
|
-
extends React.FieldsetHTMLAttributes<HTMLFieldSetElement> {
|
|
11
|
-
/**
|
|
12
|
-
* Cấu trúc hiển thị của nhóm nút:
|
|
13
|
-
* - `standard`: Các nút cách xa và có khoảng cách độc lập với nhau (gap).
|
|
14
|
-
* - `connected`: Các nút nối liền khung viền với nhau để tạo thành dạng Segmented Button.
|
|
15
|
-
* @default "standard"
|
|
16
|
-
*/
|
|
17
|
-
variant?: "standard" | "connected";
|
|
18
|
-
/**
|
|
19
|
-
* Hướng sắp xếp của các nút trong nhóm.
|
|
20
|
-
* @default "horizontal"
|
|
21
|
-
*/
|
|
22
|
-
orientation?: "horizontal" | "vertical";
|
|
23
|
-
/**
|
|
24
|
-
* Đặt thành `true` nếu bạn muốn nhóm hiển thị dạng `standard` giãn đều lấp đầy toàn bộ khu vực chứa (container).
|
|
25
|
-
* @default false
|
|
26
|
-
*/
|
|
27
|
-
fullWidth?: boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Áp dụng thống nhất chung một kích thước (`size`) cho tất cả các con trong nhóm (ghi đè kích thước lẻ từng nút).
|
|
30
|
-
*/
|
|
31
|
-
size?: "xs" | "sm" | "md" | "lg" | "xl";
|
|
32
|
-
/**
|
|
33
|
-
* Bật/tắt hiệu ứng thu phóng độ rộng / khoảng đệm (Morphing Width) khi nhấn vào các nút (áp dụng cho nhóm `standard`).
|
|
34
|
-
* @default true
|
|
35
|
-
*/
|
|
36
|
-
morphingWidth?: boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Tự động hiển thị biểu tượng (icon) Check khi một nút trạng thái nằm trong nhóm được chỉ định là `selected={true}`.
|
|
39
|
-
* @default false
|
|
40
|
-
*/
|
|
41
|
-
showCheck?: boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Bảng ánh xạ padding mặc định tương ứng với từng size trong button.tsx
|
|
45
|
-
const SIZE_PADDING_MAP: Record<string, string> = {
|
|
46
|
-
xs: "0.75rem",
|
|
47
|
-
sm: "1rem",
|
|
48
|
-
md: "1.5rem",
|
|
49
|
-
lg: "3rem",
|
|
50
|
-
xl: "3rem",
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// Bảng ánh xạ Pill Radius (h/2) để animation mượt mà
|
|
54
|
-
const PILL_RADIUS_MAP: Record<string, number> = {
|
|
55
|
-
xs: 16,
|
|
56
|
-
sm: 20,
|
|
57
|
-
md: 28,
|
|
58
|
-
lg: 48,
|
|
59
|
-
xl: 68,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// ĐỒNG BỘ SPECS MD3: Bảng ánh xạ góc bo bên trong (Inner Radius) của Connected Group
|
|
63
|
-
const INNER_RADIUS_MAP: Record<string, number> = {
|
|
64
|
-
xs: 4,
|
|
65
|
-
sm: 8,
|
|
66
|
-
md: 8,
|
|
67
|
-
lg: 16,
|
|
68
|
-
xl: 20,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Bảng ánh xạ Pressed Radius để morphing shape từ pill sang square
|
|
72
|
-
const PRESSED_RADIUS_MAP: Record<string, number> = {
|
|
73
|
-
xs: 8,
|
|
74
|
-
sm: 10,
|
|
75
|
-
md: 16,
|
|
76
|
-
lg: 28,
|
|
77
|
-
xl: 40,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const ButtonGroupComponent = React.forwardRef<
|
|
81
|
-
HTMLFieldSetElement,
|
|
82
|
-
ButtonGroupProps
|
|
83
|
-
>(
|
|
84
|
-
(
|
|
85
|
-
{
|
|
86
|
-
className,
|
|
87
|
-
variant = "standard",
|
|
88
|
-
orientation = "horizontal",
|
|
89
|
-
fullWidth = false,
|
|
90
|
-
size,
|
|
91
|
-
morphingWidth = true,
|
|
92
|
-
showCheck = false,
|
|
93
|
-
children,
|
|
94
|
-
...props
|
|
95
|
-
},
|
|
96
|
-
ref,
|
|
97
|
-
) => {
|
|
98
|
-
const [pressedIndex, setPressedIndex] = React.useState<number | null>(null);
|
|
99
|
-
|
|
100
|
-
const childrenArray = React.useMemo(
|
|
101
|
-
() => React.Children.toArray(children).filter(React.isValidElement),
|
|
102
|
-
[children],
|
|
103
|
-
);
|
|
104
|
-
const count = childrenArray.length;
|
|
105
|
-
|
|
106
|
-
const handlePointerLeaveAndUp = React.useCallback(() => {
|
|
107
|
-
setPressedIndex(null);
|
|
108
|
-
}, []);
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<fieldset
|
|
112
|
-
ref={ref}
|
|
113
|
-
className={cn(
|
|
114
|
-
"inline-flex p-0 m-0 border-none max-w-full overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
115
|
-
orientation === "vertical" ? "flex-col items-stretch" : "flex-row",
|
|
116
|
-
// SPECS: Standard = 8px (gap-2), Connected = chính xác 2px
|
|
117
|
-
variant === "standard" ? "gap-2" : "gap-0.5",
|
|
118
|
-
fullWidth && (orientation === "horizontal" ? "w-full" : "h-full"),
|
|
119
|
-
className,
|
|
120
|
-
)}
|
|
121
|
-
onPointerLeave={handlePointerLeaveAndUp}
|
|
122
|
-
onPointerUp={handlePointerLeaveAndUp}
|
|
123
|
-
{...props}
|
|
124
|
-
>
|
|
125
|
-
{childrenArray.map((child, index) => {
|
|
126
|
-
const isFirst = index === 0;
|
|
127
|
-
const isLast = index === count - 1;
|
|
128
|
-
|
|
129
|
-
const element = child as React.ReactElement<ButtonProps>;
|
|
130
|
-
|
|
131
|
-
// Lấy kích thước hiện tại để tính toán Specs
|
|
132
|
-
// Ưu tiên prop size của Group, nếu không có thì lấy của nút con, mặc định là "sm"
|
|
133
|
-
const itemSize = size || element.props.size || "sm";
|
|
134
|
-
const basePx = SIZE_PADDING_MAP[itemSize] || "1rem";
|
|
135
|
-
const innerRadius = INNER_RADIUS_MAP[itemSize] || 8;
|
|
136
|
-
|
|
137
|
-
// Inject style động
|
|
138
|
-
const dynamicStyle: React.CSSProperties = {
|
|
139
|
-
...element.props.style,
|
|
140
|
-
"--m3-inner-rad": `${innerRadius}px`,
|
|
141
|
-
} as React.CSSProperties;
|
|
142
|
-
|
|
143
|
-
let motionPropsToOverride: Partial<ButtonProps> = {};
|
|
144
|
-
let explicitIcon = element.props.icon;
|
|
145
|
-
const isSelected = element.props.selected === true;
|
|
146
|
-
|
|
147
|
-
if (showCheck && isSelected && !explicitIcon) {
|
|
148
|
-
explicitIcon = <Icon name="check" aria-hidden="true" />;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// 1. STANDARD GROUP: Xử lý hiệu ứng Morphing Width khi press
|
|
152
|
-
if (
|
|
153
|
-
variant === "standard" &&
|
|
154
|
-
orientation === "horizontal" &&
|
|
155
|
-
morphingWidth
|
|
156
|
-
) {
|
|
157
|
-
const isPressed = pressedIndex === index;
|
|
158
|
-
const isNeighbor =
|
|
159
|
-
pressedIndex !== null && Math.abs(pressedIndex - index) === 1;
|
|
160
|
-
|
|
161
|
-
if (fullWidth) {
|
|
162
|
-
if (isPressed) {
|
|
163
|
-
dynamicStyle.flex = "1.15";
|
|
164
|
-
} else if (pressedIndex !== null) {
|
|
165
|
-
dynamicStyle.flex = isNeighbor ? "0.925" : "1";
|
|
166
|
-
} else {
|
|
167
|
-
dynamicStyle.flex = "1";
|
|
168
|
-
}
|
|
169
|
-
dynamicStyle.transition = "flex 0.3s cubic-bezier(0.2, 0, 0, 1)";
|
|
170
|
-
} else {
|
|
171
|
-
if (isPressed) {
|
|
172
|
-
dynamicStyle.paddingInline = `calc(${basePx} + 0.6rem)`;
|
|
173
|
-
} else if (isNeighbor) {
|
|
174
|
-
dynamicStyle.paddingInline = `calc(${basePx} - 0.3rem)`;
|
|
175
|
-
} else {
|
|
176
|
-
dynamicStyle.paddingInline = basePx;
|
|
177
|
-
}
|
|
178
|
-
dynamicStyle.transition =
|
|
179
|
-
"padding 0.2s cubic-bezier(0.2, 0, 0, 1)";
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const pressedRadius = PRESSED_RADIUS_MAP[itemSize] || 10;
|
|
183
|
-
|
|
184
|
-
motionPropsToOverride = {
|
|
185
|
-
whileTap: {
|
|
186
|
-
scale: 0.98,
|
|
187
|
-
borderRadius: pressedRadius,
|
|
188
|
-
},
|
|
189
|
-
transition: { type: "spring", stiffness: 400, damping: 25 },
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// 2. CONNECTED GROUP: Xử lý trạng thái Selected và Border Radius
|
|
194
|
-
let connectedClasses = "";
|
|
195
|
-
if (variant === "connected") {
|
|
196
|
-
// Ưu tiên z-index khi selected/hover để đè viền lên nhau
|
|
197
|
-
if (isSelected) {
|
|
198
|
-
connectedClasses = "z-20";
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const r = PILL_RADIUS_MAP[itemSize] || 20;
|
|
202
|
-
const i = innerRadius;
|
|
203
|
-
let tl = r,
|
|
204
|
-
tr = r,
|
|
205
|
-
br = r,
|
|
206
|
-
bl = r;
|
|
207
|
-
|
|
208
|
-
if (!isSelected) {
|
|
209
|
-
if (orientation === "horizontal") {
|
|
210
|
-
if (isFirst && !isLast) {
|
|
211
|
-
tl = r;
|
|
212
|
-
tr = i;
|
|
213
|
-
br = i;
|
|
214
|
-
bl = r;
|
|
215
|
-
} else if (!isFirst && isLast) {
|
|
216
|
-
tl = i;
|
|
217
|
-
tr = r;
|
|
218
|
-
br = r;
|
|
219
|
-
bl = i;
|
|
220
|
-
} else if (!isFirst && !isLast) {
|
|
221
|
-
tl = i;
|
|
222
|
-
tr = i;
|
|
223
|
-
br = i;
|
|
224
|
-
bl = i;
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
// vertical
|
|
228
|
-
if (isFirst && !isLast) {
|
|
229
|
-
tl = r;
|
|
230
|
-
tr = r;
|
|
231
|
-
br = i;
|
|
232
|
-
bl = i;
|
|
233
|
-
} else if (!isFirst && isLast) {
|
|
234
|
-
tl = i;
|
|
235
|
-
tr = i;
|
|
236
|
-
br = r;
|
|
237
|
-
bl = r;
|
|
238
|
-
} else if (!isFirst && !isLast) {
|
|
239
|
-
tl = i;
|
|
240
|
-
tr = i;
|
|
241
|
-
br = i;
|
|
242
|
-
bl = i;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Set inline style (fallback/SSR)
|
|
248
|
-
dynamicStyle.borderTopLeftRadius = `${tl}px`;
|
|
249
|
-
dynamicStyle.borderTopRightRadius = `${tr}px`;
|
|
250
|
-
dynamicStyle.borderBottomRightRadius = `${br}px`;
|
|
251
|
-
dynamicStyle.borderBottomLeftRadius = `${bl}px`;
|
|
252
|
-
|
|
253
|
-
// Transition CSS thuần
|
|
254
|
-
dynamicStyle.transition =
|
|
255
|
-
"border-top-left-radius 0.25s cubic-bezier(0.2, 0, 0, 1), border-top-right-radius 0.25s cubic-bezier(0.2, 0, 0, 1), border-bottom-right-radius 0.25s cubic-bezier(0.2, 0, 0, 1), border-bottom-left-radius 0.25s cubic-bezier(0.2, 0, 0, 1), padding 0.2s cubic-bezier(0.2, 0, 0, 1), flex 0.2s cubic-bezier(0.2, 0, 0, 1)";
|
|
256
|
-
|
|
257
|
-
// VÔ HIỆU HÓA Framer Motion borderRadius trên children Button bằng cách override explicit corners
|
|
258
|
-
const animateProps =
|
|
259
|
-
typeof element.props.animate === "object" &&
|
|
260
|
-
!Array.isArray(element.props.animate)
|
|
261
|
-
? element.props.animate
|
|
262
|
-
: {};
|
|
263
|
-
const whileTapProps =
|
|
264
|
-
typeof element.props.whileTap === "object" &&
|
|
265
|
-
!Array.isArray(element.props.whileTap)
|
|
266
|
-
? element.props.whileTap
|
|
267
|
-
: {};
|
|
268
|
-
|
|
269
|
-
// Tính toán corners khi pressed: tất cả corners đều thu về innerRadius hoặc pressedRadius
|
|
270
|
-
// Theo MD3, khi nhấn Segmented Button, nó nên morph về dạng vuông hơn.
|
|
271
|
-
const pr = INNER_RADIUS_MAP[itemSize] || 8;
|
|
272
|
-
|
|
273
|
-
motionPropsToOverride.animate = {
|
|
274
|
-
...animateProps,
|
|
275
|
-
borderRadius: undefined,
|
|
276
|
-
borderTopLeftRadius: tl,
|
|
277
|
-
borderTopRightRadius: tr,
|
|
278
|
-
borderBottomRightRadius: br,
|
|
279
|
-
borderBottomLeftRadius: bl,
|
|
280
|
-
} as ButtonProps["animate"];
|
|
281
|
-
motionPropsToOverride.whileTap = {
|
|
282
|
-
...whileTapProps,
|
|
283
|
-
borderRadius: undefined,
|
|
284
|
-
borderTopLeftRadius: pr,
|
|
285
|
-
borderTopRightRadius: pr,
|
|
286
|
-
borderBottomRightRadius: pr,
|
|
287
|
-
borderBottomLeftRadius: pr,
|
|
288
|
-
} as ButtonProps["whileTap"];
|
|
289
|
-
|
|
290
|
-
motionPropsToOverride.transition = {
|
|
291
|
-
type: "spring",
|
|
292
|
-
bounce: 0,
|
|
293
|
-
duration: 0.2,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return React.cloneElement(element, {
|
|
298
|
-
key: element.key ?? index,
|
|
299
|
-
tabIndex: isFirst ? 0 : -1,
|
|
300
|
-
size: size || element.props.size, // Push size group down to children
|
|
301
|
-
icon: explicitIcon, // Override icon if showCheck injected it
|
|
302
|
-
// Automatically apply unselected/selected color semantics for connected group
|
|
303
|
-
...(variant === "connected" && {
|
|
304
|
-
colorStyle: element.props.colorStyle || "tonal",
|
|
305
|
-
selectedColorStyle: element.props.selectedColorStyle || "filled",
|
|
306
|
-
}),
|
|
307
|
-
className: cn(
|
|
308
|
-
element.props.className,
|
|
309
|
-
connectedClasses,
|
|
310
|
-
"focus-visible:z-10 hover:z-10 relative",
|
|
311
|
-
),
|
|
312
|
-
style: dynamicStyle,
|
|
313
|
-
onPointerDown: (e: React.PointerEvent<HTMLButtonElement>) => {
|
|
314
|
-
setPressedIndex(index);
|
|
315
|
-
if (element.props.onPointerDown) {
|
|
316
|
-
element.props.onPointerDown(e);
|
|
317
|
-
}
|
|
318
|
-
},
|
|
319
|
-
...motionPropsToOverride,
|
|
320
|
-
});
|
|
321
|
-
})}
|
|
322
|
-
</fieldset>
|
|
323
|
-
);
|
|
324
|
-
},
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
ButtonGroupComponent.displayName = "ButtonGroup";
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Component Nhóm Nút (Button Group) được sử dụng để gom nhóm nhiều nút có công năng tương tự lại với nhau.
|
|
331
|
-
* Hỗ trợ tạo các bộ nút độc lập (Standard) hoặc khối liên kết liền mạch (Connected/Segmented Buttons).
|
|
332
|
-
* Kế thừa cơ chế chuyển động kết hợp liền mạch của MD3 Expressive.
|
|
333
|
-
*
|
|
334
|
-
* @example
|
|
335
|
-
* ```tsx
|
|
336
|
-
* // Nhóm nút tiêu chuẩn rời rạc (Standard)
|
|
337
|
-
* <ButtonGroup variant="standard">
|
|
338
|
-
* <Button>Lựa chọn 1</Button>
|
|
339
|
-
* <Button>Lựa chọn 2</Button>
|
|
340
|
-
* </ButtonGroup>
|
|
341
|
-
*
|
|
342
|
-
* // Nhóm nút liền khối (Connected Segmented Button)
|
|
343
|
-
* <ButtonGroup variant="connected" showCheck fullWidth>
|
|
344
|
-
* <Button variant="toggle" selected={view === "day"} onClick={() => setView("day")}>Ngày</Button>
|
|
345
|
-
* <Button variant="toggle" selected={view === "week"} onClick={() => setView("week")}>Tuần</Button>
|
|
346
|
-
* <Button variant="toggle" selected={view === "month"} onClick={() => setView("month")}>Tháng</Button>
|
|
347
|
-
* </ButtonGroup>
|
|
348
|
-
* ```
|
|
349
|
-
*/
|
|
350
|
-
export const ButtonGroup = React.memo(ButtonGroupComponent);
|