Skip to content

Commit

Permalink
Aded new togglegroup component.
Browse files Browse the repository at this point in the history
  • Loading branch information
JeroenReumkens committed Oct 24, 2022
1 parent 9f1341e commit 1ccecbc
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 64 deletions.
20 changes: 20 additions & 0 deletions apps/storybook/stories/ToggleGroup.stories.tsx
@@ -0,0 +1,20 @@
import { ComponentMeta } from "@storybook/react";

import { ToggleGroup } from "@calcom/ui/v2/core/form/ToggleGroup";

export default {
title: "Toggle Group",
component: ToggleGroup,
} as ComponentMeta<typeof ToggleGroup>;

export const Default = () => {
return (
<ToggleGroup
defaultValue="12"
options={[
{ value: "12", label: "12h" },
{ value: "24", label: "24h" },
]}
/>
);
};
19 changes: 14 additions & 5 deletions apps/web/components/booking/AvailableTimes.tsx
Expand Up @@ -7,6 +7,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { nameOfDay } from "@calcom/lib/weekday";
import type { Slot } from "@calcom/trpc/server/routers/viewer/slots";
import { SkeletonContainer, SkeletonText } from "@calcom/ui";
import { ToggleGroup } from "@calcom/ui/v2/core/form/ToggleGroup";

import classNames from "@lib/classNames";
import { timeZone } from "@lib/clock";
Expand Down Expand Up @@ -48,14 +49,22 @@ const AvailableTimes: FC<AvailableTimesProps> = ({

return (
<div className="dark:bg-darkgray-100 mt-8 flex h-full w-full flex-col px-4 text-center sm:mt-0 sm:p-5 md:-mb-5 md:min-w-[200px] lg:min-w-[300px]">
<div className="mb-4 text-left text-base">
<span className="text-bookingdarker dark:text-darkgray-800 mb-8 w-1/2 break-words font-semibold text-gray-900">
{nameOfDay(i18n.language, Number(date.format("d")))}
<div className="mb-6 flex items-center text-left text-base">
<span className="text-bookingdarker dark:text-darkgray-800 font-semibold text-gray-900">
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
</span>
<span className="text-bookinglight font-medium">
{date.format(", D ")}
{date.toDate().toLocaleString(i18n.language, { month: "long" })}
, {date.toDate().toLocaleString(i18n.language, { month: "long", day: "numeric" })}
</span>
<div className="ml-auto">
<ToggleGroup
defaultValue="12"
options={[
{ value: "12", label: "12h" },
{ value: "24", label: "24h" },
]}
/>
</div>
</div>
<div className="-mb-5 grid flex-grow grid-cols-1 gap-x-2 overflow-y-auto sm:block md:h-[364px]">
{slots.length > 0 &&
Expand Down
28 changes: 2 additions & 26 deletions apps/web/components/booking/TimeOptions.tsx
@@ -1,25 +1,19 @@
import { FC, useEffect, useState } from "react";

import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Switch } from "@calcom/ui/v2";
import TimezoneSelect, { ITimezoneOption } from "@calcom/ui/v2/core/TimezoneSelect";

import { is24h, timeZone } from "../../lib/clock";
import { timeZone } from "../../lib/clock";

type Props = {
onSelectTimeZone: (selectedTimeZone: string) => void;
onToggle24hClock: (is24hClock: boolean) => void;
timeFormat: string;
hideTimeFormatToggle?: boolean;
};

const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat, hideTimeFormatToggle }) => {
const TimeOptions: FC<Props> = ({ onSelectTimeZone }) => {
const [selectedTimeZone, setSelectedTimeZone] = useState("");
const [is24hClock, setIs24hClock] = useState(timeFormat === "HH:mm" && true);
const { t } = useLocale();

useEffect(() => {
setIs24hClock(is24h());
setSelectedTimeZone(timeZone());
}, []);

Expand All @@ -29,28 +23,10 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat
}
}, [selectedTimeZone, onSelectTimeZone]);

const handle24hClockToggle = (is24hClock: boolean) => {
setIs24hClock(is24hClock);
onToggle24hClock(is24h(is24hClock));
};

