diff --git a/packages/components/src/components/FileUpload/FileUpload.mdx b/packages/components/src/components/FileUpload/FileUpload.mdx new file mode 100644 index 00000000..44a45cbe --- /dev/null +++ b/packages/components/src/components/FileUpload/FileUpload.mdx @@ -0,0 +1,284 @@ +import { + Meta, + Story, + Props, + Status, +} from '../../../../../.storybook/components'; + +import * as Stories from './FileUpload.stories'; + + + +# FileUpload + + + +Allows the user to upload files to the product. It ships as two components: +`SingleFileUpload` (holds at most one file) and `MultipleFileUpload` (an ordered +list of files). Files can be dragged into the upload area or picked through the +system dialog; dropped folders are expanded recursively and each file is stamped +with its `fullPath`. + +Validation is **external**: the component never filters, dedupes, or limits +files, and `accept` is only forwarded to the native input as a browse hint — it +is _not_ enforced on drop. Use the shipped helpers (`maxFileSize`, +`isCorrectExtension`) in your `onChange`. + +## Import + +```tsx +import { SingleFileUpload, MultipleFileUpload } from '@koobiq/react-components'; +``` + +## Usage + +Selecting or dropping a file replaces the current one; removing it clears the +value. A custom `icon` can mark the selected file (for example, for images), and +`showFileSize={false}` hides the size for the single uploader. + + + +## Props + + + +## Multiple upload + +Multiple files can be dragged into the upload area or selected via the system +dialog. Files are appended in selection order (duplicates allowed) and removed by +index; the list exposes native `list` / `listitem` semantics. + + + + +### Compact variant + +For the multiple uploader, a compact view is available when it is empty. + + + +## File size + +When uploading multiple files, their size is always shown in the list. For the +single uploader, displaying the file size is configured separately via +`showFileSize` (on by default). Sizes are formatted with SI units, matching the +Angular `KbqDataSizePipe`. + + + +## Icon for selected file + +Selected files can be given a custom icon — a static `icon` for the single +uploader, or a per-row `renderFileIcon` for the multiple one. + + + +## Selecting folders or files + +`allowed` selects the browse links and captions: `file` (default), `folder` +(`webkitdirectory`), or `mixed` (both a file and a folder link). + + + +## Disabled state + +In the disabled state the component does not receive focus and drag-and-drop +does not work. + + + +## Error + +Ship `isInvalid` + `errorMessage`, and optionally a `shouldShowError` predicate +(`showErrorOnTouched` (default), `showErrorOnSubmit`, `showErrorRequiredOnSubmit`, +`showErrorOnDirty`). In the multiple uploader, mark the offending rows with the +item `hasError` flag — the invalid items are highlighted and the error message is +shown below the control. + + + + +## Height of the selected items list + +By default the list grows with the number of files. To cap it — after which the +list scrolls — set the `--fileupload-list-max-block-size` custom property via +`slotProps.dropArea`. + + + +You can also give the uploader a fixed height via `slotProps.dropArea`. + + + +## Full-screen file uploader + +Set `fullScreenDropzone` (a boolean, or a `DropzoneData` object to configure the +title/caption/size). While active, the inline drop area is disabled and a +full-viewport overlay opens whenever a file is dragged over the document; +dropping adds the files and closes the overlay. + + + +## Upload area + +The drop area does not have to be the whole screen or the uploader itself — it +can be a separate region of the page. Give the upload a `dropTargetRef` and +connect a `LocalDropzone` to it; dragging over the host opens an overlay +positioned over it and routes the drop into the connected component. + +```tsx +const target = useRef(null); + + + + +; +``` + + + +## Localization + +Captions ship for `en-US`, `ru-RU`, `pt-BR`, `es-LA` and `tk-TM`. Wrapping a +subtree in an `I18nProvider` changes the language for every uploader inside it at +once. + + + +To override only specific labels for a single instance, pass `localeConfig` +(highest precedence); the rest follow the active language. + + + +## Progress + +While an item's `loading` flag is set, its icon is replaced by a progress +indicator bound to `progress` (0–100). Drive progress by updating the value +**immutably**. + + + +Use `progressMode="indeterminate"` when the progress is unknown. + + + +Because `loading` / `progress` / `hasError` are plain fields, you can model +post-upload processing — for example, flag a file as errored once a background +check completes. + + + +## Forms + +The controlled `value` / `onChange` / `onBlur` API plugs into any form library. +Feed the field's validity into `isInvalid` / `errorMessage` and gate visibility +with `shouldShowError`. + +### Native controlled form + + + +### React Hook Form + +```tsx +import { Controller, useForm } from 'react-hook-form'; +import { SingleFileUpload, type FileItem } from '@koobiq/react-components'; + +const { control } = useForm<{ file: FileItem | null }>(); + + ( + fieldState.isTouched || formState.isSubmitted} + /> + )} +/>; +``` + +### Formik + +```tsx +import { useField } from 'formik'; +import { MultipleFileUpload, type FileItem } from '@koobiq/react-components'; + +const [field, meta, helpers] = useField('files'); + + helpers.setTouched(true)} + isInvalid={Boolean(meta.error)} + errorMessage={meta.error} + shouldShowError={() => meta.touched} +/>; +``` + +## Validation + +Validation is the app's responsibility. Run a validator in `onChange` and feed +the result into `isInvalid` / `errorMessage`. The shipped helpers (`maxFileSize`, +`isCorrectExtension`) mirror the Angular `FileValidators`. + +### Required + + + + +### File size + + + + +### File type or extension + + + + +### Mixed: required and extension + + + + +### Asynchronous + +Await a server-side check and set the error when it resolves; keep the item in +its `loading` state while the check runs. + + + +## Primitives + +Build a fully custom uploader from the exported primitives — `useFileList` +(list state), `useFileDrop` (drag-and-drop), and `FileInput` (the browse link). + + + +## Migrating from Angular `kbq-file-upload` + +- The deprecated `fileQueueChange` / `fileQueueChanged` events are **dropped** — + use `onChange` (plus `onFilesAdded` / `onFileRemoved`). +- `accept` is **not** enforced on drop (it never was); validate in `onChange`. +- `allowed` defaults to `'file'` (the Angular JSDoc said `mixed`, but the code + default was always `File`). +- `KbqFileItem.loading` / `progress` were RxJS `BehaviorSubject`s; in React they + are plain `boolean` / `number` fields — update them immutably to re-render. + +## Accessibility + +- The hidden native file input is tab-reachable; browse links are `