Skip to content

Commit 5fa37b0

Browse files
committed
feat: toggle button styled layer
1 parent d9e5aa0 commit 5fa37b0

File tree

21 files changed

+455
-194
lines changed

21 files changed

+455
-194
lines changed
Lines changed: 16 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,39 @@
11
"use client";
22

33
import "@seed-design/stylesheet/progressCircle.css";
4+
// TODO: we have to ensure load order between reactionButton.css and progressCircle.css. should we bundle them together?
45
import "@seed-design/stylesheet/reactionButton.css";
56

6-
import { Slot } from "@radix-ui/react-slot";
7-
import { useToggle, type UseToggleProps } from "@seed-design/react-toggle";
8-
import {
9-
reactionButton,
10-
type ReactionButtonVariantProps,
11-
} from "@seed-design/recipe/reactionButton";
12-
import clsx from "clsx";
7+
import { ReactionButton as SeedReactionButton } from "@seed-design/react";
138
import * as React from "react";
14-
import { ProgressCircle } from "./progress-circle";
159

16-
export interface ReactionButtonProps
17-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
18-
UseToggleProps,
19-
ReactionButtonVariantProps {
10+
export interface ReactionButtonProps extends SeedReactionButton.RootProps {
2011
prefixIcon?: React.ReactNode;
21-
22-
count?: number;
23-
24-
loading?: boolean;
25-
26-
/**
27-
* @default false
28-
*/
29-
asChild?: boolean;
3012
}
3113

3214
/**
33-
* @see https://v3.seed-design.io/docs/react/components/reaction-button
15+
* @see https://v3.seed-design.io/docs/react/components/toggle-button
3416
*/
3517
export const ReactionButton = React.forwardRef<
36-
HTMLButtonElement,
18+
React.ElementRef<typeof SeedReactionButton.Root>,
3719
ReactionButtonProps
3820
>(
3921
(
40-
{
41-
className,
42-
size = "small",
43-
children,
44-
prefixIcon,
45-
count,
46-
loading = false,
47-
asChild = false,
48-
...otherProps
49-
},
22+
{ className, loading = false, prefixIcon, children, ...otherProps },
5023
ref,
5124
) => {
52-
const Comp = asChild ? Slot : "button";
53-
const { rootProps, stateProps, restProps } = useToggle(otherProps);
54-
const classNames = reactionButton({ size });
55-
const dataProps = {
56-
...stateProps,
57-
"data-loading": loading ? "" : undefined,
58-
};
59-
6025
return (
61-
<Comp
62-
ref={ref}
63-
className={clsx(classNames.root, className)}
64-
{...dataProps}
65-
{...rootProps}
66-
{...restProps}
67-
>
68-
{prefixIcon && (
69-
<Slot aria-hidden {...dataProps} className={classNames.prefixIcon}>
70-
{prefixIcon}
71-
</Slot>
72-
)}
73-
<span {...dataProps} className={classNames.label}>
74-
{children}
75-
</span>
76-
<span {...dataProps} className={classNames.count}>
77-
{count}
78-
</span>
79-
{loading ? (
80-
<ProgressCircle
81-
{...dataProps}
82-
className={classNames.progressCircle}
83-
/>
84-
) : null}
85-
</Comp>
26+
<SeedReactionButton.Root ref={ref} loading={loading} {...otherProps}>
27+
{prefixIcon && <SeedReactionButton.PrefixIcon svg={prefixIcon} />}
28+
<SeedReactionButton.Label>{children}</SeedReactionButton.Label>
29+
{loading ? <SeedReactionButton.ProgressCircle /> : null}
30+
</SeedReactionButton.Root>
8631
);
8732
},
8833
);
8934
ReactionButton.displayName = "ReactionButton";
35+
36+
/**
37+
* This file is generated snippet from the Seed Design.
38+
* You can extend the functionality from this snippet if needed.
39+
*/

docs/registry/ui/toggle-button.tsx

Lines changed: 17 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,49 @@
11
"use client";
22

33
import "@seed-design/stylesheet/progressCircle.css";
4+
// TODO: we have to ensure load order between toggleButton.css and progressCircle.css. should we bundle them together?
45
import "@seed-design/stylesheet/toggleButton.css";
56

6-
import { Slot } from "@radix-ui/react-slot";
7-
import { useToggle, type UseToggleProps } from "@seed-design/react-toggle";
8-
import {
9-
toggleButton,
10-
type ToggleButtonVariantProps,
11-
} from "@seed-design/recipe/toggleButton";
12-
import clsx from "clsx";
7+
import { ToggleButton as SeedToggleButton } from "@seed-design/react";
138
import * as React from "react";
14-
import { ProgressCircle } from "./progress-circle";
159

16-
export interface ToggleButtonProps
17-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
18-
UseToggleProps,
19-
ToggleButtonVariantProps {
10+
export interface ToggleButtonProps extends SeedToggleButton.RootProps {
2011
prefixIcon?: React.ReactNode;
2112

2213
suffixIcon?: React.ReactNode;
23-
24-
loading?: boolean;
25-
26-
/**
27-
* @default false
28-
*/
29-
asChild?: boolean;
3014
}
3115

3216
/**
3317
* @see https://v3.seed-design.io/docs/react/components/toggle-button
3418
*/
3519
export const ToggleButton = React.forwardRef<
36-
HTMLButtonElement,
20+
React.ElementRef<typeof SeedToggleButton.Root>,
3721
ToggleButtonProps
3822
>(
3923
(
4024
{
4125
className,
42-
variant = "brandSolid",
43-
size = "small",
44-
children,
26+
loading = false,
4527
prefixIcon,
4628
suffixIcon,
47-
loading = false,
48-
asChild = false,
29+
children,
4930
...otherProps
5031
},
5132
ref,
5233
) => {
53-
const Comp = asChild ? Slot : "button";
54-
const { rootProps, stateProps, restProps } = useToggle(otherProps);
55-
const classNames = toggleButton({ variant, size });
56-
const dataProps = {
57-
...stateProps,
58-
"data-loading": loading ? "" : undefined,
59-
};
60-
6134
return (
62-
<Comp
63-
ref={ref}
64-
className={clsx(classNames.root, className)}
65-
{...dataProps}
66-
{...rootProps}
67-
{...restProps}
68-
>
69-
{prefixIcon && (
70-
<Slot aria-hidden {...dataProps} className={classNames.prefixIcon}>
71-
{prefixIcon}
72-
</Slot>
73-
)}
74-
<span {...dataProps} className={classNames.label}>
75-
{children}
76-
</span>
77-
{suffixIcon && (
78-
<Slot aria-hidden {...dataProps} className={classNames.suffixIcon}>
79-
{suffixIcon}
80-
</Slot>
81-
)}
82-
{loading ? (
83-
<ProgressCircle
84-
{...dataProps}
85-
className={classNames.progressCircle}
86-
/>
87-
) : null}
88-
</Comp>
35+
<SeedToggleButton.Root ref={ref} loading={loading} {...otherProps}>
36+
{prefixIcon && <SeedToggleButton.PrefixIcon svg={prefixIcon} />}
37+
<SeedToggleButton.Label>{children}</SeedToggleButton.Label>
38+
{suffixIcon && <SeedToggleButton.SuffixIcon svg={suffixIcon} />}
39+
{loading ? <SeedToggleButton.ProgressCircle /> : null}
40+
</SeedToggleButton.Root>
8941
);
9042
},
9143
);
9244
ToggleButton.displayName = "ToggleButton";
45+
46+
/**
47+
* This file is generated snippet from the Seed Design.
48+
* You can extend the functionality from this snippet if needed.
49+
*/

packages/react-headless/toggle/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
},
2828
"dependencies": {
2929
"@radix-ui/react-use-controllable-state": "1.0.1",
30+
"@seed-design/react-primitive": "0.0.0",
3031
"@seed-design/dom-utils": "0.0.0-alpha-20241030023710"
3132
},
3233
"devDependencies": {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export {
2+
ToggleRoot as Root,
3+
type ToggleRootProps as RootProps,
4+
} from "./Toggle";
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive";
2+
import type * as React from "react";
3+
import { forwardRef } from "react";
4+
import { useToggle, type UseToggleProps } from "./useToggle";
5+
import { ToggleProvider } from "./useToggleContext";
6+
7+
export interface ToggleRootProps
8+
extends UseToggleProps,
9+
PrimitiveProps,
10+
React.HTMLAttributes<HTMLButtonElement> {}
11+
12+
export const ToggleRoot = forwardRef<HTMLButtonElement, ToggleRootProps>((props, ref) => {
13+
const api = useToggle(props);
14+
return (
15+
<ToggleProvider value={api}>
16+
<Primitive.button ref={ref} {...api.rootProps} {...api.restProps} />
17+
</ToggleProvider>
18+
);
19+
});
20+
ToggleRoot.displayName = "ToggleRoot";
Lines changed: 6 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,8 @@
1-
import { useControllableState } from "@radix-ui/react-use-controllable-state";
2-
import { useCallback, useMemo } from "react";
1+
export {
2+
ToggleRoot,
3+
type ToggleRootProps,
4+
} from "./Toggle";
35

4-
import { dataAttr, elementProps } from "@seed-design/dom-utils";
6+
export { useToggleContext, type UseToggleContext } from "./useToggleContext";
57

6-
export interface UseToggleStateProps {
7-
pressed?: boolean;
8-
9-
defaultPressed?: boolean;
10-
11-
onPressedChange?: (pressed: boolean) => void;
12-
}
13-
14-
export function useToggleState(props: UseToggleStateProps) {
15-
const [isPressed, setIsPressed] = useControllableState({
16-
prop: props.pressed,
17-
defaultProp: props.defaultPressed,
18-
onChange: props.onPressedChange,
19-
});
20-
21-
const toggle = useCallback(() => {
22-
setIsPressed((prev) => !prev);
23-
}, [setIsPressed]);
24-
25-
return useMemo(
26-
() => ({
27-
isPressed,
28-
toggle,
29-
}),
30-
[isPressed, toggle],
31-
);
32-
}
33-
34-
export interface UseToggleProps extends UseToggleStateProps {
35-
disabled?: boolean;
36-
}
37-
38-
export function useToggle(props: UseToggleProps) {
39-
const { pressed, defaultPressed, disabled, onPressedChange, ...restProps } = props;
40-
const { toggle, isPressed } = useToggleState(props);
41-
42-
const stateProps = {
43-
"data-pressed": dataAttr(isPressed),
44-
"data-disabled": dataAttr(props.disabled),
45-
};
46-
47-
return {
48-
isPressed,
49-
toggle,
50-
51-
restProps,
52-
stateProps,
53-
rootProps: elementProps({
54-
...stateProps,
55-
"aria-pressed": isPressed,
56-
onClick(event) {
57-
if (props.disabled) return;
58-
toggle();
59-
// FIXME: temporal workaround, should be replaced with `mergeProps()`.
60-
(restProps as any).onClick?.(event);
61-
},
62-
}),
63-
};
64-
}
8+
export * as Toggle from "./Toggle.namespace";
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
2+
import { useCallback, useMemo } from "react";
3+
4+
import { buttonProps, dataAttr } from "@seed-design/dom-utils";
5+
6+
export interface UseToggleStateProps {
7+
pressed?: boolean;
8+
9+
defaultPressed?: boolean;
10+
11+
onPressedChange?: (pressed: boolean) => void;
12+
}
13+
14+
export function useToggleState(props: UseToggleStateProps) {
15+
const [isPressed, setIsPressed] = useControllableState({
16+
prop: props.pressed,
17+
defaultProp: props.defaultPressed,
18+
onChange: props.onPressedChange,
19+
});
20+
21+
const toggle = useCallback(() => {
22+
setIsPressed((prev) => !prev);
23+
}, [setIsPressed]);
24+
25+
return useMemo(
26+
() => ({
27+
isPressed,
28+
toggle,
29+
}),
30+
[isPressed, toggle],
31+
);
32+
}
33+
34+
export interface UseToggleProps extends UseToggleStateProps {
35+
disabled?: boolean;
36+
37+
onClick?: React.MouseEventHandler<HTMLButtonElement>;
38+
}
39+
40+
export type UseToggleReturn = ReturnType<typeof useToggle>;
41+
42+
export function useToggle(props: UseToggleProps) {
43+
const { pressed, defaultPressed, disabled, onPressedChange, onClick, ...restProps } = props;
44+
const { toggle, isPressed } = useToggleState(props);
45+
46+
const stateProps = {
47+
"data-pressed": dataAttr(isPressed),
48+
"data-disabled": dataAttr(props.disabled),
49+
};
50+
51+
return {
52+
isPressed,
53+
toggle,
54+
55+
restProps,
56+
stateProps,
57+
// TODO: we have to unify "dataProps" / "stateProps" naming.
58+
dataProps: stateProps,
59+
rootProps: buttonProps({
60+
...stateProps,
61+
"aria-pressed": isPressed,
62+
onClick(event) {
63+
if (props.disabled) return;
64+
toggle();
65+
// FIXME: temporal workaround, should be replaced with `mergeProps()` or getRootProps() pattern.
66+
onClick?.(event);
67+
},
68+
}),
69+
};
70+
}

0 commit comments

Comments
 (0)