diff --git a/package.json b/package.json index c7d3763..a176c77 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,11 @@ "build-storybook": "build-storybook" }, "dependencies": { + "react-icons": "^4.2.0", + "react-modal": "^3.14.3", "react-table": "^7.7.0", + "slate": "^0.65.3", + "slate-react": "^0.65.3", "storybook-addon-theme-ui": "^0.1.5" }, "devDependencies": { @@ -40,6 +44,7 @@ "@theme-ui/preset-base": "^0.10.1", "@types/faker": "^5.5.7", "@types/jest": "^26.0.24", + "@types/react-modal": "^3.12.1", "@types/react-table": "^7.7.2", "@typescript-eslint/eslint-plugin": "^4.28.2", "@typescript-eslint/parser": "^4.28.2", diff --git a/src/components.ts b/src/components.ts index 9e35538..fa65247 100644 --- a/src/components.ts +++ b/src/components.ts @@ -1,3 +1,19 @@ +export * from './components/file-upload/components/file-delete-button'; +export * from './components/file-upload/components/file-upload-button'; +export * from './components/file-upload/components/file-upload-input'; +export * from './components/file-upload/document-file-upload'; +export * from './components/file-upload/image-file-upload'; +export * from './components/action-box-item'; +export * from './components/alert'; +export * from './components/editor'; +export * from './components/field-image'; +export * from './components/labeled-button'; +export * from './components/modal'; +export * from './components/select-alt'; +export * from './components/select-option'; +export * from './components/select'; +export * from './components/tag-field'; +export * from './components/tag-select'; export * from './breadcrumb'; export * from './list-item'; export * from './table'; diff --git a/src/components/action-box-item.tsx b/src/components/action-box-item.tsx new file mode 100644 index 0000000..e011ea2 --- /dev/null +++ b/src/components/action-box-item.tsx @@ -0,0 +1,34 @@ +import React, { ReactNode } from 'react'; +import { ButtonProps, Container, Flex, Grid } from 'theme-ui'; + +export interface ActionBoxItemProps extends ButtonProps { + actions?: Array; +} + +export const ActionBoxItem: React.FC = ({ + actions, + disabled, + children, +}) => { + return ( + + + + {children} + + {actions} + + + ); +}; + +ActionBoxItem.displayName = 'ListBox'; diff --git a/src/components/alert.tsx b/src/components/alert.tsx new file mode 100644 index 0000000..64ccd08 --- /dev/null +++ b/src/components/alert.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { + Alert as AlertTheme, + AlertProps as AlertThemeProps, + Close, +} from 'theme-ui'; + +export interface AlertProps { + onClose?: () => void; +} + +export const Alert: React.FC = ({ + children, + onClose, + ...rest +}) => ( + + {children} + + +); diff --git a/src/components/editor.tsx b/src/components/editor.tsx new file mode 100644 index 0000000..6f5e0fa --- /dev/null +++ b/src/components/editor.tsx @@ -0,0 +1,41 @@ +import React, { useMemo, useState } from 'react'; +import { Node, createEditor } from 'slate'; +import { Editable, Slate, withReact } from 'slate-react'; +import { ForwardRef } from 'theme-ui'; + +export interface EditorProps { + text: Node[]; + onChange?: (value: Node[]) => void; +} + +const noteDefaultValue = [ + { + type: 'paragraph', + children: [{ text: '' }], + }, +]; + +export const Editor: ForwardRef = + React.forwardRef(({ text = noteDefaultValue, onChange = () => '' }, ref) => { + const editor = useMemo(() => withReact(createEditor()), []); + const [editorValue, setEditorValue] = useState(text); + + return ( + { + onChange(val); + setEditorValue(val); + }} + ref={ref} + > + + + ); + }); diff --git a/src/components/field-image.tsx b/src/components/field-image.tsx new file mode 100644 index 0000000..963aad1 --- /dev/null +++ b/src/components/field-image.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { HiOutlineUser } from 'react-icons/hi'; +import { + Box, + Label, + Input, + FieldProps, + Text, + Image, + Flex, + ForwardRef, + InputProps, + ThemeUIStyleObject, +} from 'theme-ui'; +import { getMargin, getPos, omitMargin, omitPos } from '../util'; + +export interface UserProps { + photoUrl: string; + firstName: string; + lastName: string; + roles: string[]; +} + +// TODO correctly type this interface and implement it to type props +export interface FieldImageProps { + as: ForwardRef; + label: string; + name: string; + error: any; + sx: ThemeUIStyleObject; + userData: Partial; + children: any; +} + +export const FieldImage = React.forwardRef( + ( + { as: Control = Input, label, name, error, sx, userData, ...props }: FieldProps, + ref + ) => { + return ( + + + {/* TODO: replace with Avatar component (shows user image or initials) */} + {userData ? ( + + ) : ( + + )} + + + + + {!!error && {error}} + + + ); + } +); diff --git a/src/components/file-upload/components/file-delete-button.tsx b/src/components/file-upload/components/file-delete-button.tsx new file mode 100644 index 0000000..0ba477b --- /dev/null +++ b/src/components/file-upload/components/file-delete-button.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Button, ButtonProps } from 'theme-ui'; + +export const FileDeleteButton: React.FC = ({ children, ...props }) => ( + +); diff --git a/src/components/file-upload/components/file-upload-button.tsx b/src/components/file-upload/components/file-upload-button.tsx new file mode 100644 index 0000000..a775991 --- /dev/null +++ b/src/components/file-upload/components/file-upload-button.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { FiLoader } from 'react-icons/fi'; +import { HiOutlineUpload } from 'react-icons/hi'; +import { Button, ButtonProps } from 'theme-ui'; + +export type FileUploadButtonProps = { + loading?: boolean; +} & ButtonProps; + +export const FileUploadButton: React.FC = ({ + loading, + children, + ...props +}) => ( + +); diff --git a/src/components/file-upload/components/file-upload-input.tsx b/src/components/file-upload/components/file-upload-input.tsx new file mode 100644 index 0000000..e043436 --- /dev/null +++ b/src/components/file-upload/components/file-upload-input.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export interface FileUploadInputProps extends React.HTMLProps { + onChange: (event: React.ChangeEvent) => void; + inputRef: { current: null | HTMLInputElement }; +} + +export const FileUploadInput: React.FC = ({ + inputRef, + ...rest +}) => ; diff --git a/src/components/file-upload/document-file-upload.tsx b/src/components/file-upload/document-file-upload.tsx new file mode 100644 index 0000000..6f6a941 --- /dev/null +++ b/src/components/file-upload/document-file-upload.tsx @@ -0,0 +1,118 @@ +import React, { useRef, useState } from 'react'; +import { Box, Grid, Text } from 'theme-ui'; +import { FileDeleteButton } from './components/file-delete-button'; +import { FileUploadButton } from './components/file-upload-button'; +import { FileUploadInput } from './components/file-upload-input'; + +export interface FileUploadProps { + value?: string[]; + id?: string; + accept?: string; + multiple?: boolean; + buttonComponent?: React.ReactNode; + validate?: (file: File) => string | void; + upload: (files: FileList) => void; + remove?: () => void; +} + +export const DocumentFileUpload: React.FC = (props) => { + const { value, id, accept, multiple, buttonComponent, validate, upload, remove } = + props; + + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState([] as string[]); + + const inputRef = useRef(null); + + const onButtonClick = () => inputRef.current && inputRef.current.click(); + const onInputChange = async (event: React.ChangeEvent) => { + const { files: filesToUpload } = event.target; + if (!filesToUpload?.length) { + return; + } + if (validate) { + const validationErrors = Array.from(filesToUpload).reduce((acc: string[], file) => { + const err = validate(file); + if (err) { + acc.push(err); + } + return acc; + }, []); + if (validationErrors.length) { + setErrors(validationErrors); + return; + } + } + + setLoading(true); + try { + await upload(filesToUpload); + } catch (err) { + setErrors([err.message]); + } + setLoading(false); + }; + + return ( + + + + {value?.length > 0 ? ( + value.map(i => ( +
+ {i.split('/').pop()} +
+ )) + ) : ( + Choose file to upload + )} +
+ + + + Delete + + + {errors && errors.length > 0 && ( + {errors.join(' ')} + )} +
+
+ ); +}; + +DocumentFileUpload.defaultProps = { + value: [], + multiple: false, + remove: () => null, +}; diff --git a/src/components/file-upload/image-file-upload.tsx b/src/components/file-upload/image-file-upload.tsx new file mode 100644 index 0000000..cb76534 --- /dev/null +++ b/src/components/file-upload/image-file-upload.tsx @@ -0,0 +1,136 @@ +import React, { useRef, useState } from 'react'; +import { Box, Grid, Image, Text } from 'theme-ui'; +import { FileDeleteButton } from './components/file-delete-button'; +import { FileUploadButton } from './components/file-upload-button'; +import { FileUploadInput } from './components/file-upload-input'; + +export interface UploadedFile { + url: string; +} + +export interface ImageFileUploadProps { + value?: UploadedFile | null; + user: { + firstName: string; + lastName: string; + }; + id?: string; + accept?: string; + buttonComponent?: React.ReactNode; + validate?: (file: File) => string | void; + upload: (files: File) => Promise | null; + remove?: (file: UploadedFile) => void; +} + +const defaultProps = { + accept: 'image/*', +}; + +export const ImageFileUpload: React.FC = (props) => { + const { value, user, id, accept, buttonComponent, validate, upload, remove } = props; + + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState([] as string[]); + const [image, setImage] = useState(value?.url); + + const inputRef = useRef(null); + + const onButtonClick = () => inputRef.current && inputRef.current.click(); + const onInputChange = async (event: React.ChangeEvent) => { + const { files: filesToUpload } = event.target; + if (!filesToUpload?.length) { + return; + } + if (validate) { + const validationErrors = Array.from(filesToUpload).reduce((acc: string[], file) => { + const err = validate(file); + if (err) { + acc.push(err); + } + return acc; + }, []); + if (validationErrors.length) { + setErrors(validationErrors); + return; + } + } + + setLoading(true); + try { + if (filesToUpload.length !== 0) { + const url = await upload(filesToUpload[0]); + setImage(url); + } + } catch (err) { + setErrors([err.message]); + } + setLoading(false); + }; + + return ( + + + + + + + + { + remove(value); + setImage(''); + }} + > + Delete image + + + {errors && errors.length > 0 && ( + {errors.join(' ')} + )} + + + ); +}; + +ImageFileUpload.defaultProps = defaultProps; + +ImageFileUpload.displayName = 'ImageFileUpload'; diff --git a/src/components/labeled-button.tsx b/src/components/labeled-button.tsx new file mode 100644 index 0000000..8084177 --- /dev/null +++ b/src/components/labeled-button.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { ButtonProps, Flex, ForwardRef, IconButton, Label } from 'theme-ui'; + +export interface LabeledButtonProps extends ButtonProps { + label: string; + name?: string; +} + +export const LabeledButton: ForwardRef = + React.forwardRef(({ label, name, children, ...props }, ref) => { + return ( + + + + {children} + + + ); + }); + +LabeledButton.displayName = 'LabeledButton'; diff --git a/src/components/modal.tsx b/src/components/modal.tsx new file mode 100644 index 0000000..76db3b5 --- /dev/null +++ b/src/components/modal.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { HiX } from 'react-icons/hi'; +import ReactModal, { Props as ReactModalProps, Styles } from 'react-modal'; +import { IconButton, Label } from 'theme-ui'; + +export const defaultStyle = { + overlay: { + position: 'fixed', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: 'rgba(154, 160, 181, 0.5)', + }, + content: { + position: 'relative', + transform: 'translateY(-50%)', + top: '50%', + width: '70%', + maxWidth: '910px', + maxHeight: '90%', + margin: '0 auto', + backgroundColor: '#fff', + boxShadow: '0px 3px 6px #00000029', + borderRadius: '9px', + outline: 'none', + padding: '30px', + overflow: 'auto', + WebkitOverflowScrolling: 'touch', + }, +}; + +export type ModalProps = { + header?: React.ReactNode | string; +} & ReactModalProps; + +export const Modal: React.FC = ({ header, children, ...props }) => ( + + {React.isValidElement(header) ? ( + header + ) : header ? ( + + ) : null} + {children} + + + + +); + +ReactModal.setAppElement('#root'); diff --git a/src/components/select-alt.tsx b/src/components/select-alt.tsx new file mode 100644 index 0000000..f3912a6 --- /dev/null +++ b/src/components/select-alt.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { HiOutlineChevronDown } from 'react-icons/hi'; +import { ForwardRef, IconButton, Select as ThemeSelect } from 'theme-ui'; + +export interface SelectAltProps {} + +export const SelectAlt: ForwardRef = + React.forwardRef(({ children, ...props }, ref) => { + return ( + + + + } + sx={{ + alignItems: 'center', + border: 'none', + outline: 'none', + p: 0, + }} + > + {children} + + ); + }); diff --git a/src/components/select-option.ts b/src/components/select-option.ts new file mode 100644 index 0000000..abaf233 --- /dev/null +++ b/src/components/select-option.ts @@ -0,0 +1,4 @@ +export interface SelectOption { + id: string; + text: string; +} diff --git a/src/components/select.tsx b/src/components/select.tsx new file mode 100644 index 0000000..ceca0e9 --- /dev/null +++ b/src/components/select.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { HiChevronDown } from 'react-icons/hi'; +import { IconButton, Select as ThemeSelect } from 'theme-ui'; +import { SelectOption } from './select-option'; + +export interface DropdownProps { + onChange?: (tags: SelectOption) => void; +} + +// TODO: should be refactored, see SelectAlt component +export const Select: React.FC = React.forwardRef( + ({ onChange = () => '', children, ...props }) => { + return ( + + + + } + onChange={(event) => { + const optionElement = event.target.selectedOptions[0]; + onChange({ + id: optionElement.id, + text: optionElement.value, + }); + }} + sx={{ + minWidth: '10rem', + alignItems: 'center', + backgroundColor: 'muted', + border: 'none', + borderRadius: '8px', + outline: 'none', + padding: '8px 24px 8px 8px', + fontSize: '12px', + }} + > + {children} + + ); + } +); diff --git a/src/components/tag-field.tsx b/src/components/tag-field.tsx new file mode 100644 index 0000000..1e2747e --- /dev/null +++ b/src/components/tag-field.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { FieldProps, Flex, ForwardRef, Input, Label } from 'theme-ui'; + +export interface FieldsProps extends FieldProps { + alignItems?: string; +} + +export const TagField: ForwardRef = + React.forwardRef( + ( + { as: Control = Input, label, name, alignItems = 'center', ...props }, + ref, + ) => { + return ( + + + + + ); + }, + ); + +TagField.displayName = 'TagField'; diff --git a/src/components/tag-select.tsx b/src/components/tag-select.tsx new file mode 100644 index 0000000..5924087 --- /dev/null +++ b/src/components/tag-select.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { HiX } from 'react-icons/hi'; +import { + Card, + Flex, + ForwardRef, + Grid, + IconButton, + SelectProps, + Text, +} from 'theme-ui'; +import { Select } from './select'; +import { SelectOption } from './select-option'; + +export interface TagDropdownProps extends SelectProps { + tags?: SelectOption[]; + onChangeTags?: (tags: SelectOption[]) => void; +} + +export const TagSelect: ForwardRef = + React.forwardRef( + ({ tags = [], onChangeTags = () => '', children, ...props }) => { + const onAddition = (tag: SelectOption) => onChangeTags([...tags, tag]); + const onDelete = (tag: SelectOption) => { + const newTags = tags.slice(0); + const tagIndex = tags.findIndex(val => val.id === tag.id); + newTags.splice(tagIndex, 1); + onChangeTags(newTags); + }; + + return ( + + + {(tags || []).map(tag => ( + + + {tag.text} + onDelete(tag)} + > + + + + + ))} + + + + + + ); + }, + ); diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..a75a6aa --- /dev/null +++ b/src/util.ts @@ -0,0 +1,26 @@ +// eslint-disable-next-line jest/no-export +export const getProps = (test: any) => (props: any) => { + const next: any = {}; + for (const key in props) { + // eslint-disable-next-line jest/valid-title + if (test(key || '')) next[key] = props[key]; + } + return next; +}; +// eslint-disable-next-line jest/no-export +export const MRE = /^m[trblxy]?$/; + +// eslint-disable-next-line jest/no-export +export const POS = /^flex|m[trblxy]?$/; + +// eslint-disable-next-line jest/no-export +export const getMargin = getProps((k: any) => MRE.test(k)); + +// eslint-disable-next-line jest/no-export +export const omitMargin = getProps((k: any) => !MRE.test(k)); + +// eslint-disable-next-line jest/no-export +export const getPos = getProps((k: any) => POS.test(k)); + +// eslint-disable-next-line jest/no-export +export const omitPos = getProps((k: any) => !POS.test(k)); diff --git a/yarn.lock b/yarn.lock index 8ebd0d3..7691a14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3030,6 +3030,11 @@ "@types/estree" "*" "@types/json-schema" "*" +"@types/esrever@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@types/esrever/-/esrever-0.2.0.tgz#96404a2284b2c7527f08a1e957f8a31705f9880f" + integrity sha512-5NI6TeGzVEy/iBcuYtcPzzIC6EqlfQ2+UZ54vT0ulq8bPNGAy8UJD+XcsAyEOcnYFUjOVWuUV+k4/rVkxt9/XQ== + "@types/estree@*": version "0.0.50" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" @@ -3082,6 +3087,11 @@ resolved "https://registry.yarnpkg.com/@types/is-function/-/is-function-1.0.0.tgz#1b0b819b1636c7baf0d6785d030d12edf70c3e83" integrity sha512-iTs9HReBu7evG77Q4EC8hZnqRt57irBDkK9nvmHroiOIVwYMQc4IvYvdRgwKfYepunIY7Oh/dBuuld+Gj9uo6w== +"@types/is-hotkey@^0.1.1": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@types/is-hotkey/-/is-hotkey-0.1.5.tgz#f3123ba21228c0408c10594abf378caddbb802f8" + integrity sha512-pZTb6AsG7I56FJgYA8Cbit3cB3NGVwyHgwyUCENjXewTQChOtQaxaV+u6BO4hqtS1o9KT1wML+NRkGhQZ6swtA== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -3114,6 +3124,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/lodash@^4.14.149": + version "4.14.172" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.172.tgz#aad774c28e7bfd7a67de25408e03ee5a8c3d028a" + integrity sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw== + "@types/markdown-to-jsx@^6.11.3": version "6.11.3" resolved "https://registry.yarnpkg.com/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.3.tgz#cdd1619308fecbc8be7e6a26f3751260249b020e" @@ -3215,6 +3230,13 @@ dependencies: "@types/react" "*" +"@types/react-modal@^3.12.1": + version "3.12.1" + resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.12.1.tgz#fd1558762ed96020291b831190c85bc721aad3c1" + integrity sha512-pgq/jAnSJqHX7NXTFyUSXwlFOBUGngBXavFmAIKE7bkM7WNKyF/9XvmMr2+eIBOvR8waiA0Nj2qHfDZqMgeT6w== + dependencies: + "@types/react" "*" + "@types/react-syntax-highlighter@10.1.0": version "10.1.0" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-10.1.0.tgz#9c534e29bbe05dba9beae1234f3ae944836685d4" @@ -5941,6 +5963,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +direction@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/direction/-/direction-1.0.4.tgz#2b86fb686967e987088caf8b89059370d4837442" + integrity sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ== + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -6675,6 +6702,11 @@ esrecurse@^4.1.0, esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" +esrever@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/esrever/-/esrever-0.2.0.tgz#96e9d28f4f1b1a76784cd5d490eaae010e7407b8" + integrity sha1-lunSj08bGnZ4TNXUkOquAQ50B7g= + estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -6772,6 +6804,11 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +exenv@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50= + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -8024,6 +8061,11 @@ immer@8.0.1: resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== +immer@^8.0.1: + version "8.0.4" + resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.4.tgz#3a21605a4e2dded852fb2afd208ad50969737b7a" + integrity sha512-jMfL18P+/6P6epANRvRk6q8t+3gGhqsJ9EuJ25AXE+9bNTYtssvzeYbEd0mXRYWCmmXSIbnlpz6vd6iJlmGGGQ== + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -8422,6 +8464,11 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-hotkey@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.8.tgz#6b1f4b2d0e5639934e20c05ed24d623a21d36d25" + integrity sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ== + is-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" @@ -8493,7 +8540,7 @@ is-plain-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-object@3.0.1: +is-plain-object@3.0.1, is-plain-object@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== @@ -9484,7 +9531,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.7.0: +"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -11963,6 +12010,11 @@ react-helmet-async@^1.0.2, react-helmet-async@^1.0.7: react-fast-compare "^3.2.0" shallowequal "^1.1.0" +react-icons@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.2.0.tgz#6dda80c8a8f338ff96a1851424d63083282630d0" + integrity sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ== + react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -11973,11 +12025,21 @@ react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-modal@^3.14.3: + version "3.14.3" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.14.3.tgz#7eb7c5ec85523e5843e2d4737cc17fc3f6aeb1c0" + integrity sha512-+C2KODVKyu20zHXPJxfOOcf571L1u/EpFlH+oS/3YDn8rgVE51QZuxuuIwabJ8ZFnOEHaD+r6XNjqwtxZnXO0g== + dependencies: + exenv "^1.2.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.0" + warning "^4.0.3" + react-popper-tooltip@^2.8.3: version "2.11.1" resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.11.1.tgz#3c4bdfd8bc10d1c2b9a162e859bab8958f5b2644" @@ -12787,6 +12849,13 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +scroll-into-view-if-needed@^2.2.20: + version "2.2.28" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz#5a15b2f58a52642c88c8eca584644e01703d645a" + integrity sha512-8LuxJSuFVc92+0AdNv4QOxRL4Abeo1DgLnGNkn1XlaujPH/3cCFz3QI60r2VNu4obJJROzgnIUw5TKQkZvZI1w== + dependencies: + compute-scroll-into-view "^1.0.17" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -13043,6 +13112,32 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slate-react@^0.65.3: + version "0.65.3" + resolved "https://registry.yarnpkg.com/slate-react/-/slate-react-0.65.3.tgz#f652e4780b4304189a8a8ecc8feca723f31173a3" + integrity sha512-lyiZD7Rrt0LInvK4xhIemnCEx/N7jt+e/eHlsFor+rif/K0RT9wSPXWajTI9RglbvN9XHTEuB2BFx2cQLKiomw== + dependencies: + "@types/is-hotkey" "^0.1.1" + "@types/lodash" "^4.14.149" + direction "^1.0.3" + is-hotkey "^0.1.6" + is-plain-object "^3.0.0" + lodash "^4.17.4" + scroll-into-view-if-needed "^2.2.20" + tiny-invariant "1.0.6" + +slate@^0.65.3: + version "0.65.3" + resolved "https://registry.yarnpkg.com/slate/-/slate-0.65.3.tgz#8178cdf28a10a3a4e6858b13bc2ffa7c3d003e7a" + integrity sha512-n8wa2MKyWhCMRyVkXuMf67MmOYSeoHnqS1qYivor+/y0puNvQgXDUjC7TJJqUjhVqJ6zg2IeuYd0WfSYdAJs4g== + dependencies: + "@types/esrever" "^0.2.0" + esrever "^0.2.0" + fast-deep-equal "^3.1.3" + immer "^8.0.1" + is-plain-object "^3.0.0" + tiny-warning "^1.0.3" + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -13833,6 +13928,16 @@ tiny-emitter@^2.0.0: resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== +tiny-invariant@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73" + integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA== + +tiny-warning@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"