@yamada-ui/file-input
Version:
Yamada UI file input component
1 lines • 10.1 kB
Source Map (JSON)
{"version":3,"sources":["../src/file-input.tsx"],"sourcesContent":["import type {\n ColorModeToken,\n CSS,\n CSSUIObject,\n HTMLUIProps,\n ThemeProps,\n} from \"@yamada-ui/core\"\nimport type { FormControlOptions } from \"@yamada-ui/form-control\"\nimport type {\n ChangeEvent,\n CSSProperties,\n FC,\n ForwardedRef,\n ReactElement,\n ReactNode,\n} from \"react\"\nimport {\n forwardRef,\n omitThemeProps,\n ui,\n useComponentMultiStyle,\n} from \"@yamada-ui/core\"\nimport {\n formControlProperties,\n useFormControlProps,\n} from \"@yamada-ui/form-control\"\nimport { useClickable } from \"@yamada-ui/use-clickable\"\nimport { useControllableState } from \"@yamada-ui/use-controllable-state\"\nimport {\n assignRef,\n cx,\n dataAttr,\n handlerAll,\n isNull,\n mergeRefs,\n splitObject,\n} from \"@yamada-ui/utils\"\nimport { cloneElement, useCallback, useMemo, useRef } from \"react\"\n\nconst defaultFormat: (value: File, index: number) => string = ({ name }) => name\n\ninterface FileInputOptions {\n children?: (files: File[] | undefined) => ReactNode\n /**\n * The component that displays uploaded files.\n */\n component?: FC<{ index: number; value: File }>\n /**\n * The initial value of the file input.\n */\n defaultValue?: File[]\n /**\n * The border color when the input is invalid.\n */\n errorBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * The border color when the input is focused.\n */\n focusBorderColor?: ColorModeToken<CSS.Property.BorderColor, \"colors\">\n /**\n * A callback that formats the name of the uploaded file.\n */\n format?: (value: File, index: number) => string\n /**\n * Ref to a reset function.\n */\n resetRef?: ForwardedRef<() => void>\n /**\n * The string to separate uploaded files.\n *\n * @default ','\n */\n separator?: string\n /**\n * The value of the file input.\n */\n value?: File[]\n /**\n * Function to be called when a file change event occurs.\n */\n onChange?: (files: File[] | undefined) => void\n}\n\ninterface InputProps\n extends Partial<Pick<HTMLInputElement, \"accept\" | \"multiple\">> {}\n\nexport interface FileInputProps\n extends Omit<HTMLUIProps, \"children\" | \"defaultValue\" | \"onChange\">,\n ThemeProps<\"Input\">,\n InputProps,\n FileInputOptions,\n FormControlOptions {}\n\n/**\n * `FileInput` is a component used for users to select files.\n *\n * @see Docs https://yamada-ui.com/components/forms/file-input\n */\nexport const FileInput = forwardRef<FileInputProps, \"input\">(\n ({ children, ...props }, ref) => {\n const [styles, mergedProps] = useComponentMultiStyle(\"FileInput\", props)\n const {\n id,\n form,\n name,\n className,\n accept,\n component,\n defaultValue,\n format = defaultFormat,\n lineClamp = 1,\n multiple,\n placeholder,\n resetRef,\n separator = \",\",\n value,\n onChange: onChangeProp,\n onClick: onClickProp,\n ...computedProps\n } = useFormControlProps(omitThemeProps(mergedProps))\n const [\n {\n \"aria-readonly\": ariaReadonly,\n disabled,\n readOnly,\n ...formControlProps\n },\n rest,\n ] = splitObject(computedProps, formControlProperties)\n\n const inputRef = useRef<HTMLInputElement>(null)\n\n const [values, setValues] = useControllableState<File[] | undefined>({\n defaultValue,\n value,\n onChange: onChangeProp,\n })\n\n const onClick = useCallback(() => {\n if (disabled || readOnly) return\n\n inputRef.current?.click()\n }, [disabled, readOnly])\n\n const onChange = useCallback(\n (ev: ChangeEvent<HTMLInputElement>) => {\n let files = !isNull(ev.currentTarget.files)\n ? Array.from(ev.currentTarget.files)\n : undefined\n\n if (!files?.length) files = undefined\n\n setValues(files)\n },\n [setValues],\n )\n\n const onReset = useCallback(() => {\n if (inputRef.current) inputRef.current.value = \"\"\n\n setValues(undefined)\n }, [setValues])\n\n assignRef(resetRef, onReset)\n\n const cloneChildren = useMemo(() => {\n if (!values?.length)\n return <ui.span lineClamp={lineClamp}>{placeholder}</ui.span>\n\n if (children) return children(values)\n\n if (component) {\n return (\n <ui.span lineClamp={lineClamp}>\n {values.map((value, index) => {\n const el = component({ index, value })\n\n const style: CSSProperties = {\n marginBlockEnd: \"0.125rem\",\n marginBlockStart: \"0.125rem\",\n marginInlineEnd: \"0.25rem\",\n }\n\n return el\n ? cloneElement(el as ReactElement, { key: index, style })\n : null\n })}\n </ui.span>\n )\n } else {\n return (\n <ui.span lineClamp={lineClamp}>\n {values.map((value, index) => {\n const isLast = values.length === index + 1\n\n return (\n <ui.span key={index} display=\"inline-block\" me=\"0.25rem\">\n {format(value, index)}\n {!isLast ? separator : null}\n </ui.span>\n )\n })}\n </ui.span>\n )\n }\n }, [children, format, lineClamp, placeholder, separator, component, values])\n\n const clickableProps = useClickable<HTMLDivElement>({\n ref,\n ...formControlProps,\n ...rest,\n disabled: disabled || readOnly,\n onClick: handlerAll(onClickProp, onClick),\n })\n\n const css: CSSUIObject = {\n alignItems: \"center\",\n cursor: !readOnly ? \"pointer\" : \"auto\",\n display: \"flex\",\n ...styles.field,\n }\n\n return (\n <>\n <ui.input\n id={id}\n ref={mergeRefs(inputRef, ref)}\n form={form}\n type=\"file\"\n name={name}\n style={{\n border: \"0px\",\n clip: \"rect(0px, 0px, 0px, 0px)\",\n height: \"1px\",\n margin: \"-1px\",\n overflow: \"hidden\",\n padding: \"0px\",\n position: \"absolute\",\n whiteSpace: \"nowrap\",\n width: \"1px\",\n }}\n aria-hidden\n aria-readonly={ariaReadonly}\n accept={accept}\n disabled={disabled}\n multiple={multiple}\n readOnly={readOnly}\n tabIndex={-1}\n onChange={onChange}\n {...formControlProps}\n />\n\n <ui.div\n className={cx(\"ui-file-input\", className)}\n data-placeholder={dataAttr(!values?.length)}\n py={values?.length && component ? \"0.125rem\" : undefined}\n __css={css}\n {...clickableProps}\n >\n {cloneChildren}\n </ui.div>\n </>\n )\n },\n)\n\nFileInput.displayName = \"FileInput\"\nFileInput.__ui__ = \"FileInput\"\n"],"mappings":"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","names":["value"]}