first commit
This commit is contained in:
121
calcom/packages/ui/components/form/switch/SettingsToggle.tsx
Normal file
121
calcom/packages/ui/components/form/switch/SettingsToggle.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
|
||||
import { Label } from "..";
|
||||
import Switch from "./Switch";
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode;
|
||||
title: string;
|
||||
description?: string | React.ReactNode;
|
||||
checked: boolean;
|
||||
disabled?: boolean;
|
||||
LockedIcon?: React.ReactNode;
|
||||
Badge?: React.ReactNode;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
"data-testid"?: string;
|
||||
tooltip?: string;
|
||||
toggleSwitchAtTheEnd?: boolean;
|
||||
childrenClassName?: string;
|
||||
switchContainerClassName?: string;
|
||||
labelClassName?: string;
|
||||
descriptionClassName?: string;
|
||||
};
|
||||
|
||||
function SettingsToggle({
|
||||
checked,
|
||||
onCheckedChange,
|
||||
description,
|
||||
LockedIcon,
|
||||
Badge,
|
||||
title,
|
||||
children,
|
||||
disabled,
|
||||
tooltip,
|
||||
toggleSwitchAtTheEnd = false,
|
||||
childrenClassName,
|
||||
switchContainerClassName,
|
||||
labelClassName,
|
||||
descriptionClassName,
|
||||
...rest
|
||||
}: Props) {
|
||||
const [animateRef] = useAutoAnimate<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-full flex-col space-y-4 lg:flex-row lg:space-x-4 lg:space-y-0">
|
||||
<fieldset className="block w-full flex-col sm:flex">
|
||||
{toggleSwitchAtTheEnd ? (
|
||||
<div
|
||||
className={classNames(
|
||||
"border-subtle flex justify-between space-x-3 rounded-lg border px-4 py-6 sm:px-6",
|
||||
checked && children && "rounded-b-none",
|
||||
switchContainerClassName
|
||||
)}>
|
||||
<div>
|
||||
<div className="flex items-center gap-x-2" data-testid={`${rest["data-testid"]}-title`}>
|
||||
<Label
|
||||
className={classNames("mt-0.5 text-base font-semibold leading-none", labelClassName)}
|
||||
htmlFor="">
|
||||
{title}
|
||||
{LockedIcon}
|
||||
</Label>
|
||||
{Badge && <div className="mb-2">{Badge}</div>}
|
||||
</div>
|
||||
{description && (
|
||||
<p
|
||||
className={classNames(
|
||||
"text-default -mt-1.5 text-sm leading-normal",
|
||||
descriptionClassName
|
||||
)}
|
||||
data-testid={`${rest["data-testid"]}-description`}>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="my-auto h-full">
|
||||
<Switch
|
||||
data-testid={rest["data-testid"]}
|
||||
fitToHeight={true}
|
||||
checked={checked}
|
||||
onCheckedChange={onCheckedChange}
|
||||
disabled={disabled}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex space-x-3">
|
||||
<Switch
|
||||
data-testid={rest["data-testid"]}
|
||||
fitToHeight={true}
|
||||
checked={checked}
|
||||
onCheckedChange={onCheckedChange}
|
||||
disabled={disabled}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<Label
|
||||
className={classNames("text-emphasis text-sm font-semibold leading-none", labelClassName)}>
|
||||
{title}
|
||||
{LockedIcon}
|
||||
</Label>
|
||||
{description && <p className="text-default -mt-1.5 text-sm leading-normal">{description}</p>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{children && (
|
||||
<div className={classNames("lg:ml-14", childrenClassName)} ref={animateRef}>
|
||||
{checked && <div className={classNames(!toggleSwitchAtTheEnd && "mt-4")}>{children}</div>}
|
||||
</div>
|
||||
)}
|
||||
</fieldset>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SettingsToggle;
|
||||
82
calcom/packages/ui/components/form/switch/Switch.tsx
Normal file
82
calcom/packages/ui/components/form/switch/Switch.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { useId } from "@radix-ui/react-id";
|
||||
import * as Label from "@radix-ui/react-label";
|
||||
import * as PrimitiveSwitch from "@radix-ui/react-switch";
|
||||
import type { ReactNode } from "react";
|
||||
import React from "react";
|
||||
|
||||
import cx from "@calcom/lib/classNames";
|
||||
|
||||
import { Tooltip } from "../../tooltip";
|
||||
|
||||
const Wrapper = ({ children, tooltip }: { tooltip?: string; children: React.ReactNode }) => {
|
||||
if (!tooltip) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
return <Tooltip content={tooltip}>{children}</Tooltip>;
|
||||
};
|
||||
const Switch = (
|
||||
props: React.ComponentProps<typeof PrimitiveSwitch.Root> & {
|
||||
label?: string | ReactNode;
|
||||
fitToHeight?: boolean;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
small?: boolean;
|
||||
labelOnLeading?: boolean;
|
||||
classNames?: {
|
||||
container?: string;
|
||||
thumb?: string;
|
||||
};
|
||||
LockedIcon?: React.ReactNode;
|
||||
}
|
||||
) => {
|
||||
const { label, fitToHeight, classNames, small, labelOnLeading, LockedIcon, ...primitiveProps } = props;
|
||||
const id = useId();
|
||||
const isChecked = props.checked || props.defaultChecked;
|
||||
return (
|
||||
<Wrapper tooltip={props.tooltip}>
|
||||
<div
|
||||
className={cx(
|
||||
"flex h-auto w-auto flex-row items-center",
|
||||
fitToHeight && "h-fit",
|
||||
labelOnLeading && "flex-row-reverse",
|
||||
classNames?.container
|
||||
)}>
|
||||
{LockedIcon && <div className="mr-2">{LockedIcon}</div>}
|
||||
<PrimitiveSwitch.Root
|
||||
className={cx(
|
||||
isChecked ? "bg-brand-default dark:bg-brand-emphasis" : "bg-emphasis",
|
||||
primitiveProps.disabled && "cursor-not-allowed",
|
||||
small ? "h-4 w-[27px]" : "h-5 w-[34px]",
|
||||
"focus:ring-brand-default rounded-full shadow-none transition focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-1",
|
||||
props.className
|
||||
)}
|
||||
{...primitiveProps}>
|
||||
<PrimitiveSwitch.Thumb
|
||||
id={id}
|
||||
className={cx(
|
||||
small
|
||||
? "h-[10px] w-[10px] ltr:translate-x-[2px] rtl:-translate-x-[2px] ltr:[&[data-state='checked']]:translate-x-[13px] rtl:[&[data-state='checked']]:-translate-x-[13px]"
|
||||
: "h-[14px] w-[14px] ltr:translate-x-[4px] rtl:-translate-x-[4px] ltr:[&[data-state='checked']]:translate-x-[17px] rtl:[&[data-state='checked']]:-translate-x-[17px]",
|
||||
"block rounded-full transition will-change-transform",
|
||||
isChecked ? "bg-brand-accent shadow-inner" : "bg-default",
|
||||
classNames?.thumb
|
||||
)}
|
||||
/>
|
||||
</PrimitiveSwitch.Root>
|
||||
{label && (
|
||||
<Label.Root
|
||||
htmlFor={id}
|
||||
className={cx(
|
||||
"text-emphasis ms-2 align-text-top text-sm font-medium",
|
||||
primitiveProps.disabled ? "cursor-not-allowed opacity-25" : "cursor-pointer",
|
||||
labelOnLeading && "flex-1"
|
||||
)}>
|
||||
{label}
|
||||
</Label.Root>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Switch;
|
||||
2
calcom/packages/ui/components/form/switch/index.ts
Normal file
2
calcom/packages/ui/components/form/switch/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as SettingsToggle } from "./SettingsToggle";
|
||||
export { default as Switch } from "./Switch";
|
||||
97
calcom/packages/ui/components/form/switch/switch.stories.mdx
Normal file
97
calcom/packages/ui/components/form/switch/switch.stories.mdx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
||||
import { Canvas, Meta, Story, ArgsTable } from "@storybook/addon-docs";
|
||||
|
||||
import {
|
||||
Examples,
|
||||
Example,
|
||||
Title,
|
||||
VariantsTable,
|
||||
CustomArgsTable,
|
||||
VariantRow,
|
||||
} from "@calcom/storybook/components";
|
||||
|
||||
import Switch from "./Switch";
|
||||
|
||||
<Meta title="UI/Form/Switch" component={Switch} />
|
||||
|
||||
<Title title="Switch" suffix="Brief" subtitle="Version 1.0 — Last Update: 16 Aug 2023" />
|
||||
|
||||
## Definition
|
||||
|
||||
Switch is a customizable toggle switch component that allows users to change between two states.
|
||||
|
||||
## Structure
|
||||
|
||||
The `Switch` component can be used to create toggle switches for various purposes. It provides options for adding labels, icons, and tooltips.
|
||||
|
||||
<CustomArgsTable of={Switch} />
|
||||
|
||||
<Examples title="States">
|
||||
<Example title="Default">
|
||||
<Switch />
|
||||
</Example>
|
||||
<Example title="Disabled">
|
||||
<Switch disabled />
|
||||
</Example>
|
||||
<Example title="Checked">
|
||||
<Switch checked />
|
||||
</Example>
|
||||
</Examples>
|
||||
|
||||
<Examples title="Labels">
|
||||
<Example title="With Label and labelOnLeading">
|
||||
<Switch label="Enable Feature" labelOnLeading />
|
||||
</Example>
|
||||
<Example title="With Label">
|
||||
<Switch label="Enable Feature" />
|
||||
</Example>
|
||||
</Examples>
|
||||
|
||||
<Examples title="Hover">
|
||||
<Example title="With Tooltip (Hover me)">
|
||||
<TooltipProvider>
|
||||
<Switch tooltip="Toggle to enable/disable the feature" />
|
||||
</TooltipProvider>
|
||||
</Example>
|
||||
<Example title="Without Tooltip (Hover me)">
|
||||
<TooltipProvider>
|
||||
<Switch />
|
||||
</TooltipProvider>
|
||||
</Example>
|
||||
</Examples>
|
||||
|
||||
<Title offset title="Switch" suffix="Variants" />
|
||||
|
||||
<Canvas>
|
||||
<Story
|
||||
name="Switch"
|
||||
args={{
|
||||
label: "Enable Feature",
|
||||
tooltip: "Toggle to enable/disable the feature",
|
||||
checked: false,
|
||||
disabled: false,
|
||||
fitToHeight: false,
|
||||
labelOnLeading: false,
|
||||
}}
|
||||
argTypes={{
|
||||
label: { control: { type: "text" } },
|
||||
tooltip: { control: { type: "text" } },
|
||||
checked: { control: { type: "boolean" } },
|
||||
disabled: { control: { type: "boolean" } },
|
||||
fitToHeight: { control: { type: "boolean" } },
|
||||
labelOnLeading: { control: { type: "boolean" } },
|
||||
}}>
|
||||
{(props) => (
|
||||
<TooltipProvider>
|
||||
<VariantsTable titles={["Default"]} columnMinWidth={150}>
|
||||
<VariantRow>
|
||||
<Switch
|
||||
{...props}
|
||||
onCheckedChange={(checkedValue) => console.log("Switch value:", checkedValue)}
|
||||
/>
|
||||
</VariantRow>
|
||||
</VariantsTable>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
</Story>
|
||||
</Canvas>
|
||||
Reference in New Issue
Block a user