Update react, add some V2 components for V1 usage
This commit is contained in:
parent
921da09a63
commit
1a5465dc34
21 changed files with 564 additions and 43 deletions
12
package.json
12
package.json
|
@ -4,9 +4,13 @@
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.32",
|
"@fortawesome/fontawesome-svg-core": "^1.2.32",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.15.1",
|
"@fortawesome/free-solid-svg-icons": "^5.15.1",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.11",
|
"@fortawesome/react-fontawesome": "^0.1.11",
|
||||||
|
"@headlessui/react": "^1.6.4",
|
||||||
|
"@heroicons/react": "^1.0.6",
|
||||||
|
"@hot-loader/react-dom": "^16.14.0",
|
||||||
"@tailwindcss/forms": "^0.5.2",
|
"@tailwindcss/forms": "^0.5.2",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"chart.js": "^2.8.0",
|
"chart.js": "^2.8.0",
|
||||||
|
"classnames": "^2.3.1",
|
||||||
"codemirror": "^5.57.0",
|
"codemirror": "^5.57.0",
|
||||||
"date-fns": "^2.16.1",
|
"date-fns": "^2.16.1",
|
||||||
"debounce": "^1.2.0",
|
"debounce": "^1.2.0",
|
||||||
|
@ -20,7 +24,7 @@
|
||||||
"i18next-xhr-backend": "^3.2.2",
|
"i18next-xhr-backend": "^3.2.2",
|
||||||
"qrcode.react": "^1.0.1",
|
"qrcode.react": "^1.0.1",
|
||||||
"query-string": "^6.7.0",
|
"query-string": "^6.7.0",
|
||||||
"react": "^16.13.1",
|
"react": "^16.14.0",
|
||||||
"react-copy-to-clipboard": "^5.0.2",
|
"react-copy-to-clipboard": "^5.0.2",
|
||||||
"react-dom": "npm:@hot-loader/react-dom",
|
"react-dom": "npm:@hot-loader/react-dom",
|
||||||
"react-fast-compare": "^3.2.0",
|
"react-fast-compare": "^3.2.0",
|
||||||
|
@ -64,9 +68,9 @@
|
||||||
"@types/node": "^14.11.10",
|
"@types/node": "^14.11.10",
|
||||||
"@types/qrcode.react": "^1.0.1",
|
"@types/qrcode.react": "^1.0.1",
|
||||||
"@types/query-string": "^6.3.0",
|
"@types/query-string": "^6.3.0",
|
||||||
"@types/react": "^16.9.41",
|
"@types/react": "^16.14.0",
|
||||||
"@types/react-copy-to-clipboard": "^4.3.0",
|
"@types/react-copy-to-clipboard": "^4.3.0",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.16",
|
||||||
"@types/react-helmet": "^6.0.0",
|
"@types/react-helmet": "^6.0.0",
|
||||||
"@types/react-redux": "^7.1.1",
|
"@types/react-redux": "^7.1.1",
|
||||||
"@types/react-router": "^5.1.3",
|
"@types/react-router": "^5.1.3",
|
||||||
|
@ -103,7 +107,7 @@
|
||||||
"terser-webpack-plugin": "^4.2.3",
|
"terser-webpack-plugin": "^4.2.3",
|
||||||
"ts-essentials": "^9.1.2",
|
"ts-essentials": "^9.1.2",
|
||||||
"twin.macro": "^2.8.2",
|
"twin.macro": "^2.8.2",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.7.3",
|
||||||
"webpack": "^4.43.0",
|
"webpack": "^4.43.0",
|
||||||
"webpack-assets-manifest": "^3.1.1",
|
"webpack-assets-manifest": "^3.1.1",
|
||||||
"webpack-bundle-analyzer": "^3.8.0",
|
"webpack-bundle-analyzer": "^3.8.0",
|
||||||
|
|
36
resources/scripts/components/elements/button/Button.tsx
Normal file
36
resources/scripts/components/elements/button/Button.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import React, { forwardRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import styles from './style.module.css';
|
||||||
|
|
||||||
|
export type ButtonProps = JSX.IntrinsicElements['button'] & {
|
||||||
|
square?: boolean;
|
||||||
|
small?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ children, square, small, className, ...rest }, ref) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
className={classNames(styles.button, { [styles.square]: square, [styles.small]: small }, className)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const TextButton = forwardRef<HTMLButtonElement, ButtonProps>(({ className, ...props }, ref) => (
|
||||||
|
// @ts-expect-error
|
||||||
|
<Button ref={ref} className={classNames(styles.text, className)} {...props} />
|
||||||
|
));
|
||||||
|
|
||||||
|
const DangerButton = forwardRef<HTMLButtonElement, ButtonProps>(({ className, ...props }, ref) => (
|
||||||
|
// @ts-expect-error
|
||||||
|
<Button ref={ref} className={classNames(styles.danger, className)} {...props} />
|
||||||
|
));
|
||||||
|
|
||||||
|
const _Button = Object.assign(Button, { Text: TextButton, Danger: DangerButton });
|
||||||
|
|
||||||
|
export default _Button;
|
2
resources/scripts/components/elements/button/index.ts
Normal file
2
resources/scripts/components/elements/button/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as Button } from './Button';
|
||||||
|
export { default as styles } from './style.module.css';
|
|
@ -0,0 +1,30 @@
|
||||||
|
.button {
|
||||||
|
@apply px-4 py-2 inline-flex items-center justify-center;
|
||||||
|
@apply bg-blue-600 rounded text-base font-semibold text-blue-50 transition-all duration-100;
|
||||||
|
@apply hover:bg-blue-500 active:bg-blue-500;
|
||||||
|
|
||||||
|
&.square {
|
||||||
|
@apply p-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
@apply ring-[3px] ring-blue-500 ring-offset-2 ring-offset-neutral-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sizing Controls */
|
||||||
|
&.small {
|
||||||
|
@apply px-3 py-1 font-normal focus:ring-2;
|
||||||
|
|
||||||
|
&.square {
|
||||||
|
@apply p-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
@apply bg-transparent focus:ring-neutral-300 focus:ring-opacity-50 hover:bg-neutral-500 active:bg-neutral-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger {
|
||||||
|
@apply bg-red-600 hover:bg-red-500 active:bg-red-500 focus:ring-red-500 text-red-50;
|
||||||
|
}
|
112
resources/scripts/components/elements/dialog/Dialog.tsx
Normal file
112
resources/scripts/components/elements/dialog/Dialog.tsx
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { Dialog as HeadlessDialog, Transition } from '@headlessui/react';
|
||||||
|
import { Button } from '@/components/elements/button/index';
|
||||||
|
import styles from './style.module.css';
|
||||||
|
import { XIcon } from '@heroicons/react/solid';
|
||||||
|
import { CheckIcon, ExclamationIcon, InformationCircleIcon, ShieldExclamationIcon } from '@heroicons/react/outline';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
onDismissed: () => void;
|
||||||
|
title?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DialogIconProps {
|
||||||
|
type: 'danger' | 'info' | 'success' | 'warning';
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DialogIcon = ({ type, className }: DialogIconProps) => {
|
||||||
|
const [ Component, styles ] = (function (): [(props: React.ComponentProps<'svg'>) => JSX.Element, string] {
|
||||||
|
switch (type) {
|
||||||
|
case 'danger':
|
||||||
|
return [ ShieldExclamationIcon, 'bg-red-500 text-red-50' ];
|
||||||
|
case 'warning':
|
||||||
|
return [ ExclamationIcon, 'bg-yellow-600 text-yellow-50' ];
|
||||||
|
case 'success':
|
||||||
|
return [ CheckIcon, 'bg-green-600 text-green-50' ];
|
||||||
|
case 'info':
|
||||||
|
return [ InformationCircleIcon, 'bg-primary-500 text-primary-50' ];
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('flex items-center justify-center w-10 h-10 rounded-full', styles, className)}>
|
||||||
|
<Component className={'w-6 h-6'} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DialogButtons = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<>{children}</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Dialog = ({ visible, title, onDismissed, children }: Props) => {
|
||||||
|
const items = React.Children.toArray(children || []);
|
||||||
|
const [ buttons, icon, content ] = [
|
||||||
|
// @ts-expect-error
|
||||||
|
items.find(child => child.type === DialogButtons),
|
||||||
|
// @ts-expect-error
|
||||||
|
items.find(child => child.type === DialogIcon),
|
||||||
|
// @ts-expect-error
|
||||||
|
items.filter(child => ![ DialogIcon, DialogButtons ].includes(child.type)),
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition show={visible} as={Fragment}>
|
||||||
|
<HeadlessDialog onClose={() => onDismissed()} className={styles.wrapper}>
|
||||||
|
<div className={'flex items-center justify-center min-h-screen'}>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter={'ease-out duration-200'}
|
||||||
|
enterFrom={'opacity-0'}
|
||||||
|
enterTo={'opacity-100'}
|
||||||
|
leave={'ease-in duration-100'}
|
||||||
|
leaveFrom={'opacity-100'}
|
||||||
|
leaveTo={'opacity-0'}
|
||||||
|
>
|
||||||
|
<HeadlessDialog.Overlay className={styles.overlay}/>
|
||||||
|
</Transition.Child>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter={'ease-out duration-200'}
|
||||||
|
enterFrom={'opacity-0 scale-95'}
|
||||||
|
enterTo={'opacity-100 scale-100'}
|
||||||
|
leave={'ease-in duration-100'}
|
||||||
|
leaveFrom={'opacity-100 scale-100'}
|
||||||
|
leaveTo={'opacity-0 scale-95'}
|
||||||
|
>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={'flex p-6'}>
|
||||||
|
{icon && <div className={'mr-4'}>{icon}</div>}
|
||||||
|
<div className={'flex-1'}>
|
||||||
|
{title &&
|
||||||
|
<HeadlessDialog.Title className={styles.title}>
|
||||||
|
{title}
|
||||||
|
</HeadlessDialog.Title>
|
||||||
|
}
|
||||||
|
<HeadlessDialog.Description className={'pr-4'}>
|
||||||
|
{content}
|
||||||
|
</HeadlessDialog.Description>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{buttons && <div className={styles.button_bar}>{buttons}</div>}
|
||||||
|
{/* Keep this below the other buttons so that it isn't the default focus if they're present. */}
|
||||||
|
<div className={'absolute right-0 top-0 m-4'}>
|
||||||
|
<Button.Text square small onClick={() => onDismissed()} className={'hover:rotate-90'}>
|
||||||
|
<XIcon className={'w-5 h-5'}/>
|
||||||
|
</Button.Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</HeadlessDialog>
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _Dialog = Object.assign(Dialog, { Buttons: DialogButtons, Icon: DialogIcon });
|
||||||
|
|
||||||
|
export default _Dialog;
|
2
resources/scripts/components/elements/dialog/index.ts
Normal file
2
resources/scripts/components/elements/dialog/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as Dialog } from './Dialog';
|
||||||
|
export { default as styles } from './style.module.css';
|
|
@ -0,0 +1,20 @@
|
||||||
|
.wrapper {
|
||||||
|
@apply fixed z-10 inset-0 overflow-y-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
@apply fixed inset-0 bg-gray-900 opacity-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
@apply relative bg-gray-600 rounded max-w-xl w-full mx-auto shadow-lg;
|
||||||
|
@apply ring-4 ring-gray-800 ring-opacity-80;
|
||||||
|
|
||||||
|
& .title {
|
||||||
|
@apply font-header text-xl font-medium mb-2 text-white pr-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .button_bar {
|
||||||
|
@apply px-6 py-3 bg-gray-700 flex items-center justify-end space-x-3 rounded-b;
|
||||||
|
}
|
||||||
|
}
|
62
resources/scripts/components/elements/dropdown/Dropdown.tsx
Normal file
62
resources/scripts/components/elements/dropdown/Dropdown.tsx
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import React, { ElementType, forwardRef, useMemo } from 'react';
|
||||||
|
import { Menu, Transition } from '@headlessui/react';
|
||||||
|
import styles from './style.module.css';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import DropdownItem from '@/components/elements/dropdown/DropdownItem';
|
||||||
|
import DropdownButton from '@/components/elements/dropdown/DropdownButton';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
as?: ElementType;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DropdownGap = ({ invisible }: { invisible?: boolean }) => (
|
||||||
|
<div className={classNames('border m-2', { 'border-neutral-700': !invisible, 'border-transparent': invisible })}/>
|
||||||
|
);
|
||||||
|
|
||||||
|
type TypedChild = (React.ReactChild | React.ReactFragment | React.ReactPortal) & {
|
||||||
|
type?: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Dropdown = forwardRef<typeof Menu, Props>(({ as, children }, ref) => {
|
||||||
|
const [ Button, items ] = useMemo(() => {
|
||||||
|
const list = React.Children.toArray(children) as unknown as TypedChild[];
|
||||||
|
|
||||||
|
return [
|
||||||
|
list.filter(child => child.type === DropdownButton),
|
||||||
|
list.filter(child => child.type !== DropdownButton),
|
||||||
|
];
|
||||||
|
}, [ children ]);
|
||||||
|
|
||||||
|
if (!Button) {
|
||||||
|
throw new Error('Cannot mount <Dropdown /> component without a child <Dropdown.Button />.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu as={as || 'div'} className={styles.menu} ref={ref}>
|
||||||
|
{Button}
|
||||||
|
<Transition
|
||||||
|
enter={'transition duration-100 ease-out'}
|
||||||
|
enterFrom={'transition 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={classNames(styles.items_container, 'w-56')}>
|
||||||
|
<div className={'px-1 py-1'}>
|
||||||
|
{items}
|
||||||
|
</div>
|
||||||
|
</Menu.Items>
|
||||||
|
</Transition>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const _Dropdown = Object.assign(Dropdown, {
|
||||||
|
Button: DropdownButton,
|
||||||
|
Item: DropdownItem,
|
||||||
|
Gap: DropdownGap,
|
||||||
|
});
|
||||||
|
|
||||||
|
export { _Dropdown as default };
|
|
@ -0,0 +1,24 @@
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import styles from '@/components/elements/dropdown/style.module.css';
|
||||||
|
import { ChevronDownIcon } from '@heroicons/react/solid';
|
||||||
|
import { Menu } from '@headlessui/react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
className?: string;
|
||||||
|
animate?: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ({ className, animate = true, children }: Props) => (
|
||||||
|
<Menu.Button className={classNames(styles.button, className || 'px-4')}>
|
||||||
|
{typeof children === 'string' ?
|
||||||
|
<>
|
||||||
|
<span className={'mr-2'}>{children}</span>
|
||||||
|
<ChevronDownIcon aria-hidden={'true'} data-animated={animate.toString()}/>
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
children
|
||||||
|
}
|
||||||
|
</Menu.Button>
|
||||||
|
);
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { forwardRef } from 'react';
|
||||||
|
import { Menu } from '@headlessui/react';
|
||||||
|
import styles from './style.module.css';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: React.ReactNode | ((opts: { active: boolean; disabled: boolean }) => JSX.Element);
|
||||||
|
danger?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
|
icon?: JSX.Element;
|
||||||
|
onClick?: (e: React.MouseEvent) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DropdownItem = forwardRef<HTMLAnchorElement, Props>(({
|
||||||
|
disabled,
|
||||||
|
danger,
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
children,
|
||||||
|
icon: IconComponent,
|
||||||
|
}, ref) => {
|
||||||
|
return (
|
||||||
|
<Menu.Item disabled={disabled}>
|
||||||
|
{({ disabled, active }) => (
|
||||||
|
<a
|
||||||
|
ref={ref}
|
||||||
|
href={'#'}
|
||||||
|
className={classNames(styles.menu_item, {
|
||||||
|
[styles.danger]: danger,
|
||||||
|
[styles.disabled]: disabled,
|
||||||
|
}, className)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{IconComponent}
|
||||||
|
{typeof children === 'function' ? children({ disabled, active }) : children}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default DropdownItem;
|
2
resources/scripts/components/elements/dropdown/index.ts
Normal file
2
resources/scripts/components/elements/dropdown/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as Dropdown } from './Dropdown';
|
||||||
|
export * as styles from './style.module.css';
|
|
@ -0,0 +1,58 @@
|
||||||
|
.menu {
|
||||||
|
@apply relative inline-block text-left;
|
||||||
|
|
||||||
|
& .button {
|
||||||
|
@apply inline-flex justify-center items-center w-full py-2 text-neutral-100 rounded-md;
|
||||||
|
@apply transition-all duration-100;
|
||||||
|
|
||||||
|
&:hover, &[aria-expanded="true"] {
|
||||||
|
@apply bg-neutral-600 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus, &:focus-within, &:active {
|
||||||
|
@apply ring-2 ring-opacity-50 ring-neutral-300 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
@apply w-5 h-5 transition-transform duration-75;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[aria-expanded="true"] svg[data-animated="true"] {
|
||||||
|
@apply rotate-180;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .items_container {
|
||||||
|
@apply absolute right-0 mt-2 origin-top-right bg-neutral-900 rounded z-10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu_item {
|
||||||
|
@apply flex items-center rounded w-full px-2 py-2;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
@apply w-4 h-4 mr-4 text-neutral-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
@apply bg-blue-500 text-blue-50;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
@apply text-blue-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
&:hover, &:focus {
|
||||||
|
@apply bg-red-500 text-red-50;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
@apply text-red-50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
@apply cursor-not-allowed hover:bg-neutral-800 opacity-30 focus:bg-transparent focus:hover:bg-neutral-800;
|
||||||
|
}
|
||||||
|
}
|
11
resources/scripts/components/elements/inputs/Checkbox.tsx
Normal file
11
resources/scripts/components/elements/inputs/Checkbox.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React, { forwardRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
export default forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(({ className, ...props }, ref) => (
|
||||||
|
<input
|
||||||
|
ref={ref}
|
||||||
|
className={classNames('form-input', styles.text_input, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
11
resources/scripts/components/elements/inputs/InputField.tsx
Normal file
11
resources/scripts/components/elements/inputs/InputField.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import React, { forwardRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
export default forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(({ className, ...props }, ref) => (
|
||||||
|
<input
|
||||||
|
ref={ref}
|
||||||
|
className={classNames('form-input', styles.text_input, className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
3
resources/scripts/components/elements/inputs/index.ts
Normal file
3
resources/scripts/components/elements/inputs/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as Checkbox } from './Checkbox';
|
||||||
|
export { default as InputField } from './InputField';
|
||||||
|
export { default as styles } from './styles.module.css';
|
|
@ -0,0 +1,21 @@
|
||||||
|
.checkbox {
|
||||||
|
@apply w-4 h-4 rounded-sm border-neutral-500 bg-neutral-600 text-primary-500;
|
||||||
|
|
||||||
|
&:focus, &:active {
|
||||||
|
@apply ring-2 ring-primary-500 ring-offset-2 ring-offset-neutral-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.indeterminate:checked {
|
||||||
|
@apply text-primary-500/50 border border-primary-500;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='white'%3E%3Cpath fill-rule='evenodd' d='M5 10a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1z' clip-rule='evenodd' /%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_input {
|
||||||
|
@apply transition-all duration-75;
|
||||||
|
@apply bg-neutral-800 border-neutral-600 rounded px-4 py-2 outline-none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
@apply border-blue-600 ring-2 ring-blue-500;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Transition } from '@headlessui/react';
|
||||||
|
|
||||||
|
type Duration = `duration-${number}`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
as?: React.ElementType;
|
||||||
|
duration?: Duration | [ Duration, Duration ];
|
||||||
|
show: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ({ children, duration, ...props }: Props) => {
|
||||||
|
const [ enterDuration, exitDuration ] = Array.isArray(duration)
|
||||||
|
? duration
|
||||||
|
: (!duration ? [ 'duration-200', 'duration-100' ] : [ duration, duration ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition
|
||||||
|
{...props}
|
||||||
|
enter={`ease-out ${enterDuration}`}
|
||||||
|
enterFrom={'opacity-0'}
|
||||||
|
enterTo={'opacity-100'}
|
||||||
|
leave={`ease-in ${exitDuration}`}
|
||||||
|
leaveFrom={'opacity-100'}
|
||||||
|
leaveTo={'opacity-0'}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Transition as TransitionComponent } from '@headlessui/react';
|
||||||
|
import FadeTransition from '@/components/elements/transitions/FadeTransition';
|
||||||
|
|
||||||
|
const Transition = Object.assign(TransitionComponent, {
|
||||||
|
Fade: FadeTransition,
|
||||||
|
});
|
||||||
|
|
||||||
|
export { Transition };
|
1
resources/scripts/globals.d.ts
vendored
1
resources/scripts/globals.d.ts
vendored
|
@ -1,3 +1,4 @@
|
||||||
declare module '*.jpg';
|
declare module '*.jpg';
|
||||||
declare module '*.png';
|
declare module '*.png';
|
||||||
declare module '*.svg';
|
declare module '*.svg';
|
||||||
|
declare module '*.css';
|
||||||
|
|
43
resources/scripts/macros.d.ts
vendored
43
resources/scripts/macros.d.ts
vendored
|
@ -1,28 +1,29 @@
|
||||||
// This allows the use of css={} on JSX elements.
|
import { ComponentType, ReactElement } from 'react';
|
||||||
//
|
|
||||||
// @see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31245
|
|
||||||
//
|
|
||||||
// This is just the contents of the @types/styled-components/cssprop.d.ts file
|
|
||||||
// since using the other method of just importing the one file did not work
|
|
||||||
// correctly for some reason.
|
|
||||||
// noinspection ES6UnusedImports
|
|
||||||
import {} from 'react';
|
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { CSSProp } from 'styled-components';
|
import styledImport, { css as cssImport, CSSProp, StyledComponentProps } from 'styled-components';
|
||||||
|
|
||||||
declare module 'react' {
|
declare module 'react' {
|
||||||
interface Attributes {
|
interface Attributes {
|
||||||
// NOTE: unlike the plain javascript version, it is not possible to get access
|
|
||||||
// to the element's own attributes inside function interpolations.
|
|
||||||
// Only theme will be accessible, and only with the DefaultTheme due to the global
|
|
||||||
// nature of this declaration.
|
|
||||||
// If you are writing this inline you already have access to all the attributes anyway,
|
|
||||||
// no need for the extra indirection.
|
|
||||||
/**
|
|
||||||
* If present, this React element will be converted by
|
|
||||||
* `babel-plugin-styled-components` into a styled component
|
|
||||||
* with the given css as its styles.
|
|
||||||
*/
|
|
||||||
css?: CSSProp;
|
css?: CSSProp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module 'styled-components' {
|
||||||
|
interface StyledComponentBase<
|
||||||
|
C extends string | ComponentType<any>,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
T extends object,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
O extends object = {},
|
||||||
|
A extends keyof any = never
|
||||||
|
> extends ForwardRefExoticBase<StyledComponentProps<C, T, O, A>> {
|
||||||
|
(props: StyledComponentProps<C, T, O, A> & { as?: Element | string; forwardedAs?: never | undefined }): ReactElement<
|
||||||
|
StyledComponentProps<C, T, O, A>
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'twin.macro' {
|
||||||
|
const css: typeof cssImport;
|
||||||
|
const styled: typeof styledImport;
|
||||||
|
}
|
||||||
|
|
75
yarn.lock
75
yarn.lock
|
@ -1272,6 +1272,26 @@
|
||||||
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
|
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
|
||||||
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
|
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
|
||||||
|
|
||||||
|
"@headlessui/react@^1.6.4":
|
||||||
|
version "1.6.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.6.4.tgz#c73084e23386bef5fb86cd16da3352c3a844bb4c"
|
||||||
|
integrity sha512-0yqz1scwbFtwljmbbKjXsSGl5ABEYNICVHZnMCWo0UtOZodo2Tpu94uOVgCRjRZ77l2WcTi2S0uidINDvG7lsA==
|
||||||
|
|
||||||
|
"@heroicons/react@^1.0.6":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324"
|
||||||
|
integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==
|
||||||
|
|
||||||
|
"@hot-loader/react-dom@^16.14.0":
|
||||||
|
version "16.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@hot-loader/react-dom/-/react-dom-16.14.0.tgz#3cfc64e40bb78fa623e59b582b8f09dcdaad648a"
|
||||||
|
integrity sha512-EN9czvcLsMYmSDo5yRKZOAq3ZGRlDpad1gPtX0NdMMomJXcPE3yFSeFzE94X/NjOaiSVimB7LuqPYpkWVaIi4Q==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
scheduler "^0.19.1"
|
||||||
|
|
||||||
"@jridgewell/gen-mapping@^0.3.0":
|
"@jridgewell/gen-mapping@^0.3.0":
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
|
||||||
|
@ -1471,12 +1491,12 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-dom@^16.9.8":
|
"@types/react-dom@^16.9.16":
|
||||||
version "16.9.8"
|
version "16.9.16"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423"
|
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.16.tgz#c591f2ed1c6f32e9759dfa6eb4abfd8041f29e39"
|
||||||
integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==
|
integrity sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "^16"
|
||||||
|
|
||||||
"@types/react-helmet@^6.0.0":
|
"@types/react-helmet@^6.0.0":
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
|
@ -1523,13 +1543,19 @@
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
csstype "^2.2.0"
|
csstype "^2.2.0"
|
||||||
|
|
||||||
"@types/react@^16.9.41":
|
"@types/react@^16", "@types/react@^16.14.0":
|
||||||
version "16.9.41"
|
version "16.14.26"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.41.tgz#925137ee4d2ff406a0ecf29e8e9237390844002e"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.26.tgz#82540a240ba7207ebe87d9579051bc19c9ef7605"
|
||||||
integrity sha512-6cFei7F7L4wwuM+IND/Q2cV1koQUvJ8iSV+Gwn0c3kvABZ691g7sp3hfEQHOUBJtccl1gPi+EyNjMIl9nGA0ug==
|
integrity sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
csstype "^2.2.0"
|
"@types/scheduler" "*"
|
||||||
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@types/scheduler@*":
|
||||||
|
version "0.16.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
|
||||||
|
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
|
||||||
|
|
||||||
"@types/styled-components@^5.1.7":
|
"@types/styled-components@^5.1.7":
|
||||||
version "5.1.7"
|
version "5.1.7"
|
||||||
|
@ -2657,6 +2683,11 @@ class-utils@^0.3.5:
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
static-extend "^0.1.1"
|
static-extend "^0.1.1"
|
||||||
|
|
||||||
|
classnames@^2.3.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||||
|
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
|
||||||
|
|
||||||
clean-set@^1.1.1:
|
clean-set@^1.1.1:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/clean-set/-/clean-set-1.1.2.tgz#76d8bf238c3e27827bfa73073ecdfdc767187070"
|
resolved "https://registry.yarnpkg.com/clean-set/-/clean-set-1.1.2.tgz#76d8bf238c3e27827bfa73073ecdfdc767187070"
|
||||||
|
@ -6994,10 +7025,10 @@ react-transition-group@^4.4.1:
|
||||||
loose-envify "^1.4.0"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
react@^16.13.1:
|
react@^16.14.0:
|
||||||
version "16.13.1"
|
version "16.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
||||||
integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
|
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
@ -7374,6 +7405,14 @@ scheduler@^0.17.0:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
scheduler@^0.19.1:
|
||||||
|
version "0.19.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
|
||||||
|
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
schema-utils@2.7.0, schema-utils@^2.6.5:
|
schema-utils@2.7.0, schema-utils@^2.6.5:
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
|
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
|
||||||
|
@ -8369,10 +8408,10 @@ typedarray@^0.0.6:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
|
|
||||||
typescript@^4.2.4:
|
typescript@^4.7.3:
|
||||||
version "4.2.4"
|
version "4.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
|
||||||
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
|
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
|
||||||
|
|
||||||
unbox-primitive@^1.0.1:
|
unbox-primitive@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
|
Loading…
Reference in a new issue