mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-07 20:44:05 +09:00
Align Switch with Figma (#921)
Updated Switch styles, added [demo](https://main--638245578b47d4399fba191a.chromatic.com/?path=/story/library-switch--switch). Closes #801
This commit is contained in:
1 parent
d01dbeadce
commit
97fb27c0ce
4 files changed
+101
-90
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,30 @@
|
||||
import { Switch } from "./switch";
|
||||
import { StorySection, StoryGrid } from "./storybook";
|
||||
|
||||
export default {
|
||||
title: "Library/Switch",
|
||||
};
|
||||
|
||||
const Story = () => {
|
||||
return (
|
||||
<>
|
||||
<StorySection title="Enabled">
|
||||
<StoryGrid horizontal>
|
||||
<Switch defaultChecked />
|
||||
<Switch />
|
||||
</StoryGrid>
|
||||
</StorySection>
|
||||
<StorySection title="Disabled">
|
||||
<StoryGrid horizontal>
|
||||
<Switch defaultChecked disabled />
|
||||
<Switch disabled />
|
||||
</StoryGrid>
|
||||
</StorySection>
|
||||
<StorySection title="Focussed (initially)">
|
||||
<Switch autoFocus />
|
||||
</StorySection>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { Story as Switch };
|
||||
@@ -1,96 +1,77 @@
|
||||
import React from "react";
|
||||
import { styled, VariantProps, CSS } from "../stitches.config";
|
||||
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
||||
import { theme } from "../stitches.config";
|
||||
/**
|
||||
* Implementation of the "Switch" component from:
|
||||
* https://www.figma.com/file/sfCE7iLS0k25qCxiifQNLE/%F0%9F%93%9A-Webstudio-Library?node-id=1086%3A3430&t=WVRoENiFP3BSlQa7-0
|
||||
*/
|
||||
|
||||
const StyledThumb = styled(SwitchPrimitive.Thumb, {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
width: 13,
|
||||
height: 13,
|
||||
backgroundColor: "white",
|
||||
import { forwardRef, type ComponentProps, type Ref } from "react";
|
||||
import * as Primitive from "@radix-ui/react-switch";
|
||||
import { type CSS, css, theme } from "../stitches.config";
|
||||
|
||||
const padding = theme.spacing[1];
|
||||
const thumbOffset = `calc(${padding} + ${theme.spacing[2]})`;
|
||||
|
||||
const switchStyles = css({
|
||||
all: "unset", // reset <button>
|
||||
boxSizing: "content-box",
|
||||
width: theme.spacing[11],
|
||||
height: theme.spacing[8],
|
||||
borderRadius: theme.borderRadius.pill,
|
||||
position: "relative",
|
||||
|
||||
// in Figma there's an extra container with a padding
|
||||
// so we need a pseudo element
|
||||
padding: padding,
|
||||
"&:before": {
|
||||
content: "''",
|
||||
position: "absolute",
|
||||
inset: padding,
|
||||
borderRadius: theme.borderRadius.pill,
|
||||
backgroundColor: theme.colors.backgroundNeutralDark,
|
||||
},
|
||||
|
||||
"&[data-state=checked]:not([data-disabled]):before": {
|
||||
backgroundColor: theme.colors.backgroundPrimary,
|
||||
},
|
||||
|
||||
"&[data-disabled]:before": {
|
||||
backgroundColor: theme.colors.foregroundDisabled,
|
||||
},
|
||||
|
||||
"&:focus": {
|
||||
outline: `2px solid ${theme.colors.borderFocus}`,
|
||||
},
|
||||
});
|
||||
|
||||
const thumbStyles = css({
|
||||
width: theme.spacing[6],
|
||||
height: theme.spacing[6],
|
||||
borderRadius: theme.borderRadius.round,
|
||||
boxShadow: "rgba(0, 0, 0, 0.3) 0px 0px 1px, rgba(0, 0, 0, 0.2) 0px 1px 2px;",
|
||||
transition: "transform 100ms cubic-bezier(0.22, 1, 0.36, 1)",
|
||||
transform: `translateX(${theme.spacing[1]})`,
|
||||
willChange: "transform",
|
||||
|
||||
'&[data-state="checked"]': {
|
||||
backgroundColor: theme.colors.foregroundContrastMain,
|
||||
position: "absolute",
|
||||
top: thumbOffset,
|
||||
left: thumbOffset,
|
||||
transition: "transform 100ms",
|
||||
"&[data-state=checked]": {
|
||||
transform: `translateX(${theme.spacing[6]})`,
|
||||
},
|
||||
});
|
||||
|
||||
const StyledSwitch = styled(SwitchPrimitive.Root, {
|
||||
all: "unset",
|
||||
boxSizing: "border-box",
|
||||
userSelect: "none",
|
||||
flexShrink: 0,
|
||||
"&::before": {
|
||||
boxSizing: "border-box",
|
||||
},
|
||||
"&::after": {
|
||||
boxSizing: "border-box",
|
||||
},
|
||||
|
||||
// Reset
|
||||
alignItems: "center",
|
||||
display: "inline-flex",
|
||||
justifyContent: "center",
|
||||
lineHeight: "1",
|
||||
margin: "0",
|
||||
outline: "none",
|
||||
WebkitTapHighlightColor: "rgba(0,0,0,0)",
|
||||
|
||||
backgroundColor: theme.colors.slate6,
|
||||
borderRadius: theme.borderRadius.pill,
|
||||
position: "relative",
|
||||
"&:focus": {
|
||||
boxShadow: `0 0 0 2px ${theme.colors.slate8}`,
|
||||
},
|
||||
|
||||
'&[data-state="checked"]': {
|
||||
backgroundColor: theme.colors.blue9,
|
||||
"&:focus": {
|
||||
boxShadow: `0 0 0 2px ${theme.colors.blue8}`,
|
||||
},
|
||||
},
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
"1": {
|
||||
width: theme.spacing[11],
|
||||
height: theme.spacing[9],
|
||||
},
|
||||
"2": {
|
||||
width: theme.spacing[17],
|
||||
height: theme.spacing[11],
|
||||
[`& ${StyledThumb}`]: {
|
||||
width: 21,
|
||||
height: 21,
|
||||
transform: `translateX(${theme.spacing[2]})`,
|
||||
'&[data-state="checked"]': {
|
||||
transform: `translateX(2${theme.spacing[2]})`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: "1",
|
||||
},
|
||||
});
|
||||
|
||||
type SwitchVariants = VariantProps<typeof StyledSwitch>;
|
||||
type SwitchPrimitiveProps = React.ComponentProps<typeof SwitchPrimitive.Root>;
|
||||
type SwitchProps = SwitchPrimitiveProps & SwitchVariants & { css?: CSS };
|
||||
|
||||
export const Switch = React.forwardRef<
|
||||
React.ElementRef<typeof StyledSwitch>,
|
||||
SwitchProps
|
||||
>((props, forwardedRef) => (
|
||||
<StyledSwitch {...props} ref={forwardedRef}>
|
||||
<StyledThumb />
|
||||
</StyledSwitch>
|
||||
));
|
||||
|
||||
export const Switch = forwardRef(
|
||||
(
|
||||
{
|
||||
className,
|
||||
css,
|
||||
...props
|
||||
}: ComponentProps<typeof Primitive.Root> & { css?: CSS },
|
||||
ref: Ref<HTMLButtonElement>
|
||||
) => (
|
||||
<Primitive.Root
|
||||
className={switchStyles({ className, css })}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<Primitive.Thumb className={thumbStyles()} />
|
||||
</Primitive.Root>
|
||||
)
|
||||
);
|
||||
Switch.displayName = "Switch";
|
||||
@@ -15,6 +15,7 @@ export * from "./components/icon-button"; // mostly aligned, but needs a demo an
|
||||
export * from "./components/dialog";
|
||||
export * from "./components/floating-panel-popover";
|
||||
export { MenuItemButton } from "./components/menu";
|
||||
export * from "./components/switch";
|
||||
|
||||
// Not aligned
|
||||
|
||||
@@ -43,7 +44,6 @@ export {
|
||||
} from "./components/enhanced-tooltip";
|
||||
export { Link } from "./components/link";
|
||||
export { Panel } from "./components/panel";
|
||||
export * from "./components/switch";
|
||||
export * from "./components/slider";
|
||||
export * from "./components/radio";
|
||||
export * from "./components/checkbox";
|
||||
|
||||
Reference in new issue
Block a user