return selectedTimeZone !== "" ? (
<div className="dark:border-darkgray-300 dark:bg-darkgray-200 rounded-sm border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm">
<div className="mb-4 flex">
<div className="text-sm font-medium text-gray-600 dark:text-white">{t("time_options")}</div>
{!hideTimeFormatToggle && (
<div className="ml-auto flex items-center">
<label className="ltl:mr-3 mr-2 align-text-top text-sm font-medium text-neutral-700 ltr:ml-3 rtl:mr-3 dark:text-white">
{t("am_pm")}
</label>
<Switch
name="24hClock"
label={t("24_h")}
defaultChecked={is24hClock}
onCheckedChange={handle24hClockToggle}
/>
</div>
)}
</div>
<TimezoneSelect
id="timeZone"
Expand Down
36 changes: 3 additions & 33 deletions apps/web/components/booking/pages/AvailabilityPage.tsx
Expand Up @@ -213,35 +213,20 @@ const SlotPicker = ({
};

function TimezoneDropdown({
onChangeTimeFormat,
onChangeTimeZone,
timeZone,
timeFormat,
hideTimeFormatToggle,
}: {
onChangeTimeFormat: (newTimeFormat: string) => void;
onChangeTimeZone: (newTimeZone: string) => void;
timeZone?: string;
timeFormat: string;
hideTimeFormatToggle?: boolean;
}) {
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);

useEffect(() => {
handleToggle24hClock(!!getIs24hClockFromLocalStorage());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const handleSelectTimeZone = (newTimeZone: string) => {
onChangeTimeZone(newTimeZone);
localStorageTimeZone(newTimeZone);
setIsTimeOptionsOpen(false);
};

const handleToggle24hClock = (is24hClock: boolean) => {
onChangeTimeFormat(is24hClock ? "HH:mm" : "h:mma");
};

return (
<Popover.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
<Popover.Trigger className="min-w-32 dark:text-darkgray-600 radix-state-open:bg-gray-200 dark:radix-state-open:bg-darkgray-200 group relative mb-2 -ml-2 inline-block rounded-md px-2 py-2 text-left text-gray-600">
Expand All @@ -260,12 +245,7 @@ function TimezoneDropdown({
hideWhenDetached
align="start"
className="animate-fade-in-up absolute left-0 top-2 w-80 max-w-[calc(100vw_-_1.5rem)]">
<TimeOptions
onSelectTimeZone={handleSelectTimeZone}
onToggle24hClock={handleToggle24hClock}
timeFormat={timeFormat}
hideTimeFormatToggle={hideTimeFormatToggle}
/>
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
</Popover.Content>
</Popover.Portal>
</Popover.Root>
Expand Down Expand Up @@ -352,18 +332,8 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
const userList = eventType.users ? eventType.users.map((user) => user.username).filter(notEmpty) : [];

const timezoneDropdown = useMemo(
() => (
<TimezoneDropdown
timeFormat={timeFormat}
onChangeTimeFormat={setTimeFormat}
timeZone={timeZone}
onChangeTimeZone={setTimeZone}
// Currently we don't allow the user to change the timeformat when they're logged in,
// the only way to change it is if they go to their profile.
hideTimeFormatToggle={!!timeFormatFromProfile}
/>
),
[timeZone, timeFormat, timeFormatFromProfile]
() => <TimezoneDropdown timeZone={timeZone} onChangeTimeZone={setTimeZone} />,
[timeZone]
);
const stripeAppData = getStripeAppData(eventType);
const rainbowAppData = getEventTypeAppData(eventType, "rainbow") || {};
Expand Down
53 changes: 53 additions & 0 deletions packages/ui/v2/core/form/ToggleGroup.tsx
@@ -0,0 +1,53 @@
import * as RadixToggleGroup from "@radix-ui/react-toggle-group";
import { useEffect, useState } from "react";

import { classNames } from "@calcom/lib";

export const ToggleGroupItem = () => <div>hi</div>;

interface ToggleGroupProps extends Omit<RadixToggleGroup.ToggleGroupSingleProps, "type"> {
options: { value: string; label: string }[];
}

export const ToggleGroup = ({ options, onValueChange, ...props }: ToggleGroupProps) => {
const [value, setValue] = useState<string | undefined>(props.defaultValue);
const [activeToggleElement, setActiveToggleElement] = useState<null | HTMLButtonElement>(null);

useEffect(() => {
if (value && onValueChange) onValueChange(value);
}, [value, onValueChange]);

return (
<>
<RadixToggleGroup.Root
type="single"
{...props}
onValueChange={setValue}
className={classNames(
"dark:border-darkgray-200 relative inline-flex rounded-md border border-gray-200 p-1",
props.className
)}>
{/* Active toggle. It's a separate element so we can animate it nicely. */}
<span
aria-hidden
className="dark:bg-darkgray-200 absolute top-[4px] bottom-[4px] left-0 z-[0] rounded-[4px] bg-gray-200 transition-all"
style={{ left: activeToggleElement?.offsetLeft, width: activeToggleElement?.offsetWidth }}
/>
{options.map((option) => (
<RadixToggleGroup.Item
key={option.value}
value={option.value}
className="relative rounded-[4px] px-3 py-1 text-sm dark:text-neutral-200 [&[aria-checked='false']]:hover:font-medium"
ref={(node) => {
if (node && value === option.value) {
setActiveToggleElement(node);
}
return node;
}}>
{option.label}
</RadixToggleGroup.Item>
))}
</RadixToggleGroup.Root>
</>
);
};

0 comments on commit 1ccecbc

Please sign in to comment.