<Button>
-component (#480)
-`<Button/>` component - Uses `next/link` + `<a/>` if you supply a `href` otherwise `<button/>` - Add UI sandbox - Change the `event-types/index` to use a query param for deciding if modal is open or not
This commit is contained in:
parent
ce64080160
commit
a77a15056a
7 changed files with 251 additions and 83 deletions
|
@ -1,32 +1,37 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
|
|
||||||
export function Dialog({ children, ...props }) {
|
type DialogProps = React.ComponentProps<typeof DialogPrimitive["Root"]>;
|
||||||
|
export function Dialog(props: DialogProps) {
|
||||||
|
const { children, ...other } = props;
|
||||||
return (
|
return (
|
||||||
<DialogPrimitive.Root {...props}>
|
<DialogPrimitive.Root {...other}>
|
||||||
<DialogPrimitive.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<DialogPrimitive.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
{children}
|
{children}
|
||||||
</DialogPrimitive.Root>
|
</DialogPrimitive.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
type DialogContentProps = React.ComponentProps<typeof DialogPrimitive["Content"]>;
|
||||||
|
|
||||||
export const DialogContent = React.forwardRef(({ children, ...props }, forwardedRef) => (
|
export const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps>(
|
||||||
|
({ children, ...props }, forwardedRef) => (
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
{...props}
|
{...props}
|
||||||
className="fixed bg-white min-w-[360px] rounded top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-left overflow-hidden shadow-xl sm:align-middle sm:max-w-lg sm:w-full p-6"
|
className="min-w-[360px] fixed left-1/2 top-1/2 p-6 text-left bg-white rounded shadow-xl overflow-hidden -translate-x-1/2 -translate-y-1/2 sm:align-middle sm:w-full sm:max-w-lg"
|
||||||
ref={forwardedRef}>
|
ref={forwardedRef}>
|
||||||
{children}
|
{children}
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
export function DialogHeader({ title, subtitle }: { title: string; subtitle: string }) {
|
export function DialogHeader({ title, subtitle }: { title: string; subtitle: string }) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h3 className="text-lg leading-6 font-bold text-gray-900" id="modal-title">
|
<h3 className="text-gray-900 text-lg font-bold leading-6" id="modal-title">
|
||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-400">{subtitle}</p>
|
<p className="text-gray-400 text-sm">{subtitle}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,86 @@
|
||||||
export default function Button(props) {
|
import classNames from "@lib/classNames";
|
||||||
return (
|
import Link, { LinkProps } from "next/link";
|
||||||
<button type="submit" className="btn btn-primary dark:btn-white">
|
import React from "react";
|
||||||
{!props.loading && props.children}
|
|
||||||
{props.loading && (
|
type HTMLAnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||||
|
type HTMLButtonProps = React.ButtonHTMLAttributes<HTMLButtonProps>;
|
||||||
|
|
||||||
|
type SVGComponent = React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
|
||||||
|
|
||||||
|
export type ButtonProps = {
|
||||||
|
color?: "primary" | "secondary" | "minimal";
|
||||||
|
size?: "base" | "sm" | "lg";
|
||||||
|
loading?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
onClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||||
|
StartIcon?: SVGComponent;
|
||||||
|
EndIcon?: SVGComponent;
|
||||||
|
} & ((Omit<HTMLAnchorProps, "href"> & { href: LinkProps["href"] }) | (HTMLButtonProps & { href?: never }));
|
||||||
|
|
||||||
|
export const Button = function Button(props: ButtonProps) {
|
||||||
|
const {
|
||||||
|
loading = false,
|
||||||
|
color = "primary",
|
||||||
|
size = "base",
|
||||||
|
StartIcon,
|
||||||
|
EndIcon,
|
||||||
|
// attributes propagated from `HTMLAnchorProps` or `HTMLButtonProps`
|
||||||
|
...passThroughProps
|
||||||
|
} = props;
|
||||||
|
// Buttons are **always** disabled if we're in a `loading` state
|
||||||
|
const disabled = props.disabled || loading;
|
||||||
|
|
||||||
|
// If pass an `href`-attr is passed it's `<a>`, otherwise it's a `<button />`
|
||||||
|
const isLink = typeof props.href !== "undefined";
|
||||||
|
const elementType = isLink ? "a" : "button";
|
||||||
|
|
||||||
|
const element = React.createElement(
|
||||||
|
elementType,
|
||||||
|
{
|
||||||
|
...passThroughProps,
|
||||||
|
disabled,
|
||||||
|
className: classNames(
|
||||||
|
// base styles independent what type of button it is
|
||||||
|
"inline-flex items-center relative",
|
||||||
|
// different styles depending on size
|
||||||
|
size === "sm" && "px-3 py-2 text-sm leading-4 font-medium rounded-sm",
|
||||||
|
size === "base" && "px-3 py-2 text-sm font-medium rounded-sm",
|
||||||
|
size === "lg" && "px-4 py-2 text-base font-medium rounded-sm",
|
||||||
|
// different styles depending on color
|
||||||
|
color === "primary" &&
|
||||||
|
(disabled
|
||||||
|
? "border border-transparent bg-gray-400 text-white"
|
||||||
|
: "border border-transparent text-white bg-neutral-900 hover:bg-neutral-800 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-neutral-900"),
|
||||||
|
color === "secondary" &&
|
||||||
|
(disabled
|
||||||
|
? "border border-gray-200 text-gray-400 bg-white"
|
||||||
|
: "border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 hover:text-gray-900 hover:shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-neutral-900"),
|
||||||
|
color === "minimal" &&
|
||||||
|
(disabled
|
||||||
|
? "text-gray-400 bg-transparent"
|
||||||
|
: "text-gray-700 bg-transparent hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:bg-gray-100 focus:ring-neutral-500"),
|
||||||
|
|
||||||
|
// set not-allowed cursor if disabled
|
||||||
|
disabled && "cursor-not-allowed",
|
||||||
|
props.className
|
||||||
|
),
|
||||||
|
// if we click a disabled button, we prevent going through the click handler
|
||||||
|
onClick: disabled
|
||||||
|
? (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
: props.onClick,
|
||||||
|
},
|
||||||
|
<>
|
||||||
|
{StartIcon && <StartIcon className="inline w-5 h-5 mr-2 -ml-1" />}
|
||||||
|
{props.children}
|
||||||
|
{loading && (
|
||||||
|
<div className="absolute transform -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2">
|
||||||
<svg
|
<svg
|
||||||
className="animate-spin mx-4 h-5 w-5 text-white"
|
className={classNames(
|
||||||
|
"w-5 h-5 mx-4 animate-spin",
|
||||||
|
color === "primary" ? "text-white" : "text-black"
|
||||||
|
)}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
|
@ -20,7 +96,16 @@ export default function Button(props) {
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</button>
|
{EndIcon && <EndIcon className="inline w-5 h-5 ml-2 -mr-1" />}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
return props.href ? (
|
||||||
|
<Link passHref href={props.href}>
|
||||||
|
{element}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
element
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export default function classNames(...classes) {
|
export default function classNames(...classes: unknown[]) {
|
||||||
return classes.filter(Boolean).join(" ");
|
return classes.filter(Boolean).join(" ");
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"next": "^10.2.0",
|
"next": "^10.2.3",
|
||||||
"next-auth": "^3.28.0",
|
"next-auth": "^3.28.0",
|
||||||
"next-transpile-modules": "^8.0.0",
|
"next-transpile-modules": "^8.0.0",
|
||||||
"nodemailer": "^6.6.3",
|
"nodemailer": "^6.6.3",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@components/Dialog";
|
import { Dialog, DialogClose, DialogContent } from "@components/Dialog";
|
||||||
import { Tooltip } from "@components/Tooltip";
|
|
||||||
import Loader from "@components/Loader";
|
import Loader from "@components/Loader";
|
||||||
|
import { Tooltip } from "@components/Tooltip";
|
||||||
|
import { Button } from "@components/ui/Button";
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
import {
|
import {
|
||||||
ClockIcon,
|
ClockIcon,
|
||||||
|
@ -12,6 +13,7 @@ import {
|
||||||
UserIcon,
|
UserIcon,
|
||||||
} from "@heroicons/react/solid";
|
} from "@heroicons/react/solid";
|
||||||
import classNames from "@lib/classNames";
|
import classNames from "@lib/classNames";
|
||||||
|
import showToast from "@lib/notification";
|
||||||
import { getSession, useSession } from "next-auth/client";
|
import { getSession, useSession } from "next-auth/client";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
@ -19,7 +21,6 @@ import { useRouter } from "next/router";
|
||||||
import React, { Fragment, useRef } from "react";
|
import React, { Fragment, useRef } from "react";
|
||||||
import Shell from "../../components/Shell";
|
import Shell from "../../components/Shell";
|
||||||
import prisma from "../../lib/prisma";
|
import prisma from "../../lib/prisma";
|
||||||
import showToast from "@lib/notification";
|
|
||||||
|
|
||||||
export default function Availability({ user, types }) {
|
export default function Availability({ user, types }) {
|
||||||
const [session, loading] = useSession();
|
const [session, loading] = useSession();
|
||||||
|
@ -30,6 +31,8 @@ export default function Availability({ user, types }) {
|
||||||
const descriptionRef = useRef<HTMLTextAreaElement>();
|
const descriptionRef = useRef<HTMLTextAreaElement>();
|
||||||
const lengthRef = useRef<HTMLInputElement>();
|
const lengthRef = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
const dialogOpen = router.query.new === "1";
|
||||||
|
|
||||||
async function createEventTypeHandler(event) {
|
async function createEventTypeHandler(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
@ -69,14 +72,23 @@ export default function Availability({ user, types }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateNewEventDialog = () => (
|
const CreateNewEventDialog = () => (
|
||||||
<Dialog>
|
<Dialog
|
||||||
<DialogTrigger className="py-2 px-4 mt-6 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900">
|
open={dialogOpen}
|
||||||
<PlusIcon className="w-5 h-5 mr-1 inline" />
|
onOpenChange={(isOpen) => {
|
||||||
|
const newQuery = {
|
||||||
|
...router.query,
|
||||||
|
};
|
||||||
|
delete newQuery["new"];
|
||||||
|
if (!isOpen) {
|
||||||
|
router.push({ pathname: router.pathname, query: newQuery });
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<Button className="mt-2" StartIcon={PlusIcon} href={{ query: { ...router.query, new: "1" } }}>
|
||||||
New event type
|
New event type
|
||||||
</DialogTrigger>
|
</Button>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h3 className="text-lg leading-6 font-bold text-gray-900" id="modal-title">
|
<h3 className="text-lg font-bold leading-6 text-gray-900" id="modal-title">
|
||||||
Add a new event type
|
Add a new event type
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
|
@ -97,7 +109,7 @@ export default function Availability({ user, types }) {
|
||||||
name="title"
|
name="title"
|
||||||
id="title"
|
id="title"
|
||||||
required
|
required
|
||||||
className="shadow-sm focus:ring-neutral-900 focus:border-neutral-900 block w-full sm:text-sm border-gray-300 rounded-sm"
|
className="block w-full border-gray-300 rounded-sm shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm"
|
||||||
placeholder="Quick Chat"
|
placeholder="Quick Chat"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,7 +120,7 @@ export default function Availability({ user, types }) {
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
<div className="flex rounded-sm shadow-sm">
|
<div className="flex rounded-sm shadow-sm">
|
||||||
<span className="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
|
<span className="inline-flex items-center px-3 text-gray-500 border border-r-0 border-gray-300 bg-gray-50 rounded-l-md sm:text-sm">
|
||||||
{location.hostname}/{user.username}/
|
{location.hostname}/{user.username}/
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
|
@ -117,7 +129,7 @@ export default function Availability({ user, types }) {
|
||||||
name="slug"
|
name="slug"
|
||||||
id="slug"
|
id="slug"
|
||||||
required
|
required
|
||||||
className="flex-1 block w-full focus:ring-neutral-900 focus:border-neutral-900 min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300"
|
className="flex-1 block w-full min-w-0 border-gray-300 rounded-none focus:border-neutral-900 rounded-r-md focus:ring-neutral-900 sm:text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -131,7 +143,7 @@ export default function Availability({ user, types }) {
|
||||||
ref={descriptionRef}
|
ref={descriptionRef}
|
||||||
name="description"
|
name="description"
|
||||||
id="description"
|
id="description"
|
||||||
className="shadow-sm focus:ring-neutral-900 focus:border-neutral-900 block w-full sm:text-sm border-gray-300 rounded-sm"
|
className="block w-full border-gray-300 rounded-sm shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm"
|
||||||
placeholder="A quick video meeting."></textarea>
|
placeholder="A quick video meeting."></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -139,17 +151,17 @@ export default function Availability({ user, types }) {
|
||||||
<label htmlFor="length" className="block text-sm font-medium text-gray-700">
|
<label htmlFor="length" className="block text-sm font-medium text-gray-700">
|
||||||
Length
|
Length
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 relative rounded-sm shadow-sm">
|
<div className="relative mt-1 rounded-sm shadow-sm">
|
||||||
<input
|
<input
|
||||||
ref={lengthRef}
|
ref={lengthRef}
|
||||||
type="number"
|
type="number"
|
||||||
name="length"
|
name="length"
|
||||||
id="length"
|
id="length"
|
||||||
required
|
required
|
||||||
className="focus:ring-neutral-900 focus:border-neutral-900 block w-full pr-20 sm:text-sm border-gray-300 rounded-sm"
|
className="block w-full pr-20 border-gray-300 rounded-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm"
|
||||||
placeholder="15"
|
placeholder="15"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 text-sm">
|
<div className="absolute inset-y-0 right-0 flex items-center pr-3 text-sm text-gray-400">
|
||||||
minutes
|
minutes
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -159,7 +171,7 @@ export default function Availability({ user, types }) {
|
||||||
<button type="submit" className="btn btn-primary">
|
<button type="submit" className="btn btn-primary">
|
||||||
Continue
|
Continue
|
||||||
</button>
|
</button>
|
||||||
<DialogClose as="button" className="btn btn-white mx-2">
|
<DialogClose as="button" className="mx-2 btn btn-white">
|
||||||
Cancel
|
Cancel
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</div>
|
</div>
|
||||||
|
@ -178,44 +190,44 @@ export default function Availability({ user, types }) {
|
||||||
heading="Event Types"
|
heading="Event Types"
|
||||||
subtitle="Create events to share for people to book on your calendar."
|
subtitle="Create events to share for people to book on your calendar."
|
||||||
CTA={types.length !== 0 && <CreateNewEventDialog />}>
|
CTA={types.length !== 0 && <CreateNewEventDialog />}>
|
||||||
<div className="bg-white border border-gray-200 rounded-sm overflow-hidden -mx-4 sm:mx-0">
|
<div className="-mx-4 overflow-hidden bg-white border border-gray-200 rounded-sm sm:mx-0">
|
||||||
<ul className="divide-y divide-neutral-200">
|
<ul className="divide-y divide-neutral-200">
|
||||||
{types.map((type) => (
|
{types.map((type) => (
|
||||||
<li key={type.id}>
|
<li key={type.id}>
|
||||||
<div className="hover:bg-neutral-50">
|
<div className="hover:bg-neutral-50">
|
||||||
<div className="px-4 py-4 flex items-center sm:px-6">
|
<div className="flex items-center px-4 py-4 sm:px-6">
|
||||||
<Link href={"/event-types/" + type.id}>
|
<Link href={"/event-types/" + type.id}>
|
||||||
<a className="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
|
<a className="flex-1 min-w-0 sm:flex sm:items-center sm:justify-between">
|
||||||
<span className="truncate">
|
<span className="truncate">
|
||||||
<div className="flex text-sm">
|
<div className="flex text-sm">
|
||||||
<p className="font-medium text-neutral-900 truncate">{type.title}</p>
|
<p className="font-medium truncate text-neutral-900">{type.title}</p>
|
||||||
{type.hidden && (
|
{type.hidden && (
|
||||||
<span className="ml-2 inline-flex items-center px-1.5 py-0.5 rounded-sm text-xs font-medium bg-yellow-100 text-yellow-800">
|
<span className="inline-flex items-center ml-2 px-1.5 py-0.5 text-yellow-800 text-xs font-medium bg-yellow-100 rounded-sm">
|
||||||
Hidden
|
Hidden
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex space-x-4">
|
<div className="flex mt-2 space-x-4">
|
||||||
<div className="flex items-center text-sm text-neutral-500">
|
<div className="flex items-center text-sm text-neutral-500">
|
||||||
<ClockIcon
|
<ClockIcon
|
||||||
className="flex-shrink-0 mr-1.5 h-4 w-4 text-neutral-400"
|
className="flex-shrink-0 mr-1.5 w-4 h-4 text-neutral-400"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<p>{type.length}m</p>
|
<p>{type.length}m</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-sm text-neutral-500">
|
<div className="flex items-center text-sm text-neutral-500">
|
||||||
<UserIcon
|
<UserIcon
|
||||||
className="flex-shrink-0 mr-1.5 h-4 w-4 text-neutral-400"
|
className="flex-shrink-0 mr-1.5 w-4 h-4 text-neutral-400"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<p>1-on-1</p>
|
<p>1-on-1</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-sm text-neutral-500">
|
<div className="flex items-center text-sm text-neutral-500">
|
||||||
<InformationCircleIcon
|
<InformationCircleIcon
|
||||||
className="flex-shrink-0 mr-1.5 h-4 w-4 text-neutral-400"
|
className="flex-shrink-0 mr-1.5 w-4 h-4 text-neutral-400"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<div className="max-w-32 sm:max-w-full truncate">
|
<div className="truncate max-w-32 sm:max-w-full">
|
||||||
{type.description.substring(0, 100)}
|
{type.description.substring(0, 100)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -224,15 +236,15 @@ export default function Availability({ user, types }) {
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="hidden sm:flex mt-4 flex-shrink-0 sm:mt-0 sm:ml-5">
|
<div className="flex-shrink-0 hidden mt-4 sm:flex sm:ml-5 sm:mt-0">
|
||||||
<div className="flex overflow-hidden space-x-5">
|
<div className="flex space-x-5 overflow-hidden">
|
||||||
<Tooltip content="Preview">
|
<Tooltip content="Preview">
|
||||||
<a
|
<a
|
||||||
href={"/" + session.user.username + "/" + type.slug}
|
href={"/" + session.user.username + "/" + type.slug}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
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">
|
||||||
<ExternalLinkIcon className="group-hover:text-black w-5 h-5" />
|
<ExternalLinkIcon className="w-5 h-5 group-hover:text-black" />
|
||||||
</a>
|
</a>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
@ -244,20 +256,20 @@ export default function Availability({ user, types }) {
|
||||||
window.location.hostname + "/" + session.user.username + "/" + type.slug
|
window.location.hostname + "/" + session.user.username + "/" + 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">
|
||||||
<LinkIcon className="group-hover:text-black w-5 h-5" />
|
<LinkIcon className="w-5 h-5 group-hover:text-black" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex sm:hidden ml-5 flex-shrink-0">
|
<div className="flex flex-shrink-0 ml-5 sm:hidden">
|
||||||
<Menu as="div" className="inline-block text-left">
|
<Menu as="div" className="inline-block text-left">
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<Menu.Button className="text-neutral-400 mt-1 p-2 border border-transparent hover:border-gray-200">
|
<Menu.Button className="p-2 mt-1 border border-transparent text-neutral-400 hover:border-gray-200">
|
||||||
<span className="sr-only">Open options</span>
|
<span className="sr-only">Open options</span>
|
||||||
<DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" />
|
<DotsHorizontalIcon className="w-5 h-5" aria-hidden="true" />
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -272,7 +284,7 @@ export default function Availability({ user, types }) {
|
||||||
leaveTo="transform opacity-0 scale-95">
|
leaveTo="transform opacity-0 scale-95">
|
||||||
<Menu.Items
|
<Menu.Items
|
||||||
static
|
static
|
||||||
className="origin-top-right absolute right-0 mt-2 w-56 rounded-sm shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none divide-y divide-neutral-100">
|
className="absolute right-0 w-56 mt-2 origin-top-right bg-white divide-y rounded-sm shadow-lg focus:outline-none divide-neutral-100 ring-1 ring-black ring-opacity-5">
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
{({ active }) => (
|
{({ active }) => (
|
||||||
|
@ -285,7 +297,7 @@ export default function Availability({ user, types }) {
|
||||||
"group flex items-center px-4 py-2 text-sm font-medium"
|
"group flex items-center px-4 py-2 text-sm font-medium"
|
||||||
)}>
|
)}>
|
||||||
<ExternalLinkIcon
|
<ExternalLinkIcon
|
||||||
className="mr-3 h-4 w-4 text-neutral-400 group-hover:text-neutral-500"
|
className="w-4 h-4 mr-3 text-neutral-400 group-hover:text-neutral-500"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
Preview
|
Preview
|
||||||
|
@ -307,10 +319,10 @@ export default function Availability({ user, types }) {
|
||||||
}}
|
}}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",
|
active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",
|
||||||
"group flex items-center px-4 py-2 text-sm w-full font-medium"
|
"group flex items-center px-4 py-2 w-full text-sm font-medium"
|
||||||
)}>
|
)}>
|
||||||
<LinkIcon
|
<LinkIcon
|
||||||
className="mr-3 h-4 w-4 text-neutral-400 group-hover:text-neutral-500"
|
className="w-4 h-4 mr-3 text-neutral-400 group-hover:text-neutral-500"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
Copy link to event
|
Copy link to event
|
||||||
|
@ -326,7 +338,7 @@ export default function Availability({ user, types }) {
|
||||||
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
||||||
{/* )}>*/}
|
{/* )}>*/}
|
||||||
{/* <DuplicateIcon*/}
|
{/* <DuplicateIcon*/}
|
||||||
{/* className="mr-3 h-4 w-4 text-neutral-400 group-hover:text-neutral-500"*/}
|
{/* className="w-4 h-4 mr-3 text-neutral-400 group-hover:text-neutral-500"*/}
|
||||||
{/* aria-hidden="true"*/}
|
{/* aria-hidden="true"*/}
|
||||||
{/* />*/}
|
{/* />*/}
|
||||||
{/* Duplicate*/}
|
{/* Duplicate*/}
|
||||||
|
@ -344,7 +356,7 @@ export default function Availability({ user, types }) {
|
||||||
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
||||||
{/* )}>*/}
|
{/* )}>*/}
|
||||||
{/* <TrashIcon*/}
|
{/* <TrashIcon*/}
|
||||||
{/* className="mr-3 h-5 w-5 text-red-400 group-hover:text-red-700"*/}
|
{/* className="w-5 h-5 mr-3 text-red-400 group-hover:text-red-700"*/}
|
||||||
{/* aria-hidden="true"*/}
|
{/* aria-hidden="true"*/}
|
||||||
{/* />*/}
|
{/* />*/}
|
||||||
{/* Delete*/}
|
{/* Delete*/}
|
||||||
|
@ -367,7 +379,7 @@ export default function Availability({ user, types }) {
|
||||||
{types.length === 0 && (
|
{types.length === 0 && (
|
||||||
<div className="md:py-20">
|
<div className="md:py-20">
|
||||||
<svg
|
<svg
|
||||||
className="w-1/2 md:w-32 mx-auto block mb-4"
|
className="block w-1/2 mx-auto mb-4 md:w-32"
|
||||||
viewBox="0 0 132 132"
|
viewBox="0 0 132 132"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
@ -604,7 +616,7 @@ export default function Availability({ user, types }) {
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
<div className="text-center block md:max-w-screen-sm mx-auto">
|
<div className="block mx-auto text-center md:max-w-screen-sm">
|
||||||
<h3 className="mt-2 text-xl font-bold text-neutral-900">Create your first event type</h3>
|
<h3 className="mt-2 text-xl font-bold text-neutral-900">Create your first event type</h3>
|
||||||
<p className="mt-1 text-md text-neutral-600">
|
<p className="mt-1 text-md text-neutral-600">
|
||||||
Event types enable you to share links that show available times on your calendar and allow
|
Event types enable you to share links that show available times on your calendar and allow
|
||||||
|
|
66
pages/sandbox/Button.tsx
Normal file
66
pages/sandbox/Button.tsx
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import { Button, ButtonProps } from "@components/ui/Button";
|
||||||
|
import { PlusIcon } from "@heroicons/react/solid";
|
||||||
|
import Head from "next/head";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function ButtonPage() {
|
||||||
|
const list: ButtonProps[] = [
|
||||||
|
// primary
|
||||||
|
{ color: "primary" },
|
||||||
|
{ color: "primary", disabled: true },
|
||||||
|
{ color: "primary", disabled: true, loading: true },
|
||||||
|
|
||||||
|
// secondary
|
||||||
|
{ color: "secondary" },
|
||||||
|
{ color: "secondary", disabled: true },
|
||||||
|
{ color: "secondary", disabled: true, loading: true },
|
||||||
|
|
||||||
|
// minimal
|
||||||
|
{ color: "minimal" },
|
||||||
|
{ color: "minimal", disabled: true },
|
||||||
|
{ color: "minimal", disabled: true, loading: true },
|
||||||
|
|
||||||
|
// sizes
|
||||||
|
{ color: "primary", size: "sm" },
|
||||||
|
{ color: "primary", size: "base" },
|
||||||
|
{ color: "primary", size: "lg" },
|
||||||
|
|
||||||
|
// href
|
||||||
|
{ href: "/staging" },
|
||||||
|
{ href: "/staging", disabled: true },
|
||||||
|
|
||||||
|
{ StartIcon: PlusIcon },
|
||||||
|
{ EndIcon: PlusIcon },
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<meta name="googlebot" content="noindex" />
|
||||||
|
</Head>
|
||||||
|
<div className="p-4 bg-gray-200">
|
||||||
|
<h1>Button component</h1>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{list.map((props, index) => (
|
||||||
|
<div key={index} className="p-2 m-2 bg-white">
|
||||||
|
<h3>
|
||||||
|
<code>
|
||||||
|
{JSON.stringify(
|
||||||
|
props,
|
||||||
|
(key, value) => {
|
||||||
|
if (key.includes("Icon")) {
|
||||||
|
return "..";
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
</code>
|
||||||
|
</h3>
|
||||||
|
<Button {...props}>Button text</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -4804,7 +4804,7 @@ next-transpile-modules@^8.0.0:
|
||||||
enhanced-resolve "^5.7.0"
|
enhanced-resolve "^5.7.0"
|
||||||
escalade "^3.1.1"
|
escalade "^3.1.1"
|
||||||
|
|
||||||
next@^10.2.0:
|
next@^10.2.3:
|
||||||
version "10.2.3"
|
version "10.2.3"
|
||||||
resolved "https://registry.npmjs.org/next/-/next-10.2.3.tgz#5aa058a63626338cea91c198fda8f2715c058394"
|
resolved "https://registry.npmjs.org/next/-/next-10.2.3.tgz#5aa058a63626338cea91c198fda8f2715c058394"
|
||||||
integrity sha512-dkM1mIfnORtGyzw/Yme8RdqNxlCMZyi4Lqj56F01/yHbe1ZtOaJ0cyqqRB4RGiPhjGGh0319f8ddjDyO1605Ow==
|
integrity sha512-dkM1mIfnORtGyzw/Yme8RdqNxlCMZyi4Lqj56F01/yHbe1ZtOaJ0cyqqRB4RGiPhjGGh0319f8ddjDyO1605Ow==
|
||||||
|
|
Loading…
Reference in a new issue