// Dependencies import { cloudflareImageUrl, measurementAsMm, measurementAsUnits, distanceAsMm, } from '@freesewing/utils' import { collection } from '@freesewing/collection' import { measurements as measurementsTranslations } from '@freesewing/i18n' // Context import { ModalContext } from '@freesewing/react/context/Modal' import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus' // Hooks import React, { useState, useCallback, useContext } from 'react' import { useDropzone } from 'react-dropzone' import { useBackend } from '@freesewing/react/hooks/useBackend' // Components import { Link as WebLink } from '@freesewing/react/components/Link' import { TrashIcon, ResetIcon, UploadIcon, HelpIcon } from '@freesewing/react/components/Icon' import { ModalWrapper } from '@freesewing/react/components/Modal' import { isDegreeMeasurement } from '@freesewing/config' import { Tabs, Tab } from '@freesewing/react/components/Tab' import Markdown from 'react-markdown' /* * 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 }) => ( ) /** * A helper component to render the help link in formcontrol * * @param {string|function| help - The help href of onClick method * @param {React.component} Link - An optional framework-specific link component */ const HelpLink = ({ help, Link = false }) => { if (!Link) Link = WebLink if (typeof help === 'function') return ( ) if (typeof help === 'string') return ( ) return null } /* * 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 labelTR = 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 help = false, // An optional URL/method to link/show help/docs Link = false, // An optional framework-specific link components }) => { if (labelBR && !labelBL) labelBL = const topLabelChildren = ( <> {label ? ( {label} ) : ( )} {labelTR ? {labelTR} : null} ) const bottomLabelChildren = ( <> {labelBL ? {labelBL} : null} {labelBR ? {labelBR} : null} ) return (
{forId ? ( ) : label ? (
{topLabelChildren}
) : null} {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 accordion = false, // Set this to true to not set a background color when active dense = false, // Use less padding }) => ( ) /* * Input for integers */ export const NumberInput = ({ 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 id = '', // An id to tie the input to the label labelBL = false, // Bottom-Left label labelBR = false, // Bottom-Right label max = 0, min = 220, step = 1, }) => ( update(evt.target.value)} className={`tw:daisy-input tw:w-full tw:daisy-input-bordered ${ current === original ? 'tw:daisy-input-secondary' : valid(current) ? 'tw:daisy-input-success' : 'tw:daisy-input-error' }`} {...{ max, min, step }} /> ) /* * 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 id = '', // An id to tie the input to the label labelBL = false, // Bottom-Left label labelBR = false, // Bottom-Right label }) => ( update(evt.target.value)} className={`tw:daisy-input tw:w-full tw:daisy-input-bordered tw:text-current ${ current === original ? 'tw:daisy-input-secondary' : valid(current) ? 'tw:daisy-input-success' : 'tw:daisy-input-error' }`} /> ) /* * Input for MFA code */ export const MfaInput = ({ update, // onChange handler current, // The current value id = 'mfa', // An id to tie the input to the label }) => { return ( val.length > 4} {...{ update, current, id }} placeholder="MFA Code" /> ) } /* * Input for passwords */ export const PasswordInput = ({ label, // Label to use update, // onChange handler valid, // Method that should return whether the value is valid or not current, // The current value placeholder = '¯\\_(ツ)_/¯', // The placeholder text id = '', // An id to tie the input to the label onKeyDown = false, // Optionall capture certain keys (like enter) }) => { const [reveal, setReveal] = useState(false) const extraProps = onKeyDown ? { onKeyDown } : {} return ( setReveal(!reveal)} > {reveal ? 'Hide Password' : 'Reveal Password'} } > update(evt.target.value)} className={`tw:daisy-input tw:w-full tw:daisy-input-bordered ${ valid(current) ? 'input-success' : 'input-error' }`} {...extraProps} /> ) } /* * 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 id = '', // An id to tie the input to the label labelTR = false, // Top-Right label labelBL = false, // Bottom-Left label labelBR = false, // Bottom-Right label }) => ( update(evt.target.value)} className={`tw:daisy-input tw:w-full tw:daisy-input-bordered ${ current === original ? 'tw:daisy-input-secondary' : valid(current) ? 'tw:daisy-input-success' : 'tw:daisy-input-error' }`} /> ) /* * Input for designs */ export const DesignInput = ({ label, // Label to use update, // onChange handler current, // The current value firstOption = null, // Any first option to add in addition to designs id = '', // An id to tie the input to the label }) => { return ( ) } /* * Input for an image */ export const ImageInput = ({ label, // The label update, // The onChange handler current, // The current value original, // The original value 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 backend = useBackend() const { setLoadingStatus } = useContext(LoadingStatusContext) 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 (

Drag and drop and image here

or

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 }) => ( {list.map((item, i) => ( update(item.val)}>
{item.label}
{item.desc ? (
{item.desc}
) : null}
))}
) /* * Input for markdown content */ export const MarkdownInput = ({ label, // The label current, // The current value (markdown) update, // The onChange handler placeholder, // The placeholder content id = '', // An id to tie the input to the label labelBL = false, // Bottom-Left label labelBR = false, // Bottom-Right label }) => (