// Dependencies import { cloudflareImageUrl } from 'shared/utils.mjs' import { collection } from 'shared/hooks/use-design.mjs' // Context import { ModalContext } from 'shared/context/modal-context.mjs' // Hooks import { useState, useCallback, useContext } from 'react' import { useTranslation } from 'next-i18next' import { useDropzone } from 'react-dropzone' import { useBackend } from 'shared/hooks/use-backend.mjs' import { useLoadingStatus } from 'shared/hooks/use-loading-status.mjs' // Components import Markdown from 'react-markdown' import { ResetIcon, DocsIcon, UploadIcon } from 'shared/components/icons.mjs' import { ModalWrapper } from 'shared/components/wrappers/modal.mjs' import { isDegreeMeasurement } from 'config/measurements.mjs' import { measurementAsMm, measurementAsUnits, parseDistanceInput } from 'shared/utils.mjs' export const ns = ['account', 'measurements', 'designs'] /* * Helper component to display a tab heading */ export const Tab = ({ id, // The tab ID label, // A label for the tab, if not set we'll use the ID activeTab, // Which tab (id) is active setActiveTab, // Method to set the active tab }) => ( ) /* * Helper component to wrap a form control with a label */ export const FormControl = ({ label, // the (top-left) label children, // Children to go inside the form control docs = false, // Optional top-right label labelBL = false, // Optional bottom-left label labelBR = false, // Optional bottom-right label forId = false, // ID of the for element we are wrapping }) => { const { setModal } = useContext(ModalContext) const topLabelChildren = ( <> {label} {docs ? ( ) : null} ) const bottomLabelChildren = ( <> {labelBL ? {labelBL} : null} {labelBR ? {labelBR} : null} ) return (
{forId ? ( ) : (
{topLabelChildren}
)} {children} {labelBL || labelBR ? ( forId ? ( ) : (
{bottomLabelChildren}
) ) : null}
) } /* * Helper method to wrap content in a button */ export const ButtonFrame = ({ children, // Children of the button onClick, // onClick handler active, // Whether or not to render the button as active/selected }) => ( ) /* * Input for strings */ export const StringInput = ({ label, // Label to use update, // onChange handler valid, // Method that should return whether the value is valid or not current, // The current value original, // The original value placeholder, // The placeholder text docs = false, // Docs to load, if any id = '', // An id to tie the input to the label }) => ( update(evt.target.value)} className={`input w-full input-bordered ${ current === original ? 'input-secondary' : valid(current) ? 'input-success' : 'input-error' }`} /> ) /* * Input for email addresses */ export const EmailInput = ({ label, // Label to use update, // onChange handler valid, // Method that should return whether the value is valid or not current, // The current value original, // The original value placeholder, // The placeholder text docs = false, // Docs to load, if any id = '', // An id to tie the input to the label }) => ( update(evt.target.value)} className={`input w-full input-bordered ${ current === original ? 'input-secondary' : valid(current) ? 'input-success' : 'input-error' }`} /> ) /* * Dropdown for designs */ export const DesignDropdown = ({ label, // Label to use update, // onChange handler current, // The current value docs = false, // Docs to load, if any firstOption = null, // Any first option to add in addition to designs id = '', // An id to tie the input to the label }) => { const { t } = useTranslation(['designs']) return ( ) } /* * Input for an image */ export const ImageInput = ({ label, // The label update, // The onChange handler current, // The current value original, // The original value docs = false, // Docs to load, if any active = false, // Whether or not to upload images imgType = 'showcase', // The image type imgSubid, // The image sub-id imgSlug, // The image slug or other unique identifier to use in the image ID id = '', // An id to tie the input to the label }) => { const { t } = useTranslation(ns) const backend = useBackend() const { setLoadingStatus, LoadingStatus } = useLoadingStatus() const [url, setUrl] = useState(false) const [uploadedId, setUploadedId] = useState(false) const upload = async (img, fromUrl = false) => { setLoadingStatus([true, 'uploadingImage']) const data = { type: imgType, subId: imgSubid, slug: imgSlug, } if (fromUrl) data.url = img else data.img = img const result = await backend.uploadAnonImage(data) setLoadingStatus([true, 'allDone', true, true]) if (result.success) { update(result.data.imgId) setUploadedId(result.data.imgId) } else setLoadingStatus([true, 'backendError', true, false]) } const onDrop = useCallback( (acceptedFiles) => { const reader = new FileReader() reader.onload = async () => { if (active) upload(reader.result) else update(reader.result) } acceptedFiles.forEach((file) => reader.readAsDataURL(file)) }, [current] ) const { getRootProps, getInputProps } = useDropzone({ onDrop }) if (current) return (
) return (

{t('imgDragAndDropImageHere')}

{t('or')}

{t('or')}

setUrl(evt.target.value) : (evt) => update(evt.target.value)} /> {active && ( )}
) } /* * Input for an image that is active (it does upload the image) */ export const ActiveImageInput = (props) => /* * Input for an image that is passive (it does not upload the image) */ export const PassiveImageInput = (props) => /* * Input for a list of things to pick from */ export const ListInput = ({ update, // the onChange handler label, // The label list, // The list of items to present { val, label, desc } current, // The (value of the) current item docs = false, // Docs to load, if any }) => ( {list.map((item, i) => ( update(item.val)}>
{item.label}
{item.desc}
))}
) /* * Input for markdown content */ export const MarkdownInput = ({ label, // The label current, // The current value (markdown) update, // The onChange handler placeholder, // The placeholder content docs = false, // Docs to load, if any id = '', // An id to tie the input to the label }) => { const [activeTab, setActiveTab] = useState('edit') return (
{['edit', 'preview'].map((tab) => ( ))}
{activeTab === 'edit' ? (