Remove playground (#1053)
* remove local testing playgrounds * bump outdated packages
This commit is contained in:
+1
-4
@@ -32,13 +32,10 @@
|
||||
"trailingComma": "es5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/ui": "^0.6.2",
|
||||
"@testing-library/jest-dom": "^5.11.9",
|
||||
"@types/node": "^14.14.22",
|
||||
"husky": "^4.3.8",
|
||||
"lint-staged": "^10.5.3",
|
||||
"prismjs": "^1.23.0",
|
||||
"tailwindcss": "^1.9.6",
|
||||
"lint-staged": "^12.2.1",
|
||||
"tsdx": "^0.14.1",
|
||||
"tslib": "^2.1.0",
|
||||
"typescript": "^3.9.7"
|
||||
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
devIndicators: {
|
||||
autoPrerender: false,
|
||||
},
|
||||
}
|
||||
@@ -23,8 +23,6 @@
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"playground": "next dev",
|
||||
"playground:build": "next build",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "../../scripts/test.sh",
|
||||
"build": "../../scripts/build.sh",
|
||||
@@ -35,12 +33,9 @@
|
||||
"react-dom": "^16 || ^17 || ^18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "^11.2.3",
|
||||
"@types/react": "^16.14.2",
|
||||
"@types/react-dom": "^16.9.10",
|
||||
"@popperjs/core": "^2.6.0",
|
||||
"@testing-library/react": "^11.2.3",
|
||||
"framer-motion": "^2.9.5",
|
||||
"next": "10.0.5",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"snapshot-diff": "^0.8.1"
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import Head from 'next/head'
|
||||
|
||||
import 'tailwindcss/tailwind.css'
|
||||
import { useDisposables } from '../src/hooks/use-disposables'
|
||||
import { PropsOf } from '../src/types'
|
||||
|
||||
function NextLink(props: PropsOf<'a'>) {
|
||||
let { href, children, ...rest } = props
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a {...rest}>{children}</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
enum KeyDisplayMac {
|
||||
ArrowUp = '↑',
|
||||
ArrowDown = '↓',
|
||||
ArrowLeft = '←',
|
||||
ArrowRight = '→',
|
||||
Home = '↖',
|
||||
End = '↘',
|
||||
Alt = '⌥',
|
||||
CapsLock = '⇪',
|
||||
Meta = '⌘',
|
||||
Shift = '⇧',
|
||||
Control = '⌃',
|
||||
Backspace = '⌫',
|
||||
Delete = '⌦',
|
||||
Enter = '↵',
|
||||
Escape = '⎋',
|
||||
Tab = '↹',
|
||||
PageUp = '⇞',
|
||||
PageDown = '⇟',
|
||||
' ' = '␣',
|
||||
}
|
||||
|
||||
enum KeyDisplayWindows {
|
||||
ArrowUp = '↑',
|
||||
ArrowDown = '↓',
|
||||
ArrowLeft = '←',
|
||||
ArrowRight = '→',
|
||||
Meta = 'Win',
|
||||
Control = 'Ctrl',
|
||||
Backspace = '⌫',
|
||||
Delete = 'Del',
|
||||
Escape = 'Esc',
|
||||
PageUp = 'PgUp',
|
||||
PageDown = 'PgDn',
|
||||
' ' = '␣',
|
||||
}
|
||||
|
||||
function tap<T>(value: T, cb: (value: T) => void) {
|
||||
cb(value)
|
||||
return value
|
||||
}
|
||||
|
||||
function useKeyDisplay() {
|
||||
let [mounted, setMounted] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
if (!mounted) return {}
|
||||
let isMac = navigator.userAgent.indexOf('Mac OS X') !== -1
|
||||
return isMac ? KeyDisplayMac : KeyDisplayWindows
|
||||
}
|
||||
|
||||
function KeyCaster() {
|
||||
let [keys, setKeys] = useState<string[]>([])
|
||||
let d = useDisposables()
|
||||
let KeyDisplay = useKeyDisplay()
|
||||
|
||||
useEffect(() => {
|
||||
function handler(event: KeyboardEvent) {
|
||||
setKeys(current => [
|
||||
event.shiftKey && event.key !== 'Shift'
|
||||
? KeyDisplay[`Shift${event.key}`] ?? event.key
|
||||
: KeyDisplay[event.key] ?? event.key,
|
||||
...current,
|
||||
])
|
||||
d.setTimeout(() => setKeys(current => tap(current.slice(), clone => clone.pop())), 2000)
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handler, true)
|
||||
return () => window.removeEventListener('keydown', handler, true)
|
||||
}, [d, KeyDisplay])
|
||||
|
||||
if (keys.length <= 0) return null
|
||||
|
||||
return (
|
||||
<div className="fixed z-50 px-4 py-2 overflow-hidden text-2xl tracking-wide text-blue-100 bg-blue-800 rounded-md shadow cursor-default pointer-events-none select-none right-4 bottom-4">
|
||||
{keys
|
||||
.slice()
|
||||
.reverse()
|
||||
.join(' ')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="https://headlessui.dev/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="https://headlessui.dev/favicon-16x16.png"
|
||||
/>
|
||||
</Head>
|
||||
|
||||
<div className="flex flex-col h-screen overflow-hidden font-sans antialiased text-gray-900 bg-gray-700">
|
||||
<header className="relative z-10 flex items-center justify-between flex-shrink-0 px-4 py-4 bg-gray-700 border-b border-gray-200 sm:px-6 lg:px-8">
|
||||
<NextLink href="/">
|
||||
<Logo className="h-6" />
|
||||
</NextLink>
|
||||
</header>
|
||||
|
||||
<KeyCaster />
|
||||
|
||||
<main className="flex-1 overflow-auto bg-gray-50">
|
||||
<Component {...pageProps} />
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Logo({ className }) {
|
||||
return (
|
||||
<svg className={className} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 243 42">
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M65.74 13.663c-2.62 0-4.702.958-5.974 2.95V6.499h-4.163V33.32h4.163V23.051c0-3.908 2.159-5.518 4.896-5.518 2.62 0 4.317 1.533 4.317 4.445V33.32h4.162V21.557c0-4.982-3.083-7.894-7.4-7.894zM79.936 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.674-10.116-6.052 0-10.176 4.407-10.176 10.078 0 5.748 4.124 10.078 10.484 10.078 3.778 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.925 1.341-2.66 2.376-4.972 2.376-3.084 0-5.512-1.533-6.168-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.506 0 4.934 1.418 5.512 4.713H79.898zM113.282 14.161v2.72c-1.465-1.992-3.739-3.218-6.746-3.218-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.162V14.16h-4.162zm-6.09 15.71c-3.469 0-6.091-2.567-6.091-6.13 0-3.564 2.622-6.131 6.091-6.131 3.469 0 6.09 2.567 6.09 6.13 0 3.564-2.621 6.132-6.09 6.132zM136.597 6.498v10.384c-1.465-1.993-3.739-3.219-6.746-3.219-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.163V6.497h-4.163zm-6.09 23.374c-3.469 0-6.09-2.568-6.09-6.131 0-3.564 2.621-6.131 6.09-6.131s6.09 2.567 6.09 6.13c0 3.564-2.621 6.132-6.09 6.132zM144.648 33.32h4.163V5.348h-4.163V33.32zM155.957 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.675-10.116-6.051 0-10.176 4.407-10.176 10.078 0 5.748 4.125 10.078 10.485 10.078 3.777 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.926 1.341-2.66 2.376-4.973 2.376-3.083 0-5.512-1.533-6.167-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.505 0 4.934 1.418 5.512 4.713h-11.332zM177.137 19.45c0-1.38 1.311-2.032 2.814-2.032 1.581 0 2.93.69 3.623 2.184l3.508-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.279-2.759l-3.584 2.07c1.233 2.758 4.008 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.369-4.98-10.369-8.468zM192.774 19.45c0-1.38 1.31-2.032 2.813-2.032 1.581 0 2.93.69 3.624 2.184l3.507-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.278-2.759l-3.585 2.07c1.233 2.758 4.009 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.368-4.98-10.368-8.468zM224.523 28.9c2.889 0 5.027-1.715 5.027-4.53v-8.782h-2.588v8.577c0 1.268-.676 2.219-2.439 2.219s-2.438-.951-2.438-2.22v-8.576h-2.569v8.782c0 2.815 2.138 4.53 5.007 4.53zM232.257 15.588V28.64h2.588V15.588h-2.588z"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fillRule="evenodd"
|
||||
d="M233.817 9.328H220.42c-2.96 0-5.359 2.385-5.359 5.327v13.318c0 2.942 2.399 5.327 5.359 5.327h13.397c2.959 0 5.358-2.385 5.358-5.327V14.655c0-2.942-2.399-5.327-5.358-5.327zM220.42 6.664c-4.439 0-8.038 3.578-8.038 7.99v13.319c0 4.413 3.599 7.99 8.038 7.99h13.397c4.439 0 8.038-3.577 8.038-7.99V14.655c0-4.413-3.599-7.99-8.038-7.99H220.42z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fillRule="evenodd"
|
||||
d="M220.42 9.328h13.397c2.959 0 5.358 2.385 5.358 5.327v13.318c0 2.942-2.399 5.327-5.358 5.327H220.42c-2.96 0-5.359-2.385-5.359-5.327V14.655c0-2.942 2.399-5.327 5.359-5.327zm-8.038 5.327c0-4.413 3.599-7.99 8.038-7.99h13.397c4.439 0 8.038 3.577 8.038 7.99v13.318c0 4.413-3.599 7.99-8.038 7.99H220.42c-4.439 0-8.038-3.577-8.038-7.99V14.655z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="url(#prefix__paint0_linear)"
|
||||
d="M8.577 26.097l25.779-8.556c-.514-3.201-.88-5.342-1.307-6.974-.457-1.756-.821-2.226-.965-2.39a5.026 5.026 0 00-1.81-1.306c-.2-.086-.762-.284-2.583-.175-1.924.116-4.453.507-8.455 1.137-4.003.63-6.529 1.035-8.395 1.516-1.766.456-2.239.817-2.403.96a4.999 4.999 0 00-1.315 1.8c-.085.198-.285.757-.175 2.568.116 1.913.51 4.426 1.143 8.405.178 1.114.337 2.113.486 3.015z"
|
||||
/>
|
||||
<path
|
||||
fill="url(#prefix__paint1_linear)"
|
||||
fillRule="evenodd"
|
||||
d="M1.47 24.124C.244 16.427-.37 12.58.96 9.49A11.665 11.665 0 014.027 5.29c2.545-2.21 6.416-2.82 14.16-4.039C25.93.031 29.8-.578 32.907.743a11.729 11.729 0 014.225 3.05c2.223 2.53 2.836 6.38 4.063 14.076 1.226 7.698 1.84 11.546.511 14.636a11.666 11.666 0 01-3.069 4.199c-2.545 2.21-6.416 2.82-14.159 4.039-7.743 1.219-11.614 1.828-14.722.508a11.728 11.728 0 01-4.224-3.05C3.31 35.67 2.697 31.82 1.47 24.123zm13.657 13.668c2.074-.125 4.743-.54 8.697-1.163 3.953-.622 6.62-1.047 8.632-1.566 1.949-.502 2.846-.992 3.426-1.496a7.5 7.5 0 001.973-2.7c.302-.703.494-1.703.372-3.7-.125-2.063-.543-4.716-1.17-8.646-.625-3.93-1.053-6.582-1.574-8.582-.506-1.937-.999-2.83-1.505-3.405a7.54 7.54 0 00-2.716-1.961c-.707-.301-1.713-.492-3.723-.371-2.074.125-4.743.54-8.697 1.163-3.953.622-6.62 1.047-8.632 1.565-1.949.503-2.846.993-3.426 1.497a7.5 7.5 0 00-1.972 2.699c-.303.704-.495 1.704-.373 3.701.125 2.062.543 4.716 1.17 8.646.625 3.93 1.053 6.582 1.574 8.581.506 1.938 1 2.83 1.505 3.406a7.54 7.54 0 002.716 1.961c.707.3 1.713.492 3.723.37z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="prefix__paint0_linear"
|
||||
x1="16.759"
|
||||
x2="23.386"
|
||||
y1="0"
|
||||
y2="41.662"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#66E3FF" />
|
||||
<stop offset="1" stopColor="#7064F9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="prefix__paint1_linear"
|
||||
x1="16.759"
|
||||
x2="23.386"
|
||||
y1="0"
|
||||
y2="41.662"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#66E3FF" />
|
||||
<stop offset="1" stopColor="#7064F9" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyApp
|
||||
@@ -1,64 +0,0 @@
|
||||
import React from 'react'
|
||||
import ErrorPage from 'next/error'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { ExamplesType, resolveAllExamples } from '../playground-utils/resolve-all-examples'
|
||||
import { PropsOf } from '../src/types'
|
||||
|
||||
function NextLink(props: PropsOf<'a'>) {
|
||||
let { href, children, ...rest } = props
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a {...rest}>{children}</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
examples: await resolveAllExamples('pages'),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default function Page(props: { examples: false | ExamplesType[] }) {
|
||||
if (props.examples === false) {
|
||||
return <ErrorPage statusCode={404} />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Examples</title>
|
||||
</Head>
|
||||
|
||||
<div className="container my-24">
|
||||
<div className="prose">
|
||||
<h2>Examples</h2>
|
||||
<Examples examples={props.examples} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function Examples(props: { examples: ExamplesType[] }) {
|
||||
return (
|
||||
<ul>
|
||||
{props.examples.map(example => (
|
||||
<li key={example.path}>
|
||||
{example.children ? (
|
||||
<h3 className="text-xl capitalize">{example.name}</h3>
|
||||
) : (
|
||||
<NextLink href={example.path} className="capitalize">
|
||||
{example.name}
|
||||
</NextLink>
|
||||
)}
|
||||
{example.children && <Examples examples={example.children} />}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
import React, { useState, Fragment } from 'react'
|
||||
import { Dialog, Menu, Portal, Transition } from '@headlessui/react'
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',
|
||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
function Nested({ onClose, level = 0 }) {
|
||||
let [showChild, setShowChild] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={true} onClose={onClose} className="fixed z-10 inset-0">
|
||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 opacity-25" />
|
||||
<div
|
||||
className="z-10 fixed left-12 top-24 bg-white w-96 p-4"
|
||||
style={{
|
||||
transform: `translate(calc(50px * ${level}), calc(50px * ${level}))`,
|
||||
}}
|
||||
>
|
||||
<p>Level: {level}</p>
|
||||
<div className="space-x-4">
|
||||
<button className="bg-gray-200 px-2 py-1 rounded" onClick={() => setShowChild(true)}>
|
||||
Open (1)
|
||||
</button>
|
||||
<button className="bg-gray-200 px-2 py-1 rounded" onClick={() => setShowChild(true)}>
|
||||
Open (2)
|
||||
</button>
|
||||
<button className="bg-gray-200 px-2 py-1 rounded" onClick={() => setShowChild(true)}>
|
||||
Open (3)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{showChild && <Nested onClose={() => setShowChild(false)} level={level + 1} />}
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
let [nested, setNested] = useState(false)
|
||||
|
||||
let [trigger, container] = usePopper({
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(v => !v)}
|
||||
className="m-12 px-4 py-2 text-base font-medium leading-6 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md shadow-sm hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue sm:text-sm sm:leading-5"
|
||||
>
|
||||
Toggle!
|
||||
</button>
|
||||
|
||||
<button onClick={() => setNested(true)}>Show nested</button>
|
||||
{nested && <Nested onClose={() => setNested(false)} />}
|
||||
|
||||
<Transition show={isOpen} as={Fragment} afterLeave={() => console.log('done')}>
|
||||
<Dialog onClose={setIsOpen}>
|
||||
<div className="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-75"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-75"
|
||||
leaveTo="opacity-0"
|
||||
entered="opacity-75"
|
||||
>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
enter="ease-out transform duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in transform duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span
|
||||
className="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true"
|
||||
>
|
||||
​
|
||||
</span>
|
||||
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
|
||||
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
{/* Heroicon name: exclamation */}
|
||||
<svg
|
||||
className="h-6 w-6 text-red-600"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg leading-6 font-medium text-gray-900"
|
||||
>
|
||||
Deactivate account
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Are you sure you want to deactivate your account? All of your data will
|
||||
be permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
<div className="relative inline-block text-left mt-10">
|
||||
<Menu>
|
||||
<span className="rounded-md shadow-sm">
|
||||
<Menu.Button
|
||||
ref={trigger}
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Choose a reason</span>
|
||||
<svg
|
||||
className="w-5 h-5 ml-2 -mr-1"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Menu.Button>
|
||||
</span>
|
||||
|
||||
<Transition
|
||||
enter="transition duration-300 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Portal>
|
||||
<Menu.Items
|
||||
ref={container}
|
||||
className="z-20 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
<p className="text-sm font-medium leading-5 text-gray-900 truncate">
|
||||
tom@example.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item
|
||||
as="a"
|
||||
href="#account-settings"
|
||||
className={resolveClass}
|
||||
>
|
||||
Account settings
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" href="#support" className={resolveClass}>
|
||||
Support
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
as="a"
|
||||
disabled
|
||||
href="#new-feature"
|
||||
className={resolveClass}
|
||||
>
|
||||
New feature (soon)
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" href="#license" className={resolveClass}>
|
||||
License
|
||||
</Menu.Item>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#sign-out" className={resolveClass}>
|
||||
Sign out
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Portal>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:shadow-outline-red sm:ml-3 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Deactivate
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:shadow-outline-indigo sm:mt-0 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="w-full max-w-xs mx-auto">
|
||||
<Disclosure>
|
||||
<Disclosure.Button>Trigger</Disclosure.Button>
|
||||
|
||||
<Transition
|
||||
enter="transition duration-1000 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-1000 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Disclosure.Panel className="p-4 bg-white mt-4">Content</Disclosure.Panel>
|
||||
</Transition>
|
||||
</Disclosure>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Listbox } from '@headlessui/react'
|
||||
|
||||
import { classNames } from '../../src/utils/class-names'
|
||||
|
||||
let people = [
|
||||
'Wade Cooper',
|
||||
'Arlene Mccoy',
|
||||
'Devon Webb',
|
||||
'Tom Cook',
|
||||
'Tanya Fox',
|
||||
'Hellen Schmidt',
|
||||
'Caroline Schultz',
|
||||
'Mason Heaney',
|
||||
'Claudie Smitham',
|
||||
'Emil Schaefer',
|
||||
]
|
||||
|
||||
export default function Home() {
|
||||
let [active, setActivePerson] = useState(people[2])
|
||||
|
||||
// Choose a random person on mount
|
||||
useEffect(() => {
|
||||
setActivePerson(people[Math.floor(Math.random() * people.length)])
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="w-full max-w-xs mx-auto">
|
||||
<div className="space-y-1">
|
||||
<Listbox
|
||||
value={active}
|
||||
onChange={value => {
|
||||
console.log('value:', value)
|
||||
setActivePerson(value)
|
||||
}}
|
||||
>
|
||||
<Listbox.Label className="block text-sm font-medium leading-5 text-gray-700">
|
||||
Assigned to
|
||||
</Listbox.Label>
|
||||
|
||||
<div className="relative">
|
||||
<span className="inline-block w-full rounded-md shadow-sm">
|
||||
<Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md cursor-default focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5">
|
||||
<span className="block truncate">{active}</span>
|
||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<svg
|
||||
className="w-5 h-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7 7l3-3 3 3m0 6l-3 3-3-3"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
</span>
|
||||
|
||||
<div className="absolute w-full mt-1 bg-white rounded-md shadow-lg">
|
||||
<Listbox.Options className="py-1 overflow-auto text-base leading-6 rounded-md shadow-xs max-h-60 focus:outline-none sm:text-sm sm:leading-5">
|
||||
{people.map(name => (
|
||||
<Listbox.Option
|
||||
key={name}
|
||||
value={name}
|
||||
className={({ active }) => {
|
||||
return classNames(
|
||||
'relative py-2 pl-3 cursor-default select-none pr-9 focus:outline-none',
|
||||
active ? 'text-white bg-indigo-600' : 'text-gray-900'
|
||||
)
|
||||
}}
|
||||
>
|
||||
{({ active, selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={classNames(
|
||||
'block truncate',
|
||||
selected ? 'font-semibold' : 'font-normal'
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
{selected && (
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-indigo-600'
|
||||
)}
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</div>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Listbox } from '@headlessui/react'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
let people = [
|
||||
'Wade Cooper',
|
||||
'Arlene Mccoy',
|
||||
'Devon Webb',
|
||||
'Tom Cook',
|
||||
'Tanya Fox',
|
||||
'Hellen Schmidt',
|
||||
'Caroline Schultz',
|
||||
'Mason Heaney',
|
||||
'Claudie Smitham',
|
||||
'Emil Schaefer',
|
||||
]
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 space-x-4 bg-gray-50">
|
||||
<PeopleList />
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium leading-5 text-gray-700">
|
||||
Email
|
||||
</label>
|
||||
<div className="relative mt-1 rounded-md shadow-sm">
|
||||
<input
|
||||
className="block w-full form-input sm:text-sm sm:leading-5"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PeopleList />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function PeopleList() {
|
||||
let [active, setActivePerson] = useState(people[2])
|
||||
|
||||
// Choose a random person on mount
|
||||
useEffect(() => {
|
||||
setActivePerson(people[Math.floor(Math.random() * people.length)])
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="w-64">
|
||||
<div className="space-y-1">
|
||||
<Listbox
|
||||
value={active}
|
||||
onChange={value => {
|
||||
console.log('value:', value)
|
||||
setActivePerson(value)
|
||||
}}
|
||||
>
|
||||
<Listbox.Label className="block text-sm font-medium leading-5 text-gray-700">
|
||||
Assigned to
|
||||
</Listbox.Label>
|
||||
|
||||
<div className="relative">
|
||||
<span className="inline-block w-full rounded-md shadow-sm">
|
||||
<Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md cursor-default focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5">
|
||||
<span className="block truncate">{active}</span>
|
||||
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<svg
|
||||
className="w-5 h-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7 7l3-3 3 3m0 6l-3 3-3-3"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
</span>
|
||||
|
||||
<div className="absolute w-full mt-1 bg-white rounded-md shadow-lg">
|
||||
<Listbox.Options className="py-1 overflow-auto text-base leading-6 rounded-md shadow-xs max-h-60 focus:outline-none sm:text-sm sm:leading-5">
|
||||
{people.map(name => (
|
||||
<Listbox.Option
|
||||
key={name}
|
||||
value={name}
|
||||
className={({ active }) => {
|
||||
return classNames(
|
||||
'relative py-2 pl-3 cursor-default select-none pr-9 focus:outline-none',
|
||||
active ? 'text-white bg-indigo-600' : 'text-gray-900'
|
||||
)
|
||||
}}
|
||||
>
|
||||
{({ active, selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={classNames(
|
||||
'block truncate',
|
||||
selected ? 'font-semibold' : 'font-normal'
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
{selected && (
|
||||
<span
|
||||
className={classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-indigo-600'
|
||||
)}
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</div>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { Menu } from '../../src/components/menu/menu'
|
||||
|
||||
import { classNames } from '../../src/utils/class-names'
|
||||
import { PropsOf } from '../../src/types'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="relative inline-block text-left">
|
||||
<Menu>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<span className="rounded-md shadow-sm">
|
||||
<Menu.Button className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800">
|
||||
<span>Options</span>
|
||||
<svg className="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Menu.Button>
|
||||
</span>
|
||||
|
||||
<AnimatePresence>
|
||||
{open && (
|
||||
<Menu.Items
|
||||
static
|
||||
as={motion.div}
|
||||
initial={{ opacity: 0, y: 0 }}
|
||||
animate={{ opacity: 1, y: '0.5rem' }}
|
||||
exit={{ opacity: 0, y: 0 }}
|
||||
className="absolute right-0 w-56 bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none opacity-0"
|
||||
>
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
<p className="text-sm font-medium leading-5 text-gray-900 truncate">
|
||||
tom@example.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Item href="#account-settings">Account settings</Item>
|
||||
<Item as={NextLink} href="#support">
|
||||
Support
|
||||
</Item>
|
||||
<Item href="#new-feature" disabled>
|
||||
New feature (soon)
|
||||
</Item>
|
||||
<Item href="#license">License</Item>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Item as={SignOutButton} />
|
||||
</div>
|
||||
</Menu.Items>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function NextLink(props: PropsOf<'a'>) {
|
||||
let { href, children, ...rest } = props
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a {...rest}>{children}</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function SignOutButton(props) {
|
||||
return (
|
||||
<form
|
||||
method="POST"
|
||||
action="#"
|
||||
onSubmit={e => {
|
||||
e.preventDefault()
|
||||
alert('SIGNED OUT')
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<button type="submit" {...props}>
|
||||
Sign out
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
function Item(props: PropsOf<typeof Menu.Item>) {
|
||||
return (
|
||||
<Menu.Item
|
||||
as="a"
|
||||
className={({ active, disabled }) =>
|
||||
classNames(
|
||||
'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
|
||||
active && 'bg-gray-100 text-gray-900',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import React, { ReactNode, useState, useEffect } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Menu } from '@headlessui/react'
|
||||
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
let [trigger, container] = usePopper({
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
|
||||
})
|
||||
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
|
||||
active && 'bg-gray-100 text-gray-900',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="inline-block mt-64 text-left">
|
||||
<Menu>
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<Menu.Button
|
||||
ref={trigger}
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg className="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Menu.Button>
|
||||
</span>
|
||||
|
||||
<Portal>
|
||||
<Menu.Items
|
||||
className="w-56 bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
ref={container}
|
||||
>
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
<p className="text-sm font-medium leading-5 text-gray-900 truncate">
|
||||
tom@example.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#account-settings" className={resolveClass}>
|
||||
Account settings
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{data => (
|
||||
<a href="#support" className={resolveClass(data)}>
|
||||
Support
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" disabled href="#new-feature" className={resolveClass}>
|
||||
New feature (soon)
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" href="#license" className={resolveClass}>
|
||||
License
|
||||
</Menu.Item>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#sign-out" className={resolveClass}>
|
||||
Sign out
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Portal>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Portal(props: { children: ReactNode }) {
|
||||
let { children } = props
|
||||
let [mounted, setMounted] = useState(false)
|
||||
|
||||
useEffect(() => setMounted(true), [])
|
||||
|
||||
if (!mounted) return null
|
||||
return createPortal(children, document.body)
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
let [trigger, container] = usePopper({
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
|
||||
})
|
||||
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',
|
||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="inline-block mt-64 text-left">
|
||||
<Menu>
|
||||
<span className="rounded-md shadow-sm">
|
||||
<Menu.Button
|
||||
ref={trigger}
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg className="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Menu.Button>
|
||||
</span>
|
||||
|
||||
<div ref={container} className="w-56">
|
||||
<Transition
|
||||
enter="transition duration-100 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Menu.Items className="bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none">
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
<p className="text-sm font-medium leading-5 text-gray-900 truncate">
|
||||
tom@example.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#account-settings" className={resolveClass}>
|
||||
Account settings
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{data => (
|
||||
<a href="#support" className={resolveClass(data)}>
|
||||
Support
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" disabled href="#new-feature" className={resolveClass}>
|
||||
New feature (soon)
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" href="#license" className={resolveClass}>
|
||||
License
|
||||
</Menu.Item>
|
||||
</div>
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#sign-out" className={resolveClass}>
|
||||
Sign out
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',
|
||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span className="rounded-md shadow-sm">
|
||||
<Menu.Button className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800">
|
||||
<span>Options</span>
|
||||
<svg className="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Menu.Button>
|
||||
</span>
|
||||
|
||||
<Transition
|
||||
enter="transition duration-1000 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-1000 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none">
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
<p className="text-sm font-medium leading-5 text-gray-900 truncate">
|
||||
tom@example.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#account-settings" className={resolveClass}>
|
||||
Account settings
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" href="#support" className={resolveClass}>
|
||||
Support
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" disabled href="#new-feature" className={resolveClass}>
|
||||
New feature (soon)
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" href="#license" className={resolveClass}>
|
||||
License
|
||||
</Menu.Item>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#sign-out" className={resolveClass}>
|
||||
Sign out
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Menu } from '@headlessui/react'
|
||||
|
||||
import { PropsOf } from '../../src/types'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span className="rounded-md shadow-sm">
|
||||
<Menu.Button className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800">
|
||||
<span>Options</span>
|
||||
<svg
|
||||
className="w-5 h-5 ml-2 -mr-1 transition-transform duration-150"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Menu.Button>
|
||||
</span>
|
||||
|
||||
<Menu.Items className="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none">
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
<p className="text-sm font-medium leading-5 text-gray-900 truncate">
|
||||
tom@example.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<CustomMenuItem href="#account-settings">Account settings</CustomMenuItem>
|
||||
<CustomMenuItem href="#support">Support</CustomMenuItem>
|
||||
<CustomMenuItem disabled href="#new-feature">
|
||||
New feature (soon)
|
||||
</CustomMenuItem>
|
||||
<CustomMenuItem href="#license">License</CustomMenuItem>
|
||||
</div>
|
||||
<div className="py-1">
|
||||
<CustomMenuItem href="#sign-out">Sign out</CustomMenuItem>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CustomMenuItem(props: PropsOf<typeof Menu.Item>) {
|
||||
return (
|
||||
<Menu.Item {...props}>
|
||||
{({ active, disabled }) => (
|
||||
<a
|
||||
href={props.href}
|
||||
className={classNames(
|
||||
'flex justify-between w-full text-left px-4 py-2 text-sm leading-5',
|
||||
active ? 'bg-indigo-500 text-white' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)}
|
||||
>
|
||||
<span className={classNames(active && 'font-bold')}>{props.children}</span>
|
||||
<kbd className={classNames('font-sans', active && 'text-indigo-50')}>⌘K</kbd>
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
)
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Menu } from '@headlessui/react'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex justify-center w-screen h-full p-12 space-x-4 bg-gray-50">
|
||||
<Dropdown />
|
||||
|
||||
<div>
|
||||
<div className="relative rounded-md shadow-sm">
|
||||
<input
|
||||
className="block w-full form-input sm:text-sm sm:leading-5"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dropdown />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Dropdown() {
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
|
||||
active && 'bg-gray-100 text-gray-900',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<Menu.Button className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800">
|
||||
<span>Options</span>
|
||||
<svg className="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Menu.Button>
|
||||
</span>
|
||||
|
||||
<Menu.Items className="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none">
|
||||
<div className="px-4 py-3">
|
||||
<p className="text-sm leading-5">Signed in as</p>
|
||||
<p className="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#account-settings" className={resolveClass}>
|
||||
Account settings
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{data => (
|
||||
<a href="#support" className={resolveClass(data)}>
|
||||
Support
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" disabled href="#new-feature" className={resolveClass}>
|
||||
New feature (soon)
|
||||
</Menu.Item>
|
||||
<Menu.Item as="a" href="#license" className={resolveClass}>
|
||||
License
|
||||
</Menu.Item>
|
||||
</div>
|
||||
|
||||
<div className="py-1">
|
||||
<Menu.Item as="a" href="#sign-out" className={resolveClass}>
|
||||
Sign out
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
import React, { forwardRef, Fragment } from 'react'
|
||||
import { Popover, Portal, Transition } from '@headlessui/react'
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
import { PropsOf as Props } from '../../src/types'
|
||||
|
||||
let Button = forwardRef((props: Props<'button'>, ref) => {
|
||||
return (
|
||||
<Popover.Button
|
||||
ref={ref}
|
||||
className="px-3 py-2 bg-gray-300 border-2 border-transparent focus:outline-none focus:border-blue-900"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
function Link(props: Props<'a'>) {
|
||||
return (
|
||||
<a
|
||||
href="/"
|
||||
className="px-3 py-2 border-2 border-transparent hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:border-blue-900"
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
let options = {
|
||||
placement: 'bottom-start',
|
||||
strategy: 'fixed',
|
||||
modifiers: [],
|
||||
}
|
||||
|
||||
let [reference1, popper1] = usePopper(options)
|
||||
let [reference2, popper2] = usePopper(options)
|
||||
|
||||
let links = ['First', 'Second', 'Third', 'Fourth']
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center space-x-12 p-12">
|
||||
<button>Previous</button>
|
||||
|
||||
<Popover.Group as="nav" aria-label="Mythical University" className="flex space-x-3">
|
||||
<Popover as="div" className="relative">
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-300 transform"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="transition ease-in duration-300 transform"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Popover.Overlay className="bg-opacity-75 bg-gray-500 fixed inset-0 z-20"></Popover.Overlay>
|
||||
</Transition>
|
||||
|
||||
<Popover.Button className="px-3 py-2 bg-gray-300 border-2 border-transparent focus:outline-none focus:border-blue-900 relative z-30">
|
||||
Normal
|
||||
</Popover.Button>
|
||||
<Popover.Panel className="absolute flex flex-col w-64 bg-gray-100 border-2 border-blue-900 z-30">
|
||||
{links.map((link, i) => (
|
||||
<Link key={link} hidden={i === 2}>
|
||||
Normal - {link}
|
||||
</Link>
|
||||
))}
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
|
||||
<Popover as="div" className="relative">
|
||||
<Button>Focus</Button>
|
||||
<Popover.Panel
|
||||
focus
|
||||
className="absolute flex flex-col w-64 bg-gray-100 border-2 border-blue-900"
|
||||
>
|
||||
{links.map((link, i) => (
|
||||
<Link key={link}>Focus - {link}</Link>
|
||||
))}
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
|
||||
<Popover as="div" className="relative">
|
||||
<Button ref={reference1}>Portal</Button>
|
||||
<Portal>
|
||||
<Popover.Panel
|
||||
ref={popper1}
|
||||
className="flex flex-col w-64 bg-gray-100 border-2 border-blue-900"
|
||||
>
|
||||
{links.map(link => (
|
||||
<Link key={link}>Portal - {link}</Link>
|
||||
))}
|
||||
</Popover.Panel>
|
||||
</Portal>
|
||||
</Popover>
|
||||
|
||||
<Popover as="div" className="relative">
|
||||
<Button ref={reference2}>Focus in Portal</Button>
|
||||
<Portal>
|
||||
<Popover.Panel
|
||||
ref={popper2}
|
||||
focus
|
||||
className="flex flex-col w-64 bg-gray-100 border-2 border-blue-900"
|
||||
>
|
||||
{links.map(link => (
|
||||
<Link key={link}>Focus in Portal - {link}</Link>
|
||||
))}
|
||||
</Popover.Panel>
|
||||
</Portal>
|
||||
</Popover>
|
||||
</Popover.Group>
|
||||
|
||||
<button>Next</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
import React, { useState } from 'react'
|
||||
import { RadioGroup } from '@headlessui/react'
|
||||
import { classNames } from '../../src/utils/class-names'
|
||||
|
||||
export default function Home() {
|
||||
let access = [
|
||||
{
|
||||
id: 'access-1',
|
||||
name: 'Public access',
|
||||
description: 'This project would be available to anyone who has the link',
|
||||
},
|
||||
{
|
||||
id: 'access-2',
|
||||
name: 'Private to Project Members',
|
||||
description: 'Only members of this project would be able to access',
|
||||
},
|
||||
{
|
||||
id: 'access-3',
|
||||
name: 'Private to you',
|
||||
description: 'You are the only one able to access this project',
|
||||
},
|
||||
]
|
||||
let [active, setActive] = useState()
|
||||
|
||||
return (
|
||||
<div className="p-12 max-w-xl">
|
||||
<a href="/">Link before</a>
|
||||
<RadioGroup value={active} onChange={setActive}>
|
||||
<fieldset className="space-y-4">
|
||||
<legend>
|
||||
<h2 className="text-xl">Privacy setting</h2>
|
||||
</legend>
|
||||
|
||||
<div className="bg-white rounded-md -space-y-px">
|
||||
{access.map(({ id, name, description }, i) => {
|
||||
return (
|
||||
<RadioGroup.Option
|
||||
key={id}
|
||||
value={id}
|
||||
className={({ active }) =>
|
||||
classNames(
|
||||
// Rounded corners
|
||||
i === 0 && 'rounded-tl-md rounded-tr-md',
|
||||
access.length - 1 === i && 'rounded-bl-md rounded-br-md',
|
||||
|
||||
// Shared
|
||||
'relative border p-4 flex focus:outline-none',
|
||||
active ? 'bg-indigo-50 border-indigo-200 z-10' : 'border-gray-200'
|
||||
)
|
||||
}
|
||||
>
|
||||
{({ active, checked }) => (
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<div className="ml-3 flex flex-col cursor-pointer">
|
||||
<span
|
||||
className={classNames(
|
||||
'block text-sm leading-5 font-medium',
|
||||
active ? 'text-indigo-900' : 'text-gray-900'
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
<span
|
||||
className={classNames(
|
||||
'block text-sm leading-5',
|
||||
active ? 'text-indigo-700' : 'text-gray-500'
|
||||
)}
|
||||
>
|
||||
{description}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{checked && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="h-5 w-5 text-indigo-500"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</RadioGroup.Option>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</fieldset>
|
||||
</RadioGroup>
|
||||
<a href="/">Link after</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Switch } from '@headlessui/react'
|
||||
|
||||
import { classNames } from '../../src/utils/class-names'
|
||||
|
||||
export default function Home() {
|
||||
let [state, setState] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex items-start justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<Switch.Group as="div" className="flex items-center space-x-4">
|
||||
<Switch.Label>Enable notifications</Switch.Label>
|
||||
|
||||
<Switch
|
||||
as="button"
|
||||
checked={state}
|
||||
onChange={setState}
|
||||
className={({ checked }) =>
|
||||
classNames(
|
||||
'relative inline-flex flex-shrink-0 h-6 border-2 border-transparent rounded-full cursor-pointer w-11 focus:outline-none focus:shadow-outline transition-colors ease-in-out duration-200',
|
||||
checked ? 'bg-indigo-600' : 'bg-gray-200'
|
||||
)
|
||||
}
|
||||
>
|
||||
{({ checked }) => (
|
||||
<>
|
||||
<span
|
||||
className={classNames(
|
||||
'inline-block w-5 h-5 bg-white rounded-full transform transition ease-in-out duration-200',
|
||||
checked ? 'translate-x-5' : 'translate-x-0'
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Switch>
|
||||
</Switch.Group>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Tab, Switch } from '@headlessui/react'
|
||||
|
||||
import { classNames } from '../../src/utils/class-names'
|
||||
|
||||
export default function Home() {
|
||||
let tabs = [
|
||||
{ name: 'My Account', content: 'Tab content for my account' },
|
||||
{ name: 'Company', content: 'Tab content for company', disabled: true },
|
||||
{ name: 'Team Members', content: 'Tab content for team members' },
|
||||
{ name: 'Billing', content: 'Tab content for billing' },
|
||||
]
|
||||
|
||||
let [manual, setManual] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-start w-screen h-full p-12 bg-gray-50 space-y-12">
|
||||
<Switch.Group as="div" className="flex items-center space-x-4">
|
||||
<Switch.Label>Manual keyboard activation</Switch.Label>
|
||||
|
||||
<Switch
|
||||
as="button"
|
||||
checked={manual}
|
||||
onChange={setManual}
|
||||
className={({ checked }) =>
|
||||
classNames(
|
||||
'relative inline-flex flex-shrink-0 h-6 border-2 border-transparent rounded-full cursor-pointer w-11 focus:outline-none focus:shadow-outline transition-colors ease-in-out duration-200',
|
||||
checked ? 'bg-indigo-600' : 'bg-gray-200'
|
||||
)
|
||||
}
|
||||
>
|
||||
{({ checked }) => (
|
||||
<span
|
||||
className={classNames(
|
||||
'inline-block w-5 h-5 bg-white rounded-full transform transition ease-in-out duration-200',
|
||||
checked ? 'translate-x-5' : 'translate-x-0'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Switch>
|
||||
</Switch.Group>
|
||||
|
||||
<Tab.Group className="flex flex-col max-w-3xl w-full" as="div" manual={manual}>
|
||||
<Tab.List className="relative z-0 rounded-lg shadow flex divide-x divide-gray-200">
|
||||
{tabs.map((tab, tabIdx) => (
|
||||
<Tab
|
||||
key={tab.name}
|
||||
disabled={tab.disabled}
|
||||
className={({ selected }) =>
|
||||
classNames(
|
||||
selected ? 'text-gray-900' : 'text-gray-500 hover:text-gray-700',
|
||||
tabIdx === 0 ? 'rounded-l-lg' : '',
|
||||
tabIdx === tabs.length - 1 ? 'rounded-r-lg' : '',
|
||||
tab.disabled && 'opacity-50',
|
||||
'group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-sm font-medium text-center hover:bg-gray-50 focus:z-10'
|
||||
)
|
||||
}
|
||||
>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span>{tab.name}</span>
|
||||
{tab.disabled && <small className="inline-block px-4 text-xs">(disabled)</small>}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
selected ? 'bg-indigo-500' : 'bg-transparent',
|
||||
'absolute inset-x-0 bottom-0 h-0.5'
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
|
||||
<Tab.Panels className="mt-4">
|
||||
{tabs.map(tab => (
|
||||
<Tab.Panel className="bg-white rounded-lg p-4 shadow" key={tab.name}>
|
||||
{tab.content}
|
||||
</Tab.Panel>
|
||||
))}
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import React, { useState } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Transition Component - Playground</title>
|
||||
</Head>
|
||||
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<Dropdown />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Dropdown() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="relative inline-block text-left">
|
||||
<div>
|
||||
<span className="rounded-md shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded={isOpen}
|
||||
onClick={() => setIsOpen(v => !v)}
|
||||
>
|
||||
Options
|
||||
<svg className="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
show={isOpen}
|
||||
enter="transition ease-out duration-75"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
className="absolute right-0 w-56 mt-2 origin-top-right rounded-md shadow-lg"
|
||||
>
|
||||
<div className="bg-white rounded-md shadow-xs">
|
||||
<div
|
||||
className="py-1"
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
aria-labelledby="options-menu"
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
className="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
|
||||
role="menuitem"
|
||||
>
|
||||
Account settings
|
||||
</a>
|
||||
<a
|
||||
href="/"
|
||||
className="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
|
||||
role="menuitem"
|
||||
>
|
||||
Support
|
||||
</a>
|
||||
<a
|
||||
href="/"
|
||||
className="block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
|
||||
role="menuitem"
|
||||
>
|
||||
License
|
||||
</a>
|
||||
<form method="POST" action="#">
|
||||
<button
|
||||
type="submit"
|
||||
className="block w-full px-4 py-2 text-sm leading-5 text-left text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
|
||||
role="menuitem"
|
||||
>
|
||||
Sign out
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
import React, { useRef, useState } from 'react'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
||||
export default function Home() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
function toggle() {
|
||||
setIsOpen(v => !v)
|
||||
}
|
||||
|
||||
let [email, setEmail] = useState('')
|
||||
let [events, setEvents] = useState([])
|
||||
let inputRef = useRef(null)
|
||||
|
||||
function addEvent(name) {
|
||||
setEvents(existing => [...existing, `${new Date().toJSON()} - ${name}`])
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex p-12 space-x-4">
|
||||
<div className="inline-block p-12">
|
||||
<span className="flex w-full mt-3 rounded-md shadow-sm sm:mt-0 sm:w-auto">
|
||||
<button
|
||||
onClick={toggle}
|
||||
type="button"
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-base font-medium leading-6 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md shadow-sm hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue sm:text-sm sm:leading-5"
|
||||
>
|
||||
Show modal
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul className="p-4 text-gray-900 bg-gray-200">
|
||||
<h3 className="font-bold">Events:</h3>
|
||||
{events.map((event, i) => (
|
||||
<li key={i} className="font-mono text-sm">
|
||||
{event}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
show={isOpen}
|
||||
className="fixed inset-0 z-10 overflow-y-auto"
|
||||
beforeEnter={() => {
|
||||
addEvent('Before enter')
|
||||
}}
|
||||
afterEnter={() => {
|
||||
inputRef.current.focus()
|
||||
addEvent('After enter')
|
||||
}}
|
||||
beforeLeave={() => {
|
||||
addEvent('Before leave (before confirm)')
|
||||
window.confirm('Are you sure?')
|
||||
addEvent('Before leave (after confirm)')
|
||||
}}
|
||||
afterLeave={() => {
|
||||
addEvent('After leave (before alert)')
|
||||
window.alert('Consider it done!')
|
||||
addEvent('After leave (after alert)')
|
||||
setEmail('')
|
||||
}}
|
||||
>
|
||||
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||
<Transition.Child
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 transition-opacity">
|
||||
<div className="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen"></span>​
|
||||
<Transition.Child
|
||||
className="inline-block overflow-hidden text-left align-bottom transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline"
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<div className="px-4 pt-5 pb-4 bg-white sm:p-6 sm:pb-4">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="flex items-center justify-center flex-shrink-0 w-12 h-12 mx-auto bg-red-100 rounded-full sm:mx-0 sm:h-10 sm:w-10">
|
||||
{/* Heroicon name: exclamation */}
|
||||
<svg
|
||||
className="w-6 h-6 text-red-600"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
||||
Deactivate account
|
||||
</h3>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm leading-5 text-gray-500">
|
||||
Are you sure you want to deactivate your account? All of your data will be
|
||||
permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium leading-5 text-gray-700"
|
||||
>
|
||||
Email address
|
||||
</label>
|
||||
<div className="relative mt-1 rounded-md shadow-sm">
|
||||
<input
|
||||
ref={inputRef}
|
||||
value={email}
|
||||
onChange={event => setEmail(event.target.value)}
|
||||
id="email"
|
||||
className="block w-full px-3 form-input sm:text-sm sm:leading-5"
|
||||
placeholder="name@example.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-4 py-3 bg-gray-50 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<span className="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-base font-medium leading-6 text-white transition duration-150 ease-in-out bg-red-600 border border-transparent rounded-md shadow-sm hover:bg-red-500 focus:outline-none focus:border-red-700 focus:shadow-outline-red sm:text-sm sm:leading-5"
|
||||
>
|
||||
Deactivate
|
||||
</button>
|
||||
</span>
|
||||
<span className="flex w-full mt-3 rounded-md shadow-sm sm:mt-0 sm:w-auto">
|
||||
<button
|
||||
onClick={toggle}
|
||||
type="button"
|
||||
className="inline-flex justify-center w-full px-4 py-2 text-base font-medium leading-6 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md shadow-sm hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue sm:text-sm sm:leading-5"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import React, { useState, ReactNode } from 'react'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
||||
export default function Home() {
|
||||
let [isOpen, setIsOpen] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="space-y-2 w-96">
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(v => !v)}
|
||||
className="inline-flex items-center px-3 py-2 text-sm font-medium leading-4 text-gray-700 transition bg-white border border-gray-300 rounded-md duration-150-out hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50"
|
||||
>
|
||||
{isOpen ? 'Hide' : 'Show'}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<Transition show={isOpen} unmount={false}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box />
|
||||
</Box>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Box({ children }: { children?: ReactNode }) {
|
||||
return (
|
||||
<Transition.Child
|
||||
unmount={false}
|
||||
enter="transition translate duration-300"
|
||||
enterFrom="transform -translate-x-full"
|
||||
enterTo="transform translate-x-0"
|
||||
leave="transition translate duration-300"
|
||||
leaveFrom="transform translate-x-0"
|
||||
leaveTo="transform translate-x-full"
|
||||
>
|
||||
<div className="p-4 space-y-2 text-sm font-semibold tracking-wide text-gray-700 uppercase bg-white rounded-md shadow">
|
||||
<span>This is a box</span>
|
||||
{children}
|
||||
</div>
|
||||
</Transition.Child>
|
||||
)
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import React, { useState, ReactNode } from 'react'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
||||
export default function Home() {
|
||||
let [isOpen, setIsOpen] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="space-y-2 w-96">
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(v => !v)}
|
||||
className="inline-flex items-center px-3 py-2 text-sm font-medium leading-4 text-gray-700 transition bg-white border border-gray-300 rounded-md duration-150-out hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50"
|
||||
>
|
||||
{isOpen ? 'Hide' : 'Show'}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<Transition show={isOpen} unmount={true}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box />
|
||||
</Box>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Box({ children }: { children?: ReactNode }) {
|
||||
return (
|
||||
<Transition.Child
|
||||
unmount={true}
|
||||
enter="transition translate duration-300"
|
||||
enterFrom="transform -translate-x-full"
|
||||
enterTo="transform translate-x-0"
|
||||
leave="transition translate duration-300"
|
||||
leaveFrom="transform translate-x-0"
|
||||
leaveTo="transform translate-x-full"
|
||||
>
|
||||
<div className="p-4 space-y-2 text-sm font-semibold tracking-wide text-gray-700 uppercase bg-white rounded-md shadow">
|
||||
<span>This is a box</span>
|
||||
{children}
|
||||
</div>
|
||||
</Transition.Child>
|
||||
)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
||||
export default function Home() {
|
||||
let [isOpen, setIsOpen] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div className="space-y-2 w-96">
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsOpen(v => !v)}
|
||||
className="inline-flex items-center px-3 py-2 text-sm font-medium leading-4 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50"
|
||||
>
|
||||
{isOpen ? 'Hide' : 'Show'}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<Transition
|
||||
show={isOpen}
|
||||
unmount={false}
|
||||
enter="transition ease-out duration-300"
|
||||
enterFrom="transform opacity-0"
|
||||
enterTo="transform opacity-100"
|
||||
leave="transition ease-in duration-300"
|
||||
leaveFrom="transform opacity-100"
|
||||
leaveTo="transform opacity-0"
|
||||
className="p-4 bg-white rounded-md shadow"
|
||||
>
|
||||
Contents to show and hide
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
-181
@@ -1,181 +0,0 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
||||
import { classNames } from '../../../src/utils/class-names'
|
||||
import { match } from '../../../src/utils/match'
|
||||
|
||||
export default function Shell() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Transition Component - Full Page Transition</title>
|
||||
</Head>
|
||||
<div className="h-full p-12 bg-gray-50">
|
||||
<div className="flex flex-col flex-1 h-full overflow-hidden rounded-lg shadow-lg">
|
||||
<FullPageTransition />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function usePrevious<T>(value: T) {
|
||||
let ref = useRef(value)
|
||||
useEffect(() => {
|
||||
ref.current = value
|
||||
}, [value])
|
||||
return ref.current
|
||||
}
|
||||
|
||||
enum Direction {
|
||||
Forwards = ' -> ',
|
||||
Backwards = ' <- ',
|
||||
}
|
||||
|
||||
let pages = ['Dashboard', 'Team', 'Projects', 'Calendar', 'Reports']
|
||||
let colors = [
|
||||
'bg-gradient-to-r from-teal-400 to-blue-400',
|
||||
'bg-gradient-to-r from-blue-400 to-orange-400',
|
||||
'bg-gradient-to-r from-orange-400 to-purple-400',
|
||||
'bg-gradient-to-r from-purple-400 to-green-400',
|
||||
'bg-gradient-to-r from-green-400 to-teal-400',
|
||||
]
|
||||
|
||||
function FullPageTransition() {
|
||||
let [activePage, setActivePage] = useState(0)
|
||||
let previousPage = usePrevious(activePage)
|
||||
|
||||
let direction = activePage > previousPage ? Direction.Forwards : Direction.Backwards
|
||||
|
||||
let transitions = match(direction, {
|
||||
[Direction.Forwards]: {
|
||||
enter: 'transition transform ease-in-out duration-500',
|
||||
enterFrom: 'translate-x-full',
|
||||
enterTo: 'translate-x-0',
|
||||
leave: 'transition transform ease-in-out duration-500',
|
||||
leaveFrom: 'translate-x-0',
|
||||
leaveTo: '-translate-x-full',
|
||||
},
|
||||
[Direction.Backwards]: {
|
||||
enter: 'transition transform ease-in-out duration-500',
|
||||
enterFrom: '-translate-x-full',
|
||||
enterTo: 'translate-x-0',
|
||||
leave: 'transition transform ease-in-out duration-500',
|
||||
leaveFrom: 'translate-x-0',
|
||||
leaveTo: 'translate-x-full',
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="pb-32 bg-gray-800">
|
||||
<nav className="bg-gray-800">
|
||||
<div className="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div className="border-b border-gray-700">
|
||||
<div className="flex items-center justify-between h-16 px-4 sm:px-0">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
<img
|
||||
className="w-8 h-8"
|
||||
src="https://tailwindui.com/img/logos/workflow-mark-on-dark.svg"
|
||||
alt="Workflow logo"
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<div className="flex items-baseline ml-10 space-x-4">
|
||||
{pages.map((page, i) => (
|
||||
<button
|
||||
key={page}
|
||||
onClick={() => setActivePage(i)}
|
||||
className={classNames(
|
||||
'px-3 py-2 text-sm font-medium rounded-md focus:outline-none focus:text-white focus:bg-gray-700',
|
||||
i === activePage
|
||||
? 'text-white bg-gray-900'
|
||||
: 'text-gray-300 hover:text-white hover:bg-gray-700'
|
||||
)}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<div className="flex items-center ml-4 md:ml-6">
|
||||
<button
|
||||
className="p-1 text-gray-400 border-2 border-transparent rounded-full hover:text-white focus:outline-none focus:text-white focus:bg-gray-700"
|
||||
aria-label="Notifications"
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Profile dropdown */}
|
||||
<div className="relative ml-3">
|
||||
<div>
|
||||
<button
|
||||
className="flex items-center max-w-xs text-sm text-white rounded-full focus:outline-none focus:shadow-solid"
|
||||
id="user-menu"
|
||||
aria-label="User menu"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<img
|
||||
className="w-8 h-8 rounded-full"
|
||||
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
alt=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<header className="py-10">
|
||||
<div className="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<h1 className="relative inline-block text-3xl font-bold leading-9 text-white">
|
||||
{pages[activePage]}
|
||||
</h1>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<main className="-mt-32">
|
||||
<div className="px-4 pb-12 mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div className="px-5 py-6 bg-white rounded-lg shadow sm:px-6">
|
||||
<div className="relative overflow-hidden rounded-lg h-96">
|
||||
{pages.map((page, i) => (
|
||||
<Transition
|
||||
appear={false}
|
||||
key={page}
|
||||
show={activePage === i}
|
||||
className={classNames(
|
||||
'absolute inset-0 p-8 text-3xl rounded-lg text-white font-bold',
|
||||
colors[i]
|
||||
)}
|
||||
{...transitions}
|
||||
>
|
||||
{page} page content
|
||||
</Transition>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { Transition } from '@headlessui/react'
|
||||
|
||||
export default function App() {
|
||||
let [mobileOpen, setMobileOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
function handleEscape(event) {
|
||||
if (!mobileOpen) return
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
setMobileOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keyup', handleEscape)
|
||||
return () => document.removeEventListener('keyup', handleEscape)
|
||||
}, [mobileOpen])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Transition Component - Layout with sidebar</title>
|
||||
</Head>
|
||||
|
||||
<div className="flex h-screen overflow-hidden bg-cool-gray-100">
|
||||
{/* Off-canvas menu for mobile */}
|
||||
<Transition show={mobileOpen} unmount={false} className="fixed inset-0 z-40 flex">
|
||||
{/* Off-canvas menu overlay, show/hide based on off-canvas menu state. */}
|
||||
<Transition.Child
|
||||
unmount={false}
|
||||
enter="transition-opacity ease-linear duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="transition-opacity ease-linear duration-300"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
{() => (
|
||||
<div className="fixed inset-0">
|
||||
<div
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="absolute inset-0 opacity-75 bg-cool-gray-600"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Transition.Child>
|
||||
|
||||
{/* Off-canvas menu, show/hide based on off-canvas menu state. */}
|
||||
<Transition.Child
|
||||
unmount={false}
|
||||
enter="transition ease-in-out duration-300 transform"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition ease-in-out duration-300 transform"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full"
|
||||
className="relative flex flex-col flex-1 w-full max-w-xs pt-5 pb-4 bg-teal-600"
|
||||
>
|
||||
<div className="absolute top-0 right-0 p-1 -mr-14">
|
||||
<Transition.Child
|
||||
unmount={false}
|
||||
className="flex items-center justify-center w-12 h-12 rounded-full focus:outline-none focus:bg-cool-gray-600"
|
||||
aria-label="Close sidebar"
|
||||
as="button"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6 text-white"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
<div className="flex items-center flex-shrink-0 px-4">
|
||||
<img
|
||||
className="w-auto h-8"
|
||||
src="https://tailwindui.com/img/logos/easywire-logo-on-brand.svg"
|
||||
alt="Easywire logo"
|
||||
/>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
<div className="flex-shrink-0 w-14">
|
||||
{/* Dummy element to force sidebar to shrink to fit close icon */}
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
{/* Static sidebar for desktop */}
|
||||
<div className="hidden lg:flex lg:flex-shrink-0">
|
||||
<div className="flex flex-col w-64">
|
||||
{/* Sidebar component, swap this element with another sidebar if you like */}
|
||||
<div className="flex flex-col flex-grow pt-5 pb-4 overflow-y-auto bg-teal-600">
|
||||
<div className="flex items-center flex-shrink-0 px-4">
|
||||
<img
|
||||
className="w-auto h-8"
|
||||
src="https://tailwindui.com/img/logos/easywire-logo-on-brand.svg"
|
||||
alt="Easywire logo"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto focus:outline-none" tabIndex={0}>
|
||||
<div className="relative z-10 flex flex-shrink-0 h-16 bg-white border-b border-gray-200 lg:border-none">
|
||||
<button
|
||||
className="px-4 border-r border-cool-gray-200 text-cool-gray-400 focus:outline-none focus:bg-cool-gray-100 focus:text-cool-gray-600 lg:hidden"
|
||||
aria-label="Open sidebar"
|
||||
onClick={() => setMobileOpen(true)}
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6 transition duration-150 ease-in-out"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4 6h16M4 12h8m-8 6h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/* Search bar */}
|
||||
<div className="flex justify-between flex-1 px-4 sm:px-6 lg:max-w-6xl lg:mx-auto lg:px-8">
|
||||
<div className="flex flex-1">
|
||||
<form className="flex w-full md:ml-0" action="#" method="GET">
|
||||
<label htmlFor="search_field" className="sr-only">
|
||||
Search
|
||||
</label>
|
||||
<div className="relative w-full text-cool-gray-400 focus-within:text-cool-gray-600">
|
||||
<div className="absolute inset-y-0 left-0 flex items-center pointer-events-none">
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="search_field"
|
||||
className="block w-full h-full py-2 pl-8 pr-3 rounded-md text-cool-gray-900 placeholder-cool-gray-500 focus:outline-none focus:placeholder-cool-gray-400 sm:text-sm"
|
||||
placeholder="Search"
|
||||
type="search"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main className="relative z-0 flex-1 p-8 overflow-y-auto">
|
||||
{/* Replace with your content */}
|
||||
<div className="border-4 border-gray-200 border-dashed rounded-lg h-96"></div>
|
||||
{/* /End replace */}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { RefCallback, useRef, useCallback, useMemo } from 'react'
|
||||
import { createPopper, Options } from '@popperjs/core'
|
||||
|
||||
/**
|
||||
* Example implementation to use Popper: https://popper.js.org/
|
||||
*/
|
||||
export function usePopper(
|
||||
options?: Partial<Options>
|
||||
): [RefCallback<Element | null>, RefCallback<HTMLElement | null>] {
|
||||
let reference = useRef<Element>(null)
|
||||
let popper = useRef<HTMLElement>(null)
|
||||
|
||||
let cleanupCallback = useRef(() => {})
|
||||
|
||||
let instantiatePopper = useCallback(() => {
|
||||
if (!reference.current) return
|
||||
if (!popper.current) return
|
||||
|
||||
if (cleanupCallback.current) cleanupCallback.current()
|
||||
|
||||
cleanupCallback.current = createPopper(reference.current, popper.current, options).destroy
|
||||
}, [reference, popper, cleanupCallback, options])
|
||||
|
||||
return useMemo(
|
||||
() => [
|
||||
referenceDomNode => {
|
||||
reference.current = referenceDomNode
|
||||
instantiatePopper()
|
||||
},
|
||||
popperDomNode => {
|
||||
popper.current = popperDomNode
|
||||
instantiatePopper()
|
||||
},
|
||||
],
|
||||
[reference, popper, instantiatePopper]
|
||||
)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
export type ExamplesType = {
|
||||
name: string
|
||||
path: string
|
||||
children?: ExamplesType[]
|
||||
}
|
||||
|
||||
export async function resolveAllExamples(...paths: string[]) {
|
||||
let base = path.resolve(process.cwd(), ...paths)
|
||||
|
||||
if (!fs.existsSync(base)) {
|
||||
return false
|
||||
}
|
||||
|
||||
let files = await fs.promises.readdir(base, { withFileTypes: true })
|
||||
let items: ExamplesType[] = []
|
||||
|
||||
for (let file of files) {
|
||||
// Skip reserved filenames from Next. E.g.: _app.tsx, _error.tsx
|
||||
if (file.name.startsWith('_')) {
|
||||
continue
|
||||
}
|
||||
|
||||
let bucket: ExamplesType = {
|
||||
name: file.name.replace(/-/g, ' ').replace(/\.tsx?/g, ''),
|
||||
path: [...paths, file.name]
|
||||
.join('/')
|
||||
.replace(/^pages/, '')
|
||||
.replace(/\.tsx?/g, '')
|
||||
.replace(/\/+/g, '/'),
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
let children = await resolveAllExamples(...paths, file.name)
|
||||
|
||||
if (children) {
|
||||
bucket.children = children
|
||||
}
|
||||
}
|
||||
|
||||
items.push(bucket)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../../postcss.config.js')
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../../tailwind.config.js')
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"routes": [{ "handle": "filesystem" }, { "src": "/(.*)", "status": 404, "dest": "/404.html" }]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
*.local
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Headless UI - Playground</title>
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||
</head>
|
||||
<body class="w-full h-full font-sans antialiased text-gray-900">
|
||||
<div class="w-full h-full" id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,96 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col h-screen overflow-hidden font-sans antialiased text-gray-900 bg-gray-700"
|
||||
>
|
||||
<header
|
||||
class="relative z-10 flex items-center justify-between flex-shrink-0 px-4 py-4 bg-gray-700 border-b border-gray-200 sm:px-6 lg:px-8"
|
||||
>
|
||||
<router-link to="/">
|
||||
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 243 42">
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M65.74 13.663c-2.62 0-4.702.958-5.974 2.95V6.499h-4.163V33.32h4.163V23.051c0-3.908 2.159-5.518 4.896-5.518 2.62 0 4.317 1.533 4.317 4.445V33.32h4.162V21.557c0-4.982-3.083-7.894-7.4-7.894zM79.936 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.674-10.116-6.052 0-10.176 4.407-10.176 10.078 0 5.748 4.124 10.078 10.484 10.078 3.778 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.925 1.341-2.66 2.376-4.972 2.376-3.084 0-5.512-1.533-6.168-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.506 0 4.934 1.418 5.512 4.713H79.898zM113.282 14.161v2.72c-1.465-1.992-3.739-3.218-6.746-3.218-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.162V14.16h-4.162zm-6.09 15.71c-3.469 0-6.091-2.567-6.091-6.13 0-3.564 2.622-6.131 6.091-6.131 3.469 0 6.09 2.567 6.09 6.13 0 3.564-2.621 6.132-6.09 6.132zM136.597 6.498v10.384c-1.465-1.993-3.739-3.219-6.746-3.219-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.163V6.497h-4.163zm-6.09 23.374c-3.469 0-6.09-2.568-6.09-6.131 0-3.564 2.621-6.131 6.09-6.131s6.09 2.567 6.09 6.13c0 3.564-2.621 6.132-6.09 6.132zM144.648 33.32h4.163V5.348h-4.163V33.32zM155.957 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.675-10.116-6.051 0-10.176 4.407-10.176 10.078 0 5.748 4.125 10.078 10.485 10.078 3.777 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.926 1.341-2.66 2.376-4.973 2.376-3.083 0-5.512-1.533-6.167-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.505 0 4.934 1.418 5.512 4.713h-11.332zM177.137 19.45c0-1.38 1.311-2.032 2.814-2.032 1.581 0 2.93.69 3.623 2.184l3.508-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.279-2.759l-3.584 2.07c1.233 2.758 4.008 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.369-4.98-10.369-8.468zM192.774 19.45c0-1.38 1.31-2.032 2.813-2.032 1.581 0 2.93.69 3.624 2.184l3.507-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.278-2.759l-3.585 2.07c1.233 2.758 4.009 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.368-4.98-10.368-8.468zM224.523 28.9c2.889 0 5.027-1.715 5.027-4.53v-8.782h-2.588v8.577c0 1.268-.676 2.219-2.439 2.219s-2.438-.951-2.438-2.22v-8.576h-2.569v8.782c0 2.815 2.138 4.53 5.007 4.53zM232.257 15.588V28.64h2.588V15.588h-2.588z"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fill-rule="evenodd"
|
||||
d="M233.817 9.328H220.42c-2.96 0-5.359 2.385-5.359 5.327v13.318c0 2.942 2.399 5.327 5.359 5.327h13.397c2.959 0 5.358-2.385 5.358-5.327V14.655c0-2.942-2.399-5.327-5.358-5.327zM220.42 6.664c-4.439 0-8.038 3.578-8.038 7.99v13.319c0 4.413 3.599 7.99 8.038 7.99h13.397c4.439 0 8.038-3.577 8.038-7.99V14.655c0-4.413-3.599-7.99-8.038-7.99H220.42z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fill-rule="evenodd"
|
||||
d="M220.42 9.328h13.397c2.959 0 5.358 2.385 5.358 5.327v13.318c0 2.942-2.399 5.327-5.358 5.327H220.42c-2.96 0-5.359-2.385-5.359-5.327V14.655c0-2.942 2.399-5.327 5.359-5.327zm-8.038 5.327c0-4.413 3.599-7.99 8.038-7.99h13.397c4.439 0 8.038 3.577 8.038 7.99v13.318c0 4.413-3.599 7.99-8.038 7.99H220.42c-4.439 0-8.038-3.577-8.038-7.99V14.655z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="url(#prefix__paint0_linear)"
|
||||
d="M8.577 26.097l25.779-8.556c-.514-3.201-.88-5.342-1.307-6.974-.457-1.756-.821-2.226-.965-2.39a5.026 5.026 0 00-1.81-1.306c-.2-.086-.762-.284-2.583-.175-1.924.116-4.453.507-8.455 1.137-4.003.63-6.529 1.035-8.395 1.516-1.766.456-2.239.817-2.403.96a4.999 4.999 0 00-1.315 1.8c-.085.198-.285.757-.175 2.568.116 1.913.51 4.426 1.143 8.405.178 1.114.337 2.113.486 3.015z"
|
||||
/>
|
||||
<path
|
||||
fill="url(#prefix__paint1_linear)"
|
||||
fill-rule="evenodd"
|
||||
d="M1.47 24.124C.244 16.427-.37 12.58.96 9.49A11.665 11.665 0 014.027 5.29c2.545-2.21 6.416-2.82 14.16-4.039C25.93.031 29.8-.578 32.907.743a11.729 11.729 0 014.225 3.05c2.223 2.53 2.836 6.38 4.063 14.076 1.226 7.698 1.84 11.546.511 14.636a11.666 11.666 0 01-3.069 4.199c-2.545 2.21-6.416 2.82-14.159 4.039-7.743 1.219-11.614 1.828-14.722.508a11.728 11.728 0 01-4.224-3.05C3.31 35.67 2.697 31.82 1.47 24.123zm13.657 13.668c2.074-.125 4.743-.54 8.697-1.163 3.953-.622 6.62-1.047 8.632-1.566 1.949-.502 2.846-.992 3.426-1.496a7.5 7.5 0 001.973-2.7c.302-.703.494-1.703.372-3.7-.125-2.063-.543-4.716-1.17-8.646-.625-3.93-1.053-6.582-1.574-8.582-.506-1.937-.999-2.83-1.505-3.405a7.54 7.54 0 00-2.716-1.961c-.707-.301-1.713-.492-3.723-.371-2.074.125-4.743.54-8.697 1.163-3.953.622-6.62 1.047-8.632 1.565-1.949.503-2.846.993-3.426 1.497a7.5 7.5 0 00-1.972 2.699c-.303.704-.495 1.704-.373 3.701.125 2.062.543 4.716 1.17 8.646.625 3.93 1.053 6.582 1.574 8.581.506 1.938 1 2.83 1.505 3.406a7.54 7.54 0 002.716 1.961c.707.3 1.713.492 3.723.37z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="prefix__paint0_linear"
|
||||
x1="16.759"
|
||||
x2="23.386"
|
||||
y1="0"
|
||||
y2="41.662"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#66E3FF" />
|
||||
<stop offset="1" stop-color="#7064F9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="prefix__paint1_linear"
|
||||
x1="16.759"
|
||||
x2="23.386"
|
||||
y1="0"
|
||||
y2="41.662"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#66E3FF" />
|
||||
<stop offset="1" stop-color="#7064F9" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</router-link>
|
||||
</header>
|
||||
<main class="flex-1 overflow-auto bg-gray-50">
|
||||
<router-view />
|
||||
<KeyCaster />
|
||||
|
||||
<!-- TODO: Position this in the correct spot -->
|
||||
<div
|
||||
v-if="sourceCode"
|
||||
class="container fixed bottom-0 left-0 right-0 hidden my-12 overflow-scroll rounded-md max-h-96"
|
||||
v-html="sourceCode"
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, watchEffect } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import KeyCaster from './KeyCaster.vue'
|
||||
import './.generated/preload.js'
|
||||
import source from './.generated/source.json'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
KeyCaster,
|
||||
},
|
||||
setup() {
|
||||
let route = useRoute()
|
||||
let sourceCode = computed(() => source[route.path])
|
||||
|
||||
return { sourceCode }
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,74 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="fixed z-50 px-4 py-2 overflow-hidden text-2xl tracking-wide text-blue-100 bg-blue-800 rounded-md shadow cursor-default pointer-events-none select-none right-4 bottom-4"
|
||||
v-if="keys.length > 0"
|
||||
>
|
||||
{{
|
||||
keys
|
||||
.slice()
|
||||
.reverse()
|
||||
.join(' ')
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
let isMac = navigator.userAgent.indexOf('Mac OS X') !== -1
|
||||
|
||||
let KeyDisplay = isMac
|
||||
? {
|
||||
ArrowUp: '↑',
|
||||
ArrowDown: '↓',
|
||||
ArrowLeft: '←',
|
||||
ArrowRight: '→',
|
||||
Home: '↖',
|
||||
End: '↘',
|
||||
Alt: '⌥',
|
||||
CapsLock: '⇪',
|
||||
Meta: '⌘',
|
||||
Shift: '⇧',
|
||||
Control: '⌃',
|
||||
Backspace: '⌫',
|
||||
Delete: '⌦',
|
||||
Enter: '↵',
|
||||
Escape: '⎋',
|
||||
Tab: '⇥',
|
||||
ShiftTab: '⇤',
|
||||
PageUp: '⇞',
|
||||
PageDown: '⇟',
|
||||
' ': '␣',
|
||||
}
|
||||
: {
|
||||
ArrowUp: '↑',
|
||||
ArrowDown: '↓',
|
||||
ArrowLeft: '←',
|
||||
ArrowRight: '→',
|
||||
Meta: 'Win',
|
||||
Control: 'Ctrl',
|
||||
Backspace: '⌫',
|
||||
Delete: 'Del',
|
||||
Escape: 'Esc',
|
||||
PageUp: 'PgUp',
|
||||
PageDown: 'PgDn',
|
||||
' ': '␣',
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
let keys = ref([])
|
||||
|
||||
window.addEventListener('keydown', event => {
|
||||
keys.value.unshift(
|
||||
event.shiftKey && event.key !== 'Shift'
|
||||
? KeyDisplay[`Shift${event.key}`] ?? event.key
|
||||
: KeyDisplay[event.key] ?? event.key
|
||||
)
|
||||
setTimeout(() => keys.value.pop(), 2000)
|
||||
})
|
||||
|
||||
return { keys }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,44 +0,0 @@
|
||||
<template>
|
||||
<div class="container my-24">
|
||||
<div class="prose">
|
||||
<h2>Examples</h2>
|
||||
<Examples :examples="examples" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import routes from '../routes.json'
|
||||
|
||||
let Examples = defineComponent({
|
||||
props: ['examples'],
|
||||
setup(props) {
|
||||
return () => {
|
||||
return h(
|
||||
'ul',
|
||||
props.examples.map(example =>
|
||||
h(
|
||||
'li',
|
||||
{ key: example.path },
|
||||
example.children
|
||||
? h('h3', { class: 'text-xl' }, example.name)
|
||||
: [h(RouterLink, { to: example.path }, () => example.name)],
|
||||
example.children && h(Examples, { examples: example.children })
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default {
|
||||
components: { Examples },
|
||||
setup() {
|
||||
return {
|
||||
examples: routes,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,238 +0,0 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleIsOpen()"
|
||||
class="m-12 px-4 py-2 text-base font-medium leading-6 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md shadow-sm hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue sm:text-sm sm:leading-5"
|
||||
>
|
||||
Toggle!
|
||||
</button>
|
||||
|
||||
<TransitionRoot :show="isOpen" as="template">
|
||||
<Dialog @close="setIsOpen">
|
||||
<div class="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
|
||||
>
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-75"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-75"
|
||||
leaveTo="opacity-0"
|
||||
entered="opacity-75"
|
||||
>
|
||||
<DialogOverlay className="fixed inset-0 bg-gray-500 transition-opacity" />
|
||||
</TransitionChild>
|
||||
|
||||
<TransitionChild
|
||||
enter="ease-out transform duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in transform duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<!-- This element is to trick the browser into centering the modal contents. -->
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
<div
|
||||
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
|
||||
>
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10"
|
||||
>
|
||||
<!-- Heroicon name: exclamation -->
|
||||
<svg
|
||||
class="h-6 w-6 text-red-600"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<DialogTitle as="h3" class="text-lg leading-6 font-medium text-gray-900">
|
||||
Deactivate account
|
||||
</DialogTitle>
|
||||
<div class="mt-2">
|
||||
<p class="text-sm text-gray-500">
|
||||
Are you sure you want to deactivate your account? All of your data will be
|
||||
permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
<div class="relative inline-block text-left mt-10">
|
||||
<Menu>
|
||||
<span class="rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
ref="trigger"
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Choose a reason</span>
|
||||
<svg
|
||||
class="w-5 h-5 ml-2 -mr-1"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<TransitionRoot
|
||||
enter="transition duration-300 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Portal>
|
||||
<MenuItems
|
||||
ref="container"
|
||||
class="z-20 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">
|
||||
tom@example.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" href="#account-settings" :className="resolveClass">
|
||||
Account settings
|
||||
</MenuItem>
|
||||
<MenuItem as="a" href="#support" :className="resolveClass">
|
||||
Support
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
as="a"
|
||||
disabled
|
||||
href="#new-feature"
|
||||
:className="resolveClass"
|
||||
>
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" href="#license" :className="resolveClass">
|
||||
License
|
||||
</MenuItem>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" href="#sign-out" :className="resolveClass">
|
||||
Sign out
|
||||
</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Portal>
|
||||
</TransitionRoot>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
type="button"
|
||||
@click="setIsOpen(false)"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:shadow-outline-red sm:ml-3 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Deactivate
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="setIsOpen(false)"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:shadow-outline-indigo sm:mt-0 sm:w-auto sm:text-sm"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</TransitionRoot>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, defineComponent } from 'vue'
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogOverlay,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems,
|
||||
MenuItem,
|
||||
Portal,
|
||||
TransitionRoot,
|
||||
TransitionChild,
|
||||
} from '@headlessui/vue'
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',
|
||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogOverlay,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems,
|
||||
MenuItem,
|
||||
Portal,
|
||||
TransitionRoot,
|
||||
TransitionChild,
|
||||
},
|
||||
setup() {
|
||||
let isOpen = ref(false)
|
||||
let [trigger, container] = usePopper({
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
|
||||
})
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
trigger,
|
||||
container,
|
||||
setIsOpen(value) {
|
||||
isOpen.value = value
|
||||
},
|
||||
toggleIsOpen() {
|
||||
isOpen.value = !isOpen.value
|
||||
},
|
||||
resolveClass,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,33 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="w-full max-w-xs mx-auto">
|
||||
<Disclosure>
|
||||
<DisclosureButton>Trigger</DisclosureButton>
|
||||
|
||||
<TransitionRoot
|
||||
enter="transition duration-1000 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
leave="transition duration-1000 ease-out"
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<DisclosurePanel class="p-4 bg-white mt-4">Content</DisclosurePanel>
|
||||
</TransitionRoot>
|
||||
</Disclosure>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Disclosure, DisclosureButton, DisclosurePanel, TransitionRoot } from '@headlessui/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Disclosure,
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
TransitionRoot,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="w-full max-w-xs mx-auto">
|
||||
<button>Previous</button>
|
||||
<FocusTrap>
|
||||
<button>Trigger</button>
|
||||
</FocusTrap>
|
||||
<button>After</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import { FocusTrap } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { FocusTrap },
|
||||
setup(props, context) {
|
||||
return {}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,126 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="w-full max-w-xs mx-auto">
|
||||
<div class="space-y-1">
|
||||
<Listbox v-model="active">
|
||||
<ListboxLabel class="block text-sm font-medium leading-5 text-gray-700"
|
||||
>Assigned to</ListboxLabel
|
||||
>
|
||||
|
||||
<div class="relative">
|
||||
<span class="inline-block w-full rounded-md shadow-sm">
|
||||
<ListboxButton
|
||||
class="relative w-full py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md cursor-default focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
||||
>
|
||||
<span class="block truncate">{{ active.name }}</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7 7l3-3 3 3m0 6l-3 3-3-3"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
</span>
|
||||
|
||||
<div class="absolute w-full mt-1 bg-white rounded-md shadow-lg">
|
||||
<ListboxOptions
|
||||
class="py-1 overflow-auto text-base leading-6 rounded-md shadow-xs max-h-60 focus:outline-none sm:text-sm sm:leading-5"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="person in people"
|
||||
:key="person.id"
|
||||
:value="person"
|
||||
:className="resolveListboxOptionClassName"
|
||||
:disabled="person.disabled"
|
||||
v-slot="{ active, selected }"
|
||||
>
|
||||
<span
|
||||
:class="
|
||||
classNames('block truncate', selected ? 'font-semibold' : 'font-normal')
|
||||
"
|
||||
>
|
||||
{{ person.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="
|
||||
classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-indigo-600'
|
||||
)
|
||||
"
|
||||
>
|
||||
<svg class="w-5 h-5" viewbox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</div>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import {
|
||||
Listbox,
|
||||
ListboxLabel,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
} from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Listbox, ListboxLabel, ListboxButton, ListboxOptions, ListboxOption },
|
||||
setup(props, context) {
|
||||
let people = [
|
||||
{ id: 1, name: 'Wade Cooper' },
|
||||
{ id: 2, name: 'Arlene Mccoy' },
|
||||
{ id: 3, name: 'Devon Webb' },
|
||||
{ id: 4, name: 'Tom Cook' },
|
||||
{ id: 5, name: 'Tanya Fox', disabled: true },
|
||||
{ id: 6, name: 'Hellen Schmidt' },
|
||||
{ id: 7, name: 'Caroline Schultz' },
|
||||
{ id: 8, name: 'Mason Heaney' },
|
||||
{ id: 9, name: 'Claudie Smitham' },
|
||||
{ id: 10, name: 'Emil Schaefer' },
|
||||
]
|
||||
|
||||
let active = ref(people[Math.floor(Math.random() * people.length)])
|
||||
|
||||
return {
|
||||
people,
|
||||
active,
|
||||
classNames,
|
||||
resolveListboxOptionClassName({ active, disabled }) {
|
||||
return classNames(
|
||||
'relative py-2 pl-3 cursor-default select-none pr-9 focus:outline-none',
|
||||
active ? 'text-white bg-indigo-600' : 'text-gray-900',
|
||||
disabled && 'bg-gray-50 text-gray-300'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,210 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 space-x-4 bg-gray-50">
|
||||
<div class="w-64">
|
||||
<div class="space-y-1">
|
||||
<Listbox v-model="active">
|
||||
<ListboxLabel class="block text-sm font-medium leading-5 text-gray-700"
|
||||
>Assigned to</ListboxLabel
|
||||
>
|
||||
|
||||
<div class="relative">
|
||||
<span class="inline-block w-full rounded-md shadow-sm">
|
||||
<ListboxButton
|
||||
class="relative w-full py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md cursor-default focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
||||
>
|
||||
<span class="block truncate">{{ active.name }}</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7 7l3-3 3 3m0 6l-3 3-3-3"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
</span>
|
||||
|
||||
<div class="absolute w-full mt-1 bg-white rounded-md shadow-lg">
|
||||
<ListboxOptions
|
||||
class="py-1 overflow-auto text-base leading-6 rounded-md shadow-xs max-h-60 focus:outline-none sm:text-sm sm:leading-5"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="person in people"
|
||||
:key="person.id"
|
||||
:value="person"
|
||||
:className="resolveListboxOptionClassName"
|
||||
v-slot="{ active, selected }"
|
||||
>
|
||||
<span
|
||||
:class="
|
||||
classNames('block truncate', selected ? 'font-semibold' : 'font-normal')
|
||||
"
|
||||
>
|
||||
{{ person.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="
|
||||
classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-indigo-600'
|
||||
)
|
||||
"
|
||||
>
|
||||
<svg class="w-5 h-5" viewbox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</div>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium leading-5 text-gray-700">
|
||||
Email
|
||||
</label>
|
||||
<div class="relative mt-1 rounded-md shadow-sm">
|
||||
<input
|
||||
class="block w-full form-input sm:text-sm sm:leading-5"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-64">
|
||||
<div class="space-y-1">
|
||||
<Listbox v-model="active">
|
||||
<ListboxLabel class="block text-sm font-medium leading-5 text-gray-700"
|
||||
>Assigned to</ListboxLabel
|
||||
>
|
||||
|
||||
<div class="relative">
|
||||
<span class="inline-block w-full rounded-md shadow-sm">
|
||||
<ListboxButton
|
||||
class="relative w-full py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md cursor-default focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
||||
>
|
||||
<span class="block truncate">{{ active.name }}</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7 7l3-3 3 3m0 6l-3 3-3-3"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
</span>
|
||||
|
||||
<div class="absolute w-full mt-1 bg-white rounded-md shadow-lg">
|
||||
<ListboxOptions
|
||||
class="py-1 overflow-auto text-base leading-6 rounded-md shadow-xs max-h-60 focus:outline-none sm:text-sm sm:leading-5"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="person in people"
|
||||
:key="person.id"
|
||||
:value="person"
|
||||
:className="resolveListboxOptionClassName"
|
||||
v-slot="{ active, selected }"
|
||||
>
|
||||
<span
|
||||
:class="
|
||||
classNames('block truncate', selected ? 'font-semibold' : 'font-normal')
|
||||
"
|
||||
>
|
||||
{{ person.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="
|
||||
classNames(
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
active ? 'text-white' : 'text-indigo-600'
|
||||
)
|
||||
"
|
||||
>
|
||||
<svg class="w-5 h-5" viewbox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</div>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import {
|
||||
Listbox,
|
||||
ListboxLabel,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
} from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Listbox, ListboxLabel, ListboxButton, ListboxOptions, ListboxOption },
|
||||
setup(props, context) {
|
||||
let people = [
|
||||
{ id: 1, name: 'Wade Cooper' },
|
||||
{ id: 2, name: 'Arlene Mccoy' },
|
||||
{ id: 3, name: 'Devon Webb' },
|
||||
{ id: 4, name: 'Tom Cook' },
|
||||
{ id: 5, name: 'Tanya Fox' },
|
||||
{ id: 6, name: 'Hellen Schmidt' },
|
||||
{ id: 7, name: 'Caroline Schultz' },
|
||||
{ id: 8, name: 'Mason Heaney' },
|
||||
{ id: 9, name: 'Claudie Smitham' },
|
||||
{ id: 10, name: 'Emil Schaefer' },
|
||||
]
|
||||
|
||||
let active = ref(people[Math.floor(Math.random() * people.length)])
|
||||
|
||||
return {
|
||||
people,
|
||||
active,
|
||||
classNames,
|
||||
resolveListboxOptionClassName({ active }) {
|
||||
return classNames(
|
||||
'relative py-2 pl-3 cursor-default select-none pr-9 focus:outline-none',
|
||||
active ? 'text-white bg-indigo-600' : 'text-gray-900'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,86 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="inline-block mt-64 text-left">
|
||||
<Menu>
|
||||
<span class="inline-flex rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
ref="trigger"
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<teleport to="body">
|
||||
<MenuItems
|
||||
ref="container"
|
||||
class="absolute right-0 w-56 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" href="#account-settings" :className="resolveClass">
|
||||
Account settings
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="data">
|
||||
<a href="#support" :class="resolveClass(data)">Support</a>
|
||||
</MenuItem>
|
||||
<MenuItem as="a" disabled href="#new-feature" :className="resolveClass">
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" href="#license" :className="resolveClass">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" href="#sign-out" :className="resolveClass">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</teleport>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Menu, MenuButton, MenuItems, MenuItem },
|
||||
setup(props, context) {
|
||||
let [trigger, container] = usePopper({
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
|
||||
})
|
||||
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',
|
||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
trigger,
|
||||
container,
|
||||
resolveClass,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="relative inline-block mt-64 text-left">
|
||||
<Menu>
|
||||
<span class="inline-flex rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
ref="trigger"
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<div ref="container" class="w-56">
|
||||
<transition
|
||||
enter-active-class="transition duration-100 ease-out"
|
||||
enter-from-class="transform scale-95 opacity-0"
|
||||
enter-to-class="transform scale-100 opacity-100"
|
||||
leave-active-class="transition duration-75 ease-out"
|
||||
leave-from-class="transform scale-100 opacity-100"
|
||||
leave-to-class="transform scale-95 opacity-0"
|
||||
>
|
||||
<MenuItems
|
||||
class="w-full bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#account-settings">
|
||||
Account settings
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="data">
|
||||
<a href="#support" :class="resolveClass(data)">Support</a>
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" disabled href="#new-feature">
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#license">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#sign-out">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</transition>
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Menu, MenuButton, MenuItems, MenuItem },
|
||||
setup(props, context) {
|
||||
let [trigger, container] = usePopper({
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
|
||||
})
|
||||
|
||||
function resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',
|
||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
trigger,
|
||||
container,
|
||||
resolveClass,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span class="rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<transition
|
||||
enter-active-class="transition duration-100 ease-out"
|
||||
enter-from-class="transform scale-95 opacity-0"
|
||||
enter-to-class="transform scale-100 opacity-100"
|
||||
leave-active-class="transition duration-75 ease-out"
|
||||
leave-from-class="transform scale-100 opacity-100"
|
||||
leave-to-class="transform scale-95 opacity-0"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" href="#account-settings" :className="resolveClass">
|
||||
Account settings
|
||||
</MenuItem>
|
||||
<MenuItem as="a" href="#support" :className="resolveClass">Support</MenuItem>
|
||||
<MenuItem as="a" disabled href="#new-feature" :className="resolveClass">
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" href="#license" :className="resolveClass">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" href="#sign-out" :className="resolveClass">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</transition>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems,
|
||||
MenuItem,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',
|
||||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span class="rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<MenuItems
|
||||
class="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<CustomMenuItem href="#account-settings">Account settings</CustomMenuItem>
|
||||
<CustomMenuItem href="#support">Support</CustomMenuItem>
|
||||
<CustomMenuItem disabled href="#new-feature">New feature (soon)</CustomMenuItem>
|
||||
<CustomMenuItem href="#license">License</CustomMenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<CustomMenuItem href="#sign-out">Sign out</CustomMenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
let CustomMenuItem = defineComponent({
|
||||
components: { Menu, MenuButton, MenuItems, MenuItem },
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return h(MenuItem, ({ active, disabled }) => {
|
||||
return h(
|
||||
'a',
|
||||
{
|
||||
class: classNames(
|
||||
'flex justify-between w-full text-left px-4 py-2 text-sm leading-5',
|
||||
active ? 'bg-indigo-500 text-white' : 'text-gray-700',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
),
|
||||
},
|
||||
[
|
||||
h('span', { class: classNames(active && 'font-bold') }, slots.default()),
|
||||
h('kbd', { class: classNames('font-sans', active && 'text-indigo-50') }, '⌘K'),
|
||||
]
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems,
|
||||
MenuItem,
|
||||
CustomMenuItem,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,125 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 space-x-4 bg-gray-50">
|
||||
<div class="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span class="rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<MenuItems
|
||||
class="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#account-settings"
|
||||
>Account settings</MenuItem
|
||||
>
|
||||
<MenuItem as="a" :className="resolveClass" href="#support">Support</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" disabled href="#new-feature"
|
||||
>New feature (soon)</MenuItem
|
||||
>
|
||||
<MenuItem as="a" :className="resolveClass" href="#license">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#sign-out">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="relative rounded-md shadow-sm">
|
||||
<input
|
||||
class="block w-full form-input sm:text-sm sm:leading-5"
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span class="rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<MenuItems
|
||||
class="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#account-settings"
|
||||
>Account settings</MenuItem
|
||||
>
|
||||
<MenuItem as="a" :className="resolveClass" href="#support">Support</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" disabled href="#new-feature"
|
||||
>New feature (soon)</MenuItem
|
||||
>
|
||||
<MenuItem as="a" :className="resolveClass" href="#license">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#sign-out">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems,
|
||||
MenuItem,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
resolveClass({ active, disabled }) {
|
||||
return classNames(
|
||||
'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',
|
||||
active && 'bg-gray-100 text-gray-900',
|
||||
disabled && 'cursor-not-allowed opacity-50'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,146 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center items-center space-x-12 p-12">
|
||||
<button>Previous</button>
|
||||
|
||||
<PopoverGroup as="nav" ar-label="Mythical University" class="flex space-x-3">
|
||||
<Popover as="div" class="relative">
|
||||
<transition
|
||||
enter-active-class="transition ease-out duration-300 transform"
|
||||
enter-from-class="opacity-0"
|
||||
enter-to-class="opacity-100"
|
||||
leave-active-class="transition ease-in duration-300 transform"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<PopoverOverlay
|
||||
class="bg-opacity-75 bg-gray-500 fixed inset-0 z-20"
|
||||
></PopoverOverlay>
|
||||
</transition>
|
||||
|
||||
<PopoverButton
|
||||
class="px-3 py-2 bg-gray-300 border-2 border-transparent focus:outline-none focus:border-blue-900 relative z-30"
|
||||
>Normal</PopoverButton
|
||||
>
|
||||
<PopoverPanel class="absolute flex flex-col w-64 bg-gray-100 border-2 border-blue-900 z-30">
|
||||
<a
|
||||
v-for="(link, i) of links"
|
||||
:hidden="i === 2"
|
||||
href="/"
|
||||
class="px-3 py-2 border-2 border-transparent hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:border-blue-900"
|
||||
>
|
||||
Normal - {{ link }}
|
||||
</a>
|
||||
</PopoverPanel>
|
||||
</Popover>
|
||||
|
||||
<Popover as="div" class="relative">
|
||||
<PopoverButton
|
||||
class="px-3 py-2 bg-gray-300 border-2 border-transparent focus:outline-none focus:border-blue-900"
|
||||
>Focus</PopoverButton
|
||||
>
|
||||
<PopoverPanel
|
||||
focus
|
||||
class="absolute flex flex-col w-64 bg-gray-100 border-2 border-blue-900"
|
||||
>
|
||||
<a
|
||||
v-for="(link, i) of links"
|
||||
href="/"
|
||||
class="px-3 py-2 border-2 border-transparent hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:border-blue-900"
|
||||
>
|
||||
Focus - {{ link }}
|
||||
</a>
|
||||
</PopoverPanel>
|
||||
</Popover>
|
||||
|
||||
<Popover as="div" class="relative">
|
||||
<PopoverButton
|
||||
ref="trigger1"
|
||||
class="px-3 py-2 bg-gray-300 border-2 border-transparent focus:outline-none focus:border-blue-900"
|
||||
>Portal</PopoverButton
|
||||
>
|
||||
<Portal>
|
||||
<PopoverPanel
|
||||
ref="container1"
|
||||
class="flex flex-col w-64 bg-gray-100 border-2 border-blue-900"
|
||||
>
|
||||
<a
|
||||
v-for="(link, i) of links"
|
||||
href="/"
|
||||
class="px-3 py-2 border-2 border-transparent hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:border-blue-900"
|
||||
>
|
||||
Portal - {{ link }}
|
||||
</a>
|
||||
</PopoverPanel>
|
||||
</Portal>
|
||||
</Popover>
|
||||
|
||||
<Popover as="div" class="relative">
|
||||
<PopoverButton
|
||||
ref="trigger2"
|
||||
class="px-3 py-2 bg-gray-300 border-2 border-transparent focus:outline-none focus:border-blue-900"
|
||||
>Focus in portal</PopoverButton
|
||||
>
|
||||
<Portal>
|
||||
<PopoverPanel
|
||||
ref="container2"
|
||||
focus
|
||||
class="flex flex-col w-64 bg-gray-100 border-2 border-blue-900"
|
||||
>
|
||||
<a
|
||||
v-for="(link, i) of links"
|
||||
href="/"
|
||||
class="px-3 py-2 border-2 border-transparent hover:bg-gray-200 focus:bg-gray-200 focus:outline-none focus:border-blue-900"
|
||||
>
|
||||
Focus in Portal - {{ link }}
|
||||
</a>
|
||||
</PopoverPanel>
|
||||
</Portal>
|
||||
</Popover>
|
||||
</PopoverGroup>
|
||||
|
||||
<button>Next</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import {
|
||||
Popover,
|
||||
PopoverOverlay,
|
||||
PopoverPanel,
|
||||
PopoverGroup,
|
||||
PopoverButton,
|
||||
Portal,
|
||||
} from '@headlessui/vue'
|
||||
import { usePopper } from '../../playground-utils/hooks/use-popper'
|
||||
|
||||
function html(templates) {
|
||||
return templates.join('')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Popover,
|
||||
PopoverPanel,
|
||||
PopoverOverlay,
|
||||
PopoverGroup,
|
||||
PopoverButton,
|
||||
Portal,
|
||||
},
|
||||
setup() {
|
||||
let links = ['First', 'Second', 'Third', 'Fourth']
|
||||
|
||||
let [trigger1, container1] = usePopper({
|
||||
placement: 'bottom-start',
|
||||
strategy: 'fixed',
|
||||
})
|
||||
|
||||
let [trigger2, container2] = usePopper({
|
||||
placement: 'bottom-start',
|
||||
strategy: 'fixed',
|
||||
})
|
||||
|
||||
return { links, trigger1, container1, trigger2, container2 }
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,37 +0,0 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="w-full max-w-xs mx-auto">
|
||||
<main>
|
||||
<aside ref="container" id="group-1">
|
||||
A
|
||||
</aside>
|
||||
|
||||
<PortalGroup :target="container">
|
||||
<section id="group-2">
|
||||
<span>B</span>
|
||||
</section>
|
||||
<Portal>Next to A</Portal>
|
||||
</PortalGroup>
|
||||
|
||||
<Portal>I am in the portal root</Portal>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import { Portal, PortalGroup } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Portal, PortalGroup },
|
||||
setup(props, context) {
|
||||
let container = ref(null)
|
||||
return { container }
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,104 +0,0 @@
|
||||
<template>
|
||||
<div class="p-12 max-w-xl">
|
||||
<a href="/">Link before</a>
|
||||
<RadioGroup v-model="active">
|
||||
<fieldset class="space-y-4">
|
||||
<legend>
|
||||
<h2 class="text-xl">Privacy setting</h2>
|
||||
</legend>
|
||||
|
||||
<div class="bg-white rounded-md -space-y-px">
|
||||
<RadioGroupOption
|
||||
v-for="({ id, name, description }, i) in access"
|
||||
:key="id"
|
||||
:value="id"
|
||||
v-slot="{ active, checked }"
|
||||
:className="
|
||||
({ active }) =>
|
||||
classNames(
|
||||
// Rounded corners
|
||||
i === 0 && 'rounded-tl-md rounded-tr-md',
|
||||
access.length - 1 === i && 'rounded-bl-md rounded-br-md',
|
||||
|
||||
// Shared
|
||||
'relative border p-4 flex focus:outline-none',
|
||||
active ? 'bg-indigo-50 border-indigo-200 z-10' : 'border-gray-200'
|
||||
)
|
||||
"
|
||||
>
|
||||
<div class="flex justify-between items-center w-full">
|
||||
<div class="ml-3 flex flex-col cursor-pointer">
|
||||
<span
|
||||
:class="[
|
||||
'block text-sm leading-5 font-medium',
|
||||
active ? 'text-indigo-900' : 'text-gray-900',
|
||||
]"
|
||||
>
|
||||
{{ name }}
|
||||
</span>
|
||||
<span
|
||||
:class="['block text-sm leading-5', active ? 'text-indigo-700' : 'text-gray-500']"
|
||||
>
|
||||
{{ description }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<svg
|
||||
v-if="checked"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
class="h-5 w-5 text-indigo-500"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroupOption>
|
||||
</div>
|
||||
</fieldset>
|
||||
</RadioGroup>
|
||||
<a href="/">Link after</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from 'vue'
|
||||
import { RadioGroup, RadioGroupOption } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean)
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { RadioGroup, RadioGroupOption },
|
||||
setup() {
|
||||
let active = ref()
|
||||
let access = ref([
|
||||
{
|
||||
id: 'access-1',
|
||||
name: 'Public access',
|
||||
description: 'This project would be available to anyone who has the link',
|
||||
},
|
||||
{
|
||||
id: 'access-2',
|
||||
name: 'Private to Project Members',
|
||||
description: 'Only members of this project would be able to access',
|
||||
},
|
||||
{
|
||||
id: 'access-3',
|
||||
name: 'Private to you',
|
||||
description: 'You are the only one able to access this project',
|
||||
},
|
||||
])
|
||||
|
||||
return { active, access, classNames }
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,40 +0,0 @@
|
||||
<template>
|
||||
<div class="flex items-start justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<SwitchGroup as="div" class="flex items-center space-x-4">
|
||||
<SwitchLabel>Enable notifications</SwitchLabel>
|
||||
|
||||
<Switch as="button" v-model="state" :className="resolveSwitchClass" v-slot="{ checked }">
|
||||
<span
|
||||
class="inline-block w-5 h-5 transition duration-200 ease-in-out transform bg-white rounded-full"
|
||||
:class="{ 'translate-x-5': checked, 'translate-x-0': !checked }"
|
||||
/>
|
||||
</Switch>
|
||||
</SwitchGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import { SwitchGroup, Switch, SwitchLabel } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { SwitchGroup, Switch, SwitchLabel },
|
||||
setup(props, context) {
|
||||
let state = ref(false)
|
||||
|
||||
return {
|
||||
state,
|
||||
resolveSwitchClass({ checked }) {
|
||||
return classNames(
|
||||
'relative inline-flex flex-shrink-0 h-6 transition-colors duration-200 ease-in-out border-2 border-transparent rounded-full cursor-pointer w-11 focus:outline-none focus:shadow-outline',
|
||||
checked ? 'bg-indigo-600' : 'bg-gray-200'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<div class="flex flex-col items-start w-screen h-full p-12 bg-gray-50 space-y-12">
|
||||
<SwitchGroup as="div" class="flex items-center space-x-4">
|
||||
<SwitchLabel>Manual keyboard activation</SwitchLabel>
|
||||
|
||||
<Switch as="button" v-model="manual" :className="resolveSwitchClass" v-slot="{ checked }">
|
||||
<span
|
||||
class="inline-block w-5 h-5 transition duration-200 ease-in-out transform bg-white rounded-full"
|
||||
:class="{ 'translate-x-5': checked, 'translate-x-0': !checked }"
|
||||
/>
|
||||
</Switch>
|
||||
</SwitchGroup>
|
||||
|
||||
<TabGroup class="flex flex-col max-w-3xl w-full" as="div" :manual="manual">
|
||||
<TabList class="relative z-0 rounded-lg shadow flex divide-x divide-gray-200">
|
||||
<Tab
|
||||
v-for="(tab, tabIdx) in tabs"
|
||||
:key="tab.name"
|
||||
:disabled="tab.disabled"
|
||||
class="group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-sm font-medium text-center hover:bg-gray-50 focus:z-10"
|
||||
:class="{
|
||||
'text-gray-900': selected,
|
||||
'text-gray-500 hover:text-gray-700': !selected,
|
||||
'rounded-l-lg': tabIdx === 0,
|
||||
'rounded-r-lg': tabIdx === tabs.length - 1,
|
||||
'opacity-50': tab.disabled,
|
||||
}"
|
||||
v-slot="{ selected }"
|
||||
>
|
||||
<span>{{tab.name}}</span>
|
||||
<small v-if="tab.disabled" class="inline-block px-4 text-xs">(disabled)</small>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="absolute inset-x-0 bottom-0 h-0.5"
|
||||
:class="{'bg-indigo-500': selected, 'bg-transparent':!selected}"
|
||||
/>
|
||||
</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanels class="mt-4">
|
||||
<TabPanel v-for="tab in tabs" class="bg-white rounded-lg p-4 shadow" key="tab.name">
|
||||
{{tab.content}}
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import { SwitchGroup, Switch, SwitchLabel, TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
let tabs = [
|
||||
{ name: 'My Account', content: 'Tab content for my account' },
|
||||
{ name: 'Company', content: 'Tab content for company', disabled: true },
|
||||
{ name: 'Team Members', content: 'Tab content for team members' },
|
||||
{ name: 'Billing', content: 'Tab content for billing' },
|
||||
]
|
||||
|
||||
export default {
|
||||
components: { SwitchGroup, Switch, SwitchLabel, TabGroup, TabList, Tab, TabPanels, TabPanel },
|
||||
setup(props, context) {
|
||||
let manual = ref(false)
|
||||
|
||||
return {
|
||||
tabs: ref(tabs),
|
||||
manual,
|
||||
resolveSwitchClass({ checked }) {
|
||||
return classNames(
|
||||
'relative inline-flex flex-shrink-0 h-6 transition-colors duration-200 ease-in-out border-2 border-transparent rounded-full cursor-pointer w-11 focus:outline-none focus:shadow-outline',
|
||||
checked ? 'bg-indigo-600' : 'bg-gray-200'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1,9 +0,0 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import 'tailwindcss/tailwind.css'
|
||||
|
||||
createApp(App)
|
||||
.use(router)
|
||||
.mount('#app')
|
||||
@@ -1,26 +0,0 @@
|
||||
import { ref, onMounted, watchEffect } from 'vue'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
|
||||
export function usePopper(options) {
|
||||
let reference = ref(null)
|
||||
let popper = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
watchEffect(onInvalidate => {
|
||||
if (!popper.value) return
|
||||
if (!reference.value) return
|
||||
|
||||
let popperEl = popper.value.el || popper.value
|
||||
let referenceEl = reference.value.el || reference.value
|
||||
|
||||
if (!(referenceEl instanceof HTMLElement)) return
|
||||
if (!(popperEl instanceof HTMLElement)) return
|
||||
|
||||
let { destroy } = createPopper(referenceEl, popperEl, options)
|
||||
|
||||
onInvalidate(destroy)
|
||||
})
|
||||
})
|
||||
|
||||
return [reference, popper]
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { createWebHistory, createRouter, RouterView } from 'vue-router'
|
||||
import lookup from './.generated/preload.js'
|
||||
import routes from './routes.json'
|
||||
|
||||
function buildRoutes(routes) {
|
||||
return routes.map(route => {
|
||||
let definition = {
|
||||
path: route.path,
|
||||
component: route.component ? lookup[route.component] : RouterView,
|
||||
}
|
||||
|
||||
if (route.children) {
|
||||
definition.children = buildRoutes(route.children)
|
||||
}
|
||||
|
||||
return definition
|
||||
})
|
||||
}
|
||||
|
||||
export default createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: buildRoutes(routes),
|
||||
})
|
||||
@@ -1,141 +0,0 @@
|
||||
[
|
||||
{
|
||||
"path": "/",
|
||||
"component": "./components/Home.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu",
|
||||
"path": "/menu",
|
||||
"children": [
|
||||
{
|
||||
"name": "Menu (basic)",
|
||||
"path": "/menu/menu",
|
||||
"component": "./components/menu/menu.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu with Popper",
|
||||
"path": "/menu/menu-with-popper",
|
||||
"component": "./components/menu/menu-with-popper.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu with Transition",
|
||||
"path": "/menu/menu-with-transition",
|
||||
"component": "./components/menu/menu-with-transition.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu with Popper + Transition",
|
||||
"path": "/menu/menu-with-transition-and-popper",
|
||||
"component": "./components/menu/menu-with-transition-and-popper.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu multiple elements",
|
||||
"path": "/menu/multiple-elements",
|
||||
"component": "./components/menu/multiple-elements.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Listbox",
|
||||
"path": "/listbox",
|
||||
"children": [
|
||||
{
|
||||
"name": "Listbox (basic)",
|
||||
"path": "/listbox/listbox",
|
||||
"component": "./components/listbox/listbox.vue"
|
||||
},
|
||||
{
|
||||
"name": "Listbox multiple elements",
|
||||
"path": "/listbox/multiple-elements",
|
||||
"component": "./components/listbox/multiple-elements.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Switch",
|
||||
"path": "/switch",
|
||||
"children": [
|
||||
{
|
||||
"name": "Switch (basic)",
|
||||
"path": "/switch/switch",
|
||||
"component": "./components/switch/switch.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Tabs",
|
||||
"path": "/tabs",
|
||||
"children": [
|
||||
{
|
||||
"name": "Tabs (basic)",
|
||||
"path": "/tabs/tabs",
|
||||
"component": "./components/tabs/tabs.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Focus Trap",
|
||||
"path": "/focus-trap",
|
||||
"children": [
|
||||
{
|
||||
"name": "FocusTrap (basic)",
|
||||
"path": "/focus-trap/focus-trap",
|
||||
"component": "./components/focus-trap/focus-trap.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Portal",
|
||||
"path": "/portal",
|
||||
"children": [
|
||||
{
|
||||
"name": "Portal (group)",
|
||||
"path": "/portal/portal",
|
||||
"component": "./components/portal/portal.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Popover",
|
||||
"path": "/popover",
|
||||
"children": [
|
||||
{
|
||||
"name": "Popover",
|
||||
"path": "/popover/popover",
|
||||
"component": "./components/popover/popover.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Disclosure",
|
||||
"path": "/disclosure",
|
||||
"children": [
|
||||
{
|
||||
"name": "Disclosure",
|
||||
"path": "/disclosure/disclosure",
|
||||
"component": "./components/disclosure/disclosure.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Dialog",
|
||||
"path": "/dialog",
|
||||
"children": [
|
||||
{
|
||||
"name": "Dialog",
|
||||
"path": "/dialog/dialog",
|
||||
"component": "./components/dialog/dialog.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "RadioGroup",
|
||||
"path": "/radio-group",
|
||||
"children": [
|
||||
{
|
||||
"name": "RadioGroup",
|
||||
"path": "/radio-group/radio-group",
|
||||
"component": "./components/radio-group/radio-group.vue"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -23,8 +23,6 @@
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"playground": "vite --config ./vite.config.js serve examples",
|
||||
"playground:build": "NODE_ENV=production vite build examples",
|
||||
"prepublishOnly": "npm run build",
|
||||
"build": "../../scripts/build.sh",
|
||||
"watch": "../../scripts/watch.sh",
|
||||
@@ -35,13 +33,8 @@
|
||||
"vue": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@popperjs/core": "^2.5.3",
|
||||
"@testing-library/vue": "^5.1.0",
|
||||
"@types/debounce": "^1.2.0",
|
||||
"@vue/compiler-sfc": "3.0.1",
|
||||
"@vue/test-utils": "^2.0.0-beta.7",
|
||||
"vite": "^1.0.0-rc.4",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0-beta.13"
|
||||
"vue": "3.0.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('../../tailwind.config.js')
|
||||
@@ -1,122 +0,0 @@
|
||||
let fs = require('fs')
|
||||
let path = require('path')
|
||||
|
||||
let prettier = require('prettier')
|
||||
let Prism = require('prismjs')
|
||||
require('prismjs/plugins/custom-class/prism-custom-class')
|
||||
|
||||
let routes = require('./examples/src/routes')
|
||||
|
||||
function flatten(routes, resolver) {
|
||||
return routes
|
||||
.map(route => (route.children ? flatten(route.children, resolver) : resolver(route)))
|
||||
.flat(Infinity)
|
||||
}
|
||||
|
||||
// This is a hack, but the idea is that we want to import all the examples from the routes.json
|
||||
// file. However just doing dynamic imports() doesn't work well at build time. Therefore we will
|
||||
// generate a fake file that contains them all.
|
||||
let i = 0
|
||||
let map = {}
|
||||
let contents = flatten(routes, route => route.component)
|
||||
.map(path => {
|
||||
let name = `Component$${++i}`
|
||||
map[path] = name
|
||||
return `import ${name} from ".${path}";`
|
||||
})
|
||||
.join('\n')
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, './examples/src/.generated/preload.js'),
|
||||
`${contents}\n\nexport default {\n${Object.entries(map)
|
||||
.map(([path, name]) => ` "${path}": ${name}`)
|
||||
.join(',\n')}\n}`,
|
||||
'utf8'
|
||||
)
|
||||
|
||||
// ---
|
||||
|
||||
function pipe(...fns) {
|
||||
return fns.reduceRight((f, g) => (...args) => f(g(...args)), fns.pop())
|
||||
}
|
||||
|
||||
Prism.plugins.customClass.map({
|
||||
tag: 'text-code-red',
|
||||
'attr-name': 'text-code-yellow',
|
||||
'attr-value': 'text-code-green',
|
||||
deleted: 'text-code-red',
|
||||
inserted: 'text-code-green',
|
||||
punctuation: 'text-code-white',
|
||||
keyword: 'text-code-purple',
|
||||
string: 'text-code-green',
|
||||
function: 'text-code-blue',
|
||||
boolean: 'text-code-red',
|
||||
comment: 'text-gray-400 italic',
|
||||
})
|
||||
|
||||
let sourcePipeline = pipe(
|
||||
path => fs.readFileSync(path, 'utf8'),
|
||||
contents =>
|
||||
prettier.format(contents, {
|
||||
parser: 'vue',
|
||||
printWidth: 100,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
}),
|
||||
contents => Prism.highlight(contents, Prism.languages.markup),
|
||||
contents =>
|
||||
[
|
||||
'<pre class="language-vue rounded-md bg-gray-800 py-3 px-4 overflow-x-auto">',
|
||||
'<code class="language-vue text-gray-200">',
|
||||
contents,
|
||||
'</code>',
|
||||
'</pre>',
|
||||
].join('')
|
||||
)
|
||||
|
||||
let skipRoutes = ['/']
|
||||
let source = Object.assign(
|
||||
{},
|
||||
...flatten(routes, route => ({
|
||||
urlPath: route.path,
|
||||
sourcePath: route.component,
|
||||
}))
|
||||
.filter(({ urlPath }) => !skipRoutes.includes(urlPath))
|
||||
.map(({ urlPath, sourcePath }) => ({
|
||||
[urlPath]: sourcePipeline(path.resolve(__dirname, 'examples', 'src', sourcePath), 'utf8'),
|
||||
}))
|
||||
)
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, './examples/src/.generated/source.json'),
|
||||
JSON.stringify(source, null, 2),
|
||||
'utf8'
|
||||
)
|
||||
// ---
|
||||
|
||||
let HeadlessUIPlugin = ({
|
||||
root, // project root directory, absolute path
|
||||
app, // Koa app instance
|
||||
server, // raw http server instance
|
||||
watcher, // chokidar file watcher instance
|
||||
resolver, // chokidar file watcher instance
|
||||
}) => {
|
||||
let routePaths = flatten(routes, route => route.path)
|
||||
|
||||
app.use(async (ctx, next) => {
|
||||
if (routePaths.includes(ctx.path)) {
|
||||
ctx.path = './index.html'
|
||||
}
|
||||
|
||||
await next()
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
alias: {
|
||||
[process.env.NODE_ENV === 'production' ? '@headlessui/vue' : '/@headlessui/vue/']: path.resolve(
|
||||
__dirname,
|
||||
'./src/index.ts'
|
||||
),
|
||||
},
|
||||
configureServer: [HeadlessUIPlugin],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
|
||||
module.exports = {
|
||||
purge: [],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
},
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
colors: {
|
||||
code: {
|
||||
green: '#b5f4a5',
|
||||
yellow: '#ffe484',
|
||||
purple: '#d9a9ff',
|
||||
red: '#ff8383',
|
||||
blue: '#93ddfd',
|
||||
white: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {},
|
||||
plugins: [require('@tailwindcss/ui')],
|
||||
}
|
||||
Reference in New Issue
Block a user