diff --git a/components/team/MemberListItem.tsx b/components/team/MemberListItem.tsx index 65ab55fe..d4e7faa0 100644 --- a/components/team/MemberListItem.tsx +++ b/components/team/MemberListItem.tsx @@ -1,5 +1,5 @@ import { DotsHorizontalIcon, UserRemoveIcon } from "@heroicons/react/outline"; -import Dropdown from "../ui/Dropdown"; +import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/Dropdown"; import { useState } from "react"; import { Dialog, DialogTrigger } from "@components/Dialog"; import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent"; @@ -55,14 +55,12 @@ export default function MemberListItem(props: { Owner )} - - - + + diff --git a/components/team/TeamListItem.tsx b/components/team/TeamListItem.tsx index 8338e73a..10e10010 100644 --- a/components/team/TeamListItem.tsx +++ b/components/team/TeamListItem.tsx @@ -5,7 +5,7 @@ import { PencilAltIcon, ExternalLinkIcon, } from "@heroicons/react/outline"; -import Dropdown from "../ui/Dropdown"; +import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/Dropdown"; import { useState } from "react"; import { Tooltip } from "@components/Tooltip"; import Link from "next/link"; @@ -105,16 +105,12 @@ export default function TeamListItem(props: { StartIcon={LinkIcon} type="button"> - - - + + )} diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index c980f7a4..4face4aa 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -1,6 +1,6 @@ import classNames from "@lib/classNames"; import Link, { LinkProps } from "next/link"; -import React from "react"; +import React, { forwardRef } from "react"; type SVGComponent = React.FunctionComponent>; @@ -18,7 +18,10 @@ export type ButtonProps = { | (JSX.IntrinsicElements["button"] & { href?: never }) ); -export const Button = function Button(props: ButtonProps) { +export const Button = forwardRef(function Button( + props: ButtonProps, + forwardedRef +) { const { loading = false, color = "primary", @@ -41,6 +44,7 @@ export const Button = function Button(props: ButtonProps) { { ...passThroughProps, disabled, + ref: forwardedRef, className: classNames( // base styles independent what type of button it is "inline-flex items-center", @@ -117,6 +121,6 @@ export const Button = function Button(props: ButtonProps) { ) : ( element ); -}; +}); export default Button; diff --git a/components/ui/Dropdown.tsx b/components/ui/Dropdown.tsx index 777c161e..2987ecc4 100644 --- a/components/ui/Dropdown.tsx +++ b/components/ui/Dropdown.tsx @@ -1,20 +1,92 @@ -import { useEffect, useState } from "react"; +import { CheckCircleIcon } from "@heroicons/react/outline"; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { ComponentProps, forwardRef } from "react"; -export default function Dropdown(props) { - const [open, setOpen] = useState(false); +export const Dropdown = DropdownMenuPrimitive.Root; - useEffect(() => { - document.addEventListener("keyup", (e) => { - if (e.key === "Escape") { - setOpen(false); +type DropdownMenuTriggerProps = ComponentProps; +export const DropdownMenuTrigger = forwardRef( + ({ className = "", ...props }, forwardedRef) => ( + + ) +); +DropdownMenuTrigger.displayName = "DropdownMenuTrigger"; - return ( -
setOpen(!open)} {...props}> - {props.children[0]} - {open && props.children[1]} -
- ); -} +type DropdownMenuContentProps = ComponentProps; +export const DropdownMenuContent = forwardRef( + ({ children, ...props }, forwardedRef) => { + return ( + + {children} + + + ); + } +); +DropdownMenuContent.displayName = "DropdownMenuContent"; + +type DropdownMenuLabelProps = ComponentProps; +export const DropdownMenuLabel = (props: DropdownMenuLabelProps) => ( + +); + +type DropdownMenuItemProps = ComponentProps; +export const DropdownMenuItem = forwardRef( + ({ className = "", ...props }, forwardedRef) => ( + + ) +); +DropdownMenuItem.displayName = "DropdownMenuItem"; + +export const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +type DropdownMenuCheckboxItemProps = ComponentProps; +export const DropdownMenuCheckboxItem = forwardRef( + ({ children, ...props }, forwardedRef) => { + return ( + + {children} + + + + + ); + } +); +DropdownMenuCheckboxItem.displayName = "DropdownMenuCheckboxItem"; + +export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +type DropdownMenuRadioItemProps = ComponentProps; +export const DropdownMenuRadioItem = forwardRef( + ({ children, ...props }, forwardedRef) => { + return ( + + {children} + + + + + ); + } +); +DropdownMenuRadioItem.displayName = "DropdownMenuRadioItem"; + +export const DropdownMenuSeparator = DropdownMenuPrimitive.Separator; + +export default Dropdown; diff --git a/package.json b/package.json index cded4442..7d862295 100644 --- a/package.json +++ b/package.json @@ -26,12 +26,12 @@ "@heroicons/react": "^1.0.4", "@jitsu/sdk-js": "^2.2.4", "@prisma/client": "^2.30.2", - "@radix-ui/react-avatar": "^0.0.15", - "@radix-ui/react-collapsible": "^0.0.17", - "@radix-ui/react-dialog": "^0.0.20", - "@radix-ui/react-dropdown-menu": "^0.0.23", - "@radix-ui/react-slider": "^0.0.17", - "@radix-ui/react-switch": "^0.0.15", + "@radix-ui/react-avatar": "^0.1.0", + "@radix-ui/react-collapsible": "^0.1.0", + "@radix-ui/react-dialog": "^0.1.0", + "@radix-ui/react-dropdown-menu": "^0.1.0", + "@radix-ui/react-slider": "^0.1.0", + "@radix-ui/react-switch": "^0.1.0", "@radix-ui/react-tooltip": "^0.1.0", "@tailwindcss/forms": "^0.3.3", "async": "^3.2.1", diff --git a/pages/event-types/index.tsx b/pages/event-types/index.tsx index 6630b867..cda10d72 100644 --- a/pages/event-types/index.tsx +++ b/pages/event-types/index.tsx @@ -1,6 +1,21 @@ import { Dialog, DialogClose, DialogContent } from "@components/Dialog"; +import EventTypeDescription from "@components/eventtype/EventTypeDescription"; +import Shell from "@components/Shell"; import { Tooltip } from "@components/Tooltip"; +import { Alert } from "@components/ui/Alert"; +import Avatar from "@components/ui/Avatar"; +import AvatarGroup from "@components/ui/AvatarGroup"; +import Badge from "@components/ui/Badge"; import { Button } from "@components/ui/Button"; +import Dropdown, { + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@components/ui/Dropdown"; +import * as RadioArea from "@components/ui/form/radio-area"; +import UserCalendarIllustration from "@components/ui/svg/UserCalendarIllustration"; // TODO: replace headlessui with radix-ui import { Menu, Transition } from "@headlessui/react"; import { @@ -11,40 +26,36 @@ import { PlusIcon, UsersIcon, } from "@heroicons/react/solid"; +import { asStringOrNull } from "@lib/asStringOrNull"; +import { getSession } from "@lib/auth"; import classNames from "@lib/classNames"; -import { getSession } from "next-auth/client"; +import { HttpError } from "@lib/core/http/error"; +import { ONBOARDING_INTRODUCED_AT } from "@lib/getting-started"; +import { useToggleQuery } from "@lib/hooks/useToggleQuery"; +import createEventType from "@lib/mutations/event-types/create-event-type"; +import showToast from "@lib/notification"; +import prisma from "@lib/prisma"; +import { inferSSRProps } from "@lib/types/inferSSRProps"; +import { SchedulingType } from "@prisma/client"; +import dayjs from "dayjs"; import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; import React, { Fragment, useRef } from "react"; -import dayjs from "dayjs"; -import Shell from "@components/Shell"; -import prisma from "@lib/prisma"; -import { EventType, SchedulingType } from "@prisma/client"; -import showToast from "@lib/notification"; -import Avatar from "@components/ui/Avatar"; -import UserCalendarIllustration from "@components/ui/svg/UserCalendarIllustration"; -import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; -import EventTypeDescription from "@components/eventtype/EventTypeDescription"; -import * as RadioArea from "@components/ui/form/radio-area"; -import { ONBOARDING_INTRODUCED_AT } from "@lib/getting-started"; -import { inferSSRProps } from "@lib/types/inferSSRProps"; -import { Alert } from "@components/ui/Alert"; -import { useToggleQuery } from "@lib/hooks/useToggleQuery"; import { useMutation } from "react-query"; -import createEventType from "@lib/mutations/event-types/create-event-type"; -import { HttpError } from "@lib/core/http/error"; -import { asStringOrNull } from "@lib/asStringOrNull"; -import AvatarGroup from "@components/ui/AvatarGroup"; -import Badge from "@components/ui/Badge"; -const EventTypesPage = (props: inferSSRProps) => { +type PageProps = inferSSRProps; +type EventType = PageProps["eventTypes"][number]; +type Profile = PageProps["profiles"][number]; +type MembershipCount = EventType["metadata"]["membershipCount"]; + +const EventTypesPage = (props: PageProps) => { const CreateFirstEventTypeView = () => (
-
+

Create your first event type

-

+

Event types enable you to share links that show available times on your calendar and allow people to make bookings with you.

@@ -53,11 +64,22 @@ const EventTypesPage = (props: inferSSRProps) => {
); - const EventTypeListHeading = ({ profile, membershipCount }) => ( + const EventTypeListHeading = ({ + profile, + membershipCount, + }: { + profile: Profile; + membershipCount: MembershipCount; + }) => (
- +
@@ -65,11 +87,11 @@ const EventTypesPage = (props: inferSSRProps) => { {profile.name} {membershipCount && ( - + - + {membershipCount} @@ -77,7 +99,7 @@ const EventTypesPage = (props: inferSSRProps) => { )} {typeof window !== "undefined" && ( - + {`${window.location.host}/${profile.slug}`} )} @@ -90,11 +112,11 @@ const EventTypesPage = (props: inferSSRProps) => { types, profile, }: { - profile; + profile: PageProps["profiles"][number]; readOnly: boolean; types: EventType[]; }) => ( -
+
    {types.map((type) => (
  • ) => { )} data-disabled={type.$disabled ? 1 : 0}>
    -
    +
    - +
    - {type.title} + {type.title} {type.hidden && ( Hidden @@ -124,8 +146,8 @@ const EventTypesPage = (props: inferSSRProps) => { -
    -
    +
    +
    {type.users.length > 1 && ( ) => { href={`/${profile.slug}/${type.slug}`} target="_blank" rel="noreferrer" - className="group cursor-pointer text-neutral-400 p-2 border border-transparent hover:border-gray-200"> - + className="p-2 border border-transparent cursor-pointer group text-neutral-400 hover:border-gray-200"> + @@ -154,21 +176,21 @@ const EventTypesPage = (props: inferSSRProps) => { `${window.location.origin}/${profile.slug}/${type.slug}` ); }} - className="group text-neutral-400 p-2 border border-transparent hover:border-gray-200"> - + className="p-2 border border-transparent group text-neutral-400 hover:border-gray-200"> +
    -
    +
    {({ open }) => ( <>
    - + Open options -
    @@ -183,7 +205,7 @@ const EventTypesPage = (props: inferSSRProps) => { leaveTo="transform opacity-0 scale-95"> + className="absolute right-0 w-56 mt-2 origin-top-right bg-white divide-y rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none divide-neutral-100">
    {({ active }) => ( @@ -196,7 +218,7 @@ const EventTypesPage = (props: inferSSRProps) => { "group flex items-center px-4 py-2 text-sm font-medium" )}>