@ioca/react 1.5.10 → 1.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/lib/cjs/components/editor/editor.js +5 -0
  2. package/lib/cjs/components/editor/editor.js.map +1 -1
  3. package/lib/cjs/components/editor/memtion.js +45 -3
  4. package/lib/cjs/components/editor/memtion.js.map +1 -1
  5. package/lib/cjs/components/list/list.js +3 -3
  6. package/lib/cjs/components/list/list.js.map +1 -1
  7. package/lib/cjs/components/picker/colors/index.js +7 -3
  8. package/lib/cjs/components/picker/colors/index.js.map +1 -1
  9. package/lib/cjs/components/tabs/navs.js.map +1 -1
  10. package/lib/cjs/components/tabs/tabs.js.map +1 -1
  11. package/lib/cjs/components/upload/dropbox.js +54 -0
  12. package/lib/cjs/components/upload/dropbox.js.map +1 -0
  13. package/lib/cjs/components/upload/renderFile.js +33 -36
  14. package/lib/cjs/components/upload/renderFile.js.map +1 -1
  15. package/lib/cjs/components/upload/upload.js +106 -69
  16. package/lib/cjs/components/upload/upload.js.map +1 -1
  17. package/lib/cjs/js/usePreview/renderFile.js +1 -1
  18. package/lib/cjs/js/usePreview/renderFile.js.map +1 -1
  19. package/lib/css/index.css +1 -1
  20. package/lib/css/index.css.map +1 -1
  21. package/lib/es/components/editor/editor.js +5 -0
  22. package/lib/es/components/editor/editor.js.map +1 -1
  23. package/lib/es/components/editor/memtion.js +46 -4
  24. package/lib/es/components/editor/memtion.js.map +1 -1
  25. package/lib/es/components/list/list.js +4 -4
  26. package/lib/es/components/list/list.js.map +1 -1
  27. package/lib/es/components/picker/colors/index.js +6 -3
  28. package/lib/es/components/picker/colors/index.js.map +1 -1
  29. package/lib/es/components/tabs/navs.js.map +1 -1
  30. package/lib/es/components/tabs/tabs.js.map +1 -1
  31. package/lib/es/components/upload/dropbox.js +46 -0
  32. package/lib/es/components/upload/dropbox.js.map +1 -0
  33. package/lib/es/components/upload/renderFile.js +34 -37
  34. package/lib/es/components/upload/renderFile.js.map +1 -1
  35. package/lib/es/components/upload/upload.js +107 -70
  36. package/lib/es/components/upload/upload.js.map +1 -1
  37. package/lib/es/js/usePreview/renderFile.js +1 -1
  38. package/lib/es/js/usePreview/renderFile.js.map +1 -1
  39. package/lib/index.js +233 -115
  40. package/lib/types/components/list/item.d.ts +6 -0
  41. package/lib/types/components/list/list.d.ts +3 -5
  42. package/lib/types/components/picker/type.d.ts +4 -1
  43. package/lib/types/components/tabs/type.d.ts +1 -1
  44. package/lib/types/components/upload/type.d.ts +4 -8
  45. package/package.json +97 -97
package/lib/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import classNames from 'classnames';
3
- import { debounce, uid, throttle, title } from 'radash';
3
+ import { debounce, uid, throttle } from 'radash';
4
4
  import { useState, useRef, useEffect, useCallback, useMemo, Children, cloneElement, createElement, isValidElement, memo, Fragment as Fragment$1, useTransition, forwardRef, useLayoutEffect, createContext, useContext, useImperativeHandle } from 'react';
5
- import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, UndoRound, RedoRound, FormatBoldRound, FormatItalicRound, FormatUnderlinedRound, StrikethroughSRound, ClearAllRound, PlayArrowRound, PauseRound, StopRound, VolumeDownRound, VolumeOffRound, FullscreenRound, FullscreenExitRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, KeyboardDoubleArrowUpRound, SyncAltRound, VisibilityRound, VisibilityOffRound, MoreHorizRound, SearchRound, CheckRound, UnfoldMoreRound, CalendarMonthTwotone, AccessTimeRound, InfoOutlined, KeyboardArrowDownRound, ListAltRound, DriveFolderUploadOutlined, PlusSharp } from '@ricons/material';
5
+ import { SkipPreviousRound, CloseRound, MinusRound, PlusRound, InboxTwotone, UndoRound, RedoRound, FormatBoldRound, FormatItalicRound, FormatUnderlinedRound, StrikethroughSRound, ClearAllRound, PlayArrowRound, PauseRound, StopRound, VolumeDownRound, VolumeOffRound, FullscreenRound, FullscreenExitRound, FeedOutlined, AspectRatioRound, OpenInNewRound, FileDownloadOutlined, RotateRightRound, RotateLeftRound, KeyboardArrowLeftRound, KeyboardArrowRightRound, KeyboardDoubleArrowUpRound, SyncAltRound, VisibilityRound, VisibilityOffRound, MoreHorizRound, SearchRound, CheckRound, UnfoldMoreRound, CalendarMonthTwotone, AccessTimeRound, InfoOutlined, KeyboardArrowDownRound, MoveToInboxTwotone, OutboxTwotone, FilePresentOutlined, DriveFolderUploadOutlined, PlusSharp } from '@ricons/material';
6
6
  import { createRoot } from 'react-dom/client';
7
7
  import { getScrollbarSize, List as List$2 } from 'react-window';
8
8
  import { createPortal } from 'react-dom';
@@ -1752,9 +1752,9 @@ const Item$4 = (props) => {
1752
1752
  }), style: { alignItems: align, ...style }, ...restProps, children: [label !== undefined && (jsx("span", { className: 'i-list-item-label', children: label })), children] }));
1753
1753
  };
1754
1754
 
1755
- const List$1 = (props) => {
1755
+ const List$1 = forwardRef((props, ref) => {
1756
1756
  const { label, type, border, className, children, ...restProps } = props;
1757
- return (jsx("ul", { className: classNames("i-list", className), ...restProps, children: Children.map(children, (node, i) => {
1757
+ return (jsx("ul", { ref: ref, className: classNames("i-list", className), ...restProps, children: Children.map(children, (node, i) => {
1758
1758
  const renderLabel = typeof label === "function" ? label(i) : label;
1759
1759
  const { type, props: nodeProps } = node;
1760
1760
  if (type === Item$4) {
@@ -1767,7 +1767,7 @@ const List$1 = (props) => {
1767
1767
  }
1768
1768
  return node;
1769
1769
  }) }));
1770
- };
1770
+ });
1771
1771
  List$1.Item = Item$4;
1772
1772
 
1773
1773
  const Content$2 = forwardRef((props, ref) => {
@@ -2605,15 +2605,56 @@ const removeAdjacentMemtionTag = (editor, key) => {
2605
2605
  editor.focus();
2606
2606
  return true;
2607
2607
  };
2608
+ const makeRectSource = (rect) => {
2609
+ const { left, top, width, height } = rect;
2610
+ return {
2611
+ offsetParent: null,
2612
+ offsetLeft: left,
2613
+ offsetTop: top,
2614
+ getBoundingClientRect: () => ({
2615
+ left,
2616
+ top,
2617
+ right: left + width,
2618
+ bottom: top + height,
2619
+ width,
2620
+ height,
2621
+ x: left,
2622
+ y: top,
2623
+ toJSON: () => ({}),
2624
+ }),
2625
+ };
2626
+ };
2608
2627
  const Memtion = (props) => {
2609
2628
  const { visible, rect, options, activeIndex, onActiveChange, onSelect } = props;
2629
+ const containerRef = useRef(null);
2630
+ const [pos, setPos] = useState({ left: 0, top: 0 });
2631
+ const [ready, setReady] = useState(false);
2632
+ useLayoutEffect(() => {
2633
+ if (!visible || !rect || !options?.length) {
2634
+ setReady(false);
2635
+ return;
2636
+ }
2637
+ const el = containerRef.current;
2638
+ if (!el)
2639
+ return;
2640
+ const [left, top] = getPosition(makeRectSource(rect), el, {
2641
+ position: "bottom",
2642
+ gap: 4,
2643
+ offset: 0,
2644
+ align: "start",
2645
+ refWindow: true,
2646
+ });
2647
+ setPos({ left, top });
2648
+ setReady(true);
2649
+ }, [visible, rect, options]);
2610
2650
  if (!visible || !rect || !options?.length) {
2611
2651
  return null;
2612
2652
  }
2613
- const content = (jsx(List$1, { className: "i-editor-memtion", type: "option", style: {
2653
+ const content = (jsx(List$1, { ref: containerRef, className: "i-editor-memtion", type: "option", style: {
2614
2654
  position: "fixed",
2615
- top: rect.bottom,
2616
- left: rect.left,
2655
+ left: pos.left,
2656
+ top: pos.top,
2657
+ opacity: ready ? 1 : 0,
2617
2658
  }, children: options.map((option, i) => (jsx(List$1.Item, { type: "option", active: i === activeIndex, onMouseDown: (e) => e.preventDefault(), onMouseEnter: () => onActiveChange?.(i), onClick: () => onSelect?.(option), children: option.label }, `${option.value}-${i}`))) }));
2618
2659
  if (typeof document === "undefined") {
2619
2660
  return content;
@@ -2732,6 +2773,8 @@ const Editor = (props) => {
2732
2773
  };
2733
2774
  const handleKeyDown = (e) => {
2734
2775
  onKeyDown?.(e);
2776
+ if (e.defaultPrevented)
2777
+ return;
2735
2778
  if (!isPlaintextMode &&
2736
2779
  (e.key === "Backspace" || e.key === "Delete") &&
2737
2780
  removeAdjacentMemtionTag(editorRef.current, e.key)) {
@@ -2772,6 +2815,9 @@ const Editor = (props) => {
2772
2815
  exec(isRichMode ? "insertHTML" : "insertText", false, isRichMode ? "	" : "\t");
2773
2816
  break;
2774
2817
  case "Enter":
2818
+ if (e.shiftKey) {
2819
+ break;
2820
+ }
2775
2821
  if (!onEnter)
2776
2822
  break;
2777
2823
  e.preventDefault();
@@ -3657,7 +3703,7 @@ function renderFile(props) {
3657
3703
  case TFileType.VIDEO:
3658
3704
  return jsx(Video, { ...props });
3659
3705
  default:
3660
- return (jsxs("div", { className: 'i-preview-unknown', children: [jsx(Icon, { icon: jsx(FeedOutlined, {}), size: '3em' }), jsx("h5", { className: 'mt-4', children: name || suffix || "?" })] }));
3706
+ return (jsxs("div", { className: "i-preview-unknown", children: [jsx(Icon, { icon: jsx(FeedOutlined, {}), size: "3em" }), jsx("h5", { className: "mt-4", children: name || suffix })] }));
3661
3707
  }
3662
3708
  }
3663
3709
 
@@ -4615,7 +4661,7 @@ const Handle = (props) => {
4615
4661
  };
4616
4662
 
4617
4663
  function ColorPicker(props) {
4618
- const { value, type = "HEX", disabledAlpha, children, usePanel, handle = "both", placeholder = "Colors", popupProps, onChange, label, required, ...restProps } = props;
4664
+ const { value, type = "HEX", disabledAlpha, children, usePanel, handle = "both", placeholder = "Colors", popupProps, onChange, label, required, className, style, ...restProps } = props;
4619
4665
  const [colorType, setColorType] = useState(type);
4620
4666
  const [colorValue, setColorValue] = useState(value);
4621
4667
  const [syncValue, setSyncValue] = useState(value);
@@ -4659,9 +4705,11 @@ function ColorPicker(props) {
4659
4705
  if (usePanel) {
4660
4706
  return (jsx(InputContainer, { label: label, required: required, children: jsx(ColorsPanel, { ...restProps, value: value, onChange: onChange }) }));
4661
4707
  }
4662
- return (jsx(InputContainer, { label: label, required: required, children: jsx(Popup, { trigger: "click", touchable: true, position: "bottom", ...popupProps, visible: visible, content: jsx(ColorsPanel, { ...restProps, value: syncValue, disabledAlpha: disabledAlpha, panelRender: (panel) => {
4708
+ return (jsx(InputContainer, { label: label, required: required, className: classNames("i-colorpicker", className), style: style, children: jsx(Popup, { trigger: "click", touchable: true, position: "bottom", ...popupProps, visible: visible, content: jsx(ColorsPanel, { ...restProps, value: syncValue, disabledAlpha: disabledAlpha, panelRender: (panel) => {
4663
4709
  return (jsxs(Fragment, { children: [panel, jsx(Footer, { value: colorValue, type: colorType, onTypeChange: handleTypeChange, onChange: handleValueChange, onOk: handleOk })] }));
4664
- }, onChange: handleChange, onChangeComplete: handleComplete }), onVisibleChange: handleVisibleChange, children: children ?? (jsx(Handle, { color: value, handle: handle, placeholder: placeholder })) }) }));
4710
+ }, onChange: handleChange, onChangeComplete: handleComplete }), onVisibleChange: handleVisibleChange, children: typeof children === "function"
4711
+ ? children({ type: colorType, value: colorValue })
4712
+ : (children ?? (jsx(Handle, { color: value, handle: handle, placeholder: placeholder }))) }) }));
4665
4713
  }
4666
4714
 
4667
4715
  const Dates = (props) => {
@@ -5991,155 +6039,225 @@ const Tree = (props) => {
5991
6039
  return (jsx(TreeList, { data: data, selected: selectedKey, checked: checkedKeys, partofs: partofs, nodeProps: oNodeProps, onItemCheck: handleCheck, onItemSelect: handleSelect, ...restProps }));
5992
6040
  };
5993
6041
 
5994
- const ListContainer = (props) => {
5995
- const { sortable, onSortEnd, itemProps, ...restProps } = props;
5996
- const customProps = {
5997
- className: "i-upload-list",
5998
- onClick: (e) => {
5999
- e.stopPropagation();
6000
- e.preventDefault();
6001
- },
6042
+ const Dropbox = (props) => {
6043
+ const { multiple, accept, disabled, children, onChange, onDropFiles } = props;
6044
+ const [dragging, setDragging] = useState(false);
6045
+ const inputRef = useRef(null);
6046
+ const handleDragOver = (e) => {
6047
+ e.preventDefault();
6048
+ e.stopPropagation();
6049
+ };
6050
+ const handleDragEnter = (e) => {
6051
+ e.preventDefault();
6052
+ e.stopPropagation();
6053
+ if (!disabled)
6054
+ setDragging(true);
6002
6055
  };
6056
+ const handleDragLeave = (e) => {
6057
+ e.preventDefault();
6058
+ e.stopPropagation();
6059
+ setDragging(false);
6060
+ };
6061
+ const handleDrop = (e) => {
6062
+ e.preventDefault();
6063
+ e.stopPropagation();
6064
+ setDragging(false);
6065
+ if (disabled)
6066
+ return;
6067
+ const files = Array.from(e.dataTransfer.files);
6068
+ if (files.length)
6069
+ onDropFiles(files);
6070
+ };
6071
+ const handleClick = () => {
6072
+ if (!disabled)
6073
+ inputRef.current?.click();
6074
+ };
6075
+ return (jsxs("div", { className: classNames("i-upload-dropbox", dragging && "i-upload-dropbox-active"), onDragOver: handleDragOver, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDrop: handleDrop, onClick: handleClick, children: [jsx("input", { ref: inputRef, type: "file", className: "i-input-file-hidden", multiple: multiple, accept: accept, disabled: disabled, onChange: onChange }), typeof children === "function"
6076
+ ? children(dragging)
6077
+ : children || (jsx(Icon, { icon: dragging ? (jsx(MoveToInboxTwotone, {})) : (jsx(OutboxTwotone, {})), size: "2em" }))] }));
6078
+ };
6079
+
6080
+ const ListContainer = memo((props) => {
6081
+ const { sortable, onSortEnd, children, ...restProps } = props;
6003
6082
  if (!sortable) {
6004
- return jsx("div", { ...customProps, ...restProps });
6083
+ return (jsx("div", { className: "i-upload-list", onClick: (e) => {
6084
+ e.stopPropagation();
6085
+ e.preventDefault();
6086
+ }, ...restProps, children: children }));
6005
6087
  }
6006
- return (jsx(SortableContainer, { draggedItemClassName: 'i-upload-item-dragged', onSortEnd: onSortEnd, ...customProps, ...restProps }));
6007
- };
6008
- const FileListItem = (props) => {
6088
+ return (jsx(SortableContainer, { draggedItemClassName: "i-upload-item-dragged", onSortEnd: onSortEnd, className: "i-upload-list", ...restProps, children: children }));
6089
+ });
6090
+ const CloseBtn = memo(({ index, onRemove }) => (jsx(Helpericon, { active: true, className: "i-upload-delete", onClick: (e) => {
6091
+ e.stopPropagation();
6092
+ e.preventDefault();
6093
+ onRemove(index);
6094
+ } })));
6095
+ const FileListItem = memo((props) => {
6009
6096
  const { ref, mode, index, file, renderItem, onRemove, onPreview } = props;
6010
6097
  if (!file)
6011
- return "";
6012
- const { id, name, size, url, src } = file;
6098
+ return null;
6099
+ const { name, size, url, src } = file;
6013
6100
  const type = getFileType(name, file.type);
6101
+ const handleClick = useCallback(() => {
6102
+ onPreview?.(index);
6103
+ }, [onPreview, index]);
6014
6104
  if (renderItem) {
6015
6105
  return renderItem(file, index);
6016
6106
  }
6017
- const CloseBtn = (jsx(Helpericon, { active: true, className: 'i-upload-delete', onClick: (e) => {
6018
- e.stopPropagation();
6019
- e.preventDefault();
6020
- onRemove(index);
6021
- } }));
6107
+ const node = useMemo(() => {
6108
+ switch (type) {
6109
+ case TFileType.IMAGE:
6110
+ return (jsx(MemoImage, { lazyload: true, src: url || src, fit: "cover", onMouseDown: (e) => e.preventDefault() }));
6111
+ case TFileType.VIDEO:
6112
+ return jsx("video", { src: url || src, preload: "none" });
6113
+ default:
6114
+ return (jsxs(Fragment, { children: [jsx(Icon, { icon: jsx(FilePresentOutlined, {}), size: "1.5em" }), jsx("span", { className: "i-upload-file-name", children: name })] }));
6115
+ }
6116
+ }, [type, url, src, name]);
6022
6117
  switch (mode) {
6023
6118
  case "card":
6024
- let node = jsx(Fragment, {});
6025
- switch (type) {
6026
- case TFileType.IMAGE:
6027
- node = (jsx(MemoImage, { lazyload: true, src: url || src, fit: 'cover', onMouseDown: (e) => e.preventDefault() }));
6028
- break;
6029
- case TFileType.VIDEO:
6030
- node = jsx("video", { src: url || src, preload: 'none' });
6031
- break;
6032
- default:
6033
- node = (jsxs(Fragment, { children: [jsx(Icon, { icon: jsx(ListAltRound, {}) }), jsx("span", { className: 'i-upload-file-name', children: title(name) })] }));
6034
- break;
6035
- }
6036
- return (jsxs("div", { ref: ref, title: name, className: 'i-upload-item-card', onClick: () => onPreview?.(index), children: [node, CloseBtn] }));
6119
+ return (jsxs("div", { ref: ref, className: "i-upload-item-card", onClick: handleClick, children: [node, jsx(CloseBtn, { index: index, onRemove: onRemove }), name && (jsx("span", { className: "px-12 py-8 i-upload-tip", children: name }))] }));
6037
6120
  default:
6038
- return (jsxs("div", { ref: ref, className: 'i-upload-item', onClick: () => onPreview?.(index), children: [jsx("span", { children: name }), jsx("i", { className: 'i-upload-size', children: formatBytes(size ?? 0) }), CloseBtn] }, id));
6121
+ return (jsxs("div", { ref: ref, className: "i-upload-item", onClick: handleClick, children: [jsx("span", { children: name }), jsx("i", { className: "i-upload-size", children: formatBytes(size ?? 0) }), jsx(CloseBtn, { index: index, onRemove: onRemove })] }));
6039
6122
  }
6040
- };
6123
+ });
6041
6124
 
6125
+ const normalizeFiles = (files) => (files ?? []).map((item) => {
6126
+ const file = item;
6127
+ if (item instanceof File) {
6128
+ const src = file.src ?? URL.createObjectURL(item);
6129
+ Object.assign(item, {
6130
+ id: file.id ?? uid(7),
6131
+ src,
6132
+ url: file.url ?? src,
6133
+ });
6134
+ return item;
6135
+ }
6136
+ const src = file.src ?? file.name;
6137
+ return {
6138
+ ...file,
6139
+ id: file.id ?? uid(7),
6140
+ src,
6141
+ url: file.url ?? src,
6142
+ };
6143
+ });
6042
6144
  const Upload = (props) => {
6043
- const { ref, label, labelInline, value, files = [], initialFiles, placeholder, status = "normal", message, className, style, children, defaultButtonProps, mode = "default", cardSize = "4em", disabled, sortable, limit = props.multiple ? Infinity : 1, multiple, renderItem, shouldUpload = () => true, uploader, onChange, onFilesChange, onUpload, ...restProps } = props;
6044
- const [fileList, setFileListState] = useState(files);
6045
- const [uploadMessage, setUploadMessage] = useState(message);
6145
+ const { label, labelInline, value, files, placeholder, status = "normal", message, className, style, children, droppable, dropbox, getDropboxContainer, defaultButtonProps, mode = "default", cardSize = "3.2em", disabled, sortable, limit = props.multiple ? Infinity : 1, multiple, renderItem, shouldUpload = () => true, uploader, onChange, onFilesChange, onUpload, onRemove, ...restProps } = props;
6146
+ const [internalFileList, setInternalFileList] = useState([]);
6147
+ const isControlled = useMemo(() => value !== undefined || files !== undefined, [value, files]);
6148
+ const fileList = isControlled
6149
+ ? normalizeFiles(value ?? files ?? [])
6150
+ : internalFileList;
6151
+ const uploadMessage = message;
6046
6152
  const inputRef = useRef(null);
6047
6153
  const preview = usePreview();
6048
- const defBtnProps = Object.assign({
6154
+ const defBtnProps = useMemo(() => ({
6049
6155
  children: (jsxs(Fragment, { children: [jsx(Icon, { icon: jsx(DriveFolderUploadOutlined, {}) }), " \u4E0A\u4F20"] })),
6050
- }, defaultButtonProps);
6156
+ ...defaultButtonProps,
6157
+ }), [defaultButtonProps]);
6051
6158
  const trigger = useMemo(() => {
6052
6159
  if (children)
6053
6160
  return children;
6054
6161
  switch (mode) {
6055
6162
  case "card":
6056
- return (jsx(Button, { className: 'i-upload-card-btn color-5', square: true, flat: true, outline: true, disabled: disabled, children: jsx(Icon, { icon: jsx(PlusSharp, {}) }) }));
6163
+ return (jsx(Button, { className: "i-upload-card-btn color-5", square: true, flat: true, outline: true, disabled: disabled, children: jsx(Icon, { icon: jsx(PlusSharp, {}) }) }));
6057
6164
  default:
6058
6165
  return (jsx(Button, { ...defBtnProps, className: classNames("i-upload-btn", defBtnProps.className), disabled: disabled }));
6059
6166
  }
6060
- }, [mode, children]);
6061
- const handleChange = (e) => {
6062
- const files = Array.from(e.target.files || []);
6167
+ }, [mode, children, disabled, defBtnProps]);
6168
+ const handleUpload = useCallback(async (files) => {
6169
+ if (!uploader)
6170
+ return;
6171
+ const shouldUploadFiles = files.filter(shouldUpload);
6172
+ const result = await Promise.all(shouldUploadFiles.map(uploader));
6173
+ return onUpload?.(result);
6174
+ }, [uploader, shouldUpload, onUpload]);
6175
+ const processFiles = useCallback((inputFiles) => {
6063
6176
  const before = fileList;
6064
6177
  const changed = [];
6065
- files.map((f) => {
6066
- const { id, name, size, type } = f;
6067
- const same = before.find((pf) => {
6068
- const { name: n, size: s, type: t } = pf;
6069
- return n === name && s === size && t === type;
6070
- });
6071
- const src = URL.createObjectURL(f);
6072
- Object.assign(f, {
6178
+ inputFiles.forEach((file) => {
6179
+ const { id, name, size, type } = file;
6180
+ const same = before.some((pf) => pf.name === name &&
6181
+ pf.size === size &&
6182
+ pf.type === type);
6183
+ const src = URL.createObjectURL(file);
6184
+ Object.assign(file, {
6073
6185
  id: id ?? uid(7),
6074
- src: src ?? f.name,
6075
- url: src ?? f.name,
6186
+ src: src ?? file.name,
6187
+ url: src ?? file.name,
6076
6188
  });
6077
- !same && changed.push(f);
6189
+ if (!same)
6190
+ changed.push(file);
6078
6191
  });
6079
6192
  const after = [...before, ...changed];
6080
6193
  const last = after.at(-1);
6081
- const nextFiles = multiple ? after.slice(0, limit) : last ? [last] : [];
6082
- setFileListState(nextFiles);
6083
- setUploadMessage(message);
6194
+ const nextFiles = multiple
6195
+ ? after.slice(0, limit)
6196
+ : last
6197
+ ? [last]
6198
+ : [];
6199
+ return { nextFiles, changed };
6200
+ }, [fileList, multiple, limit]);
6201
+ const applyFiles = useCallback((nextFiles, changed, e) => {
6202
+ if (!isControlled)
6203
+ setInternalFileList(nextFiles);
6084
6204
  onFilesChange?.(nextFiles, changed, e);
6085
6205
  onChange?.(nextFiles, e);
6086
6206
  handleUpload(changed);
6087
- inputRef.current && (inputRef.current.value = "");
6088
- };
6089
- const handleRemove = (i) => {
6090
- const [...files] = fileList;
6207
+ }, [isControlled, onFilesChange, onChange, handleUpload]);
6208
+ const handleChange = useCallback((e) => {
6209
+ const inputFiles = Array.from(e.target.files || []);
6210
+ const { nextFiles, changed } = processFiles(inputFiles);
6211
+ applyFiles(nextFiles, changed, e);
6212
+ if (inputRef.current)
6213
+ inputRef.current.value = "";
6214
+ }, [processFiles, applyFiles]);
6215
+ const handleDropFiles = useCallback((files) => {
6216
+ const { nextFiles, changed } = processFiles(files);
6217
+ applyFiles(nextFiles, changed);
6218
+ }, [processFiles, applyFiles]);
6219
+ const handleRemove = useCallback((i) => {
6220
+ const files = [...fileList];
6091
6221
  const changed = files.splice(i, 1);
6092
6222
  URL.revokeObjectURL(changed[0]?.src || "");
6093
- setFileListState(files);
6223
+ if (!isControlled)
6224
+ setInternalFileList(files);
6094
6225
  onFilesChange?.(files, changed);
6095
6226
  onChange?.(files);
6096
- inputRef.current && (inputRef.current.value = "");
6097
- };
6098
- const handleUpload = async (files) => {
6099
- if (!uploader)
6100
- return;
6101
- const shouldUploadFiles = files.filter(shouldUpload);
6102
- const result = Promise.all(shouldUploadFiles.map(uploader));
6103
- return onUpload?.(result);
6104
- };
6105
- const handlePreview = (i) => {
6106
- preview({ items: fileList, initial: i });
6107
- };
6108
- const setFileList = (files) => {
6109
- if (!files)
6110
- return;
6111
- setFileListState(files.map((f) => {
6112
- const file = f;
6113
- return { ...file, id: file.id ?? uid(7) };
6114
- }));
6115
- };
6116
- const handleSortEnd = (before, after) => {
6117
- const [...files] = fileList;
6227
+ onRemove?.(changed[0]);
6228
+ if (inputRef.current)
6229
+ inputRef.current.value = "";
6230
+ }, [fileList, isControlled, onFilesChange, onChange, onRemove]);
6231
+ const handlePreview = useCallback((i) => {
6232
+ preview({
6233
+ items: fileList,
6234
+ initial: i,
6235
+ });
6236
+ }, [fileList, preview]);
6237
+ const handleSortEnd = useCallback((before, after) => {
6238
+ const files = [...fileList];
6118
6239
  const nextFiles = arrayMove(files, before, after);
6119
- setFileListState(nextFiles);
6240
+ if (!isControlled)
6241
+ setInternalFileList(nextFiles);
6242
+ onFilesChange?.(nextFiles, []);
6120
6243
  onChange?.(nextFiles);
6121
- };
6122
- useEffect(() => {
6123
- setUploadMessage(message);
6124
- }, [status, message]);
6125
- useEffect(() => {
6126
- setFileListState(value?.filter?.((file) => !!file.id) ?? []);
6127
- }, [value]);
6128
- useEffect(() => {
6129
- setFileList(initialFiles);
6130
- }, []);
6131
- useImperativeHandle(ref, () => ({
6132
- getFileList: () => fileList,
6133
- setFileList,
6134
- }), [fileList]);
6135
- return (jsx(InputContainer, { as: 'div', label: label, labelInline: labelInline, className: classNames("i-input-label-file", className), style: style, children: jsxs("div", { className: classNames("i-upload-inner", {
6244
+ }, [fileList, isControlled, onFilesChange, onChange]);
6245
+ return (jsx(InputContainer, { as: "div", label: label, labelInline: labelInline, className: classNames("i-input-label-file", className), style: style, children: jsxs("div", { className: classNames("i-upload-inner", {
6136
6246
  [`i-upload-${mode}`]: mode !== "default",
6137
6247
  }), style: { ["--upload-card-size"]: cardSize }, children: [jsx(ListContainer, { sortable: sortable, onSortEnd: handleSortEnd, children: fileList.map((file, i) => {
6138
- const node = (jsx(FileListItem, { index: i, file: file, mode: mode, renderItem: renderItem, onRemove: handleRemove, onPreview: handlePreview }, i));
6248
+ const f = file;
6249
+ const key = f.id ?? i;
6250
+ const node = (jsx(FileListItem, { index: i, file: f, mode: mode, renderItem: renderItem, onRemove: handleRemove, onPreview: handlePreview }, key));
6139
6251
  if (!sortable)
6140
6252
  return node;
6141
- return jsx(SortableItem, { children: node }, i);
6142
- }) }), uploadMessage && (jsx("span", { className: 'i-upload-message', children: uploadMessage })), fileList.length < limit && (jsxs("label", { children: [jsx("input", { ...restProps, disabled: disabled, ref: inputRef, type: 'file', className: 'i-input-file-hidden', multiple: multiple, onChange: handleChange }), trigger] }))] }) }));
6253
+ return jsx(SortableItem, { children: node }, key);
6254
+ }) }), uploadMessage && (jsx("span", { className: "i-upload-message", children: uploadMessage })), fileList.length < limit &&
6255
+ (droppable ? ((() => {
6256
+ const node = (jsx(Dropbox, { multiple: multiple, accept: restProps.accept, disabled: disabled, onChange: handleChange, onDropFiles: handleDropFiles, children: dropbox }));
6257
+ return getDropboxContainer
6258
+ ? createPortal(node, getDropboxContainer())
6259
+ : node;
6260
+ })()) : (jsxs("label", { children: [jsx("input", { ...restProps, disabled: disabled, ref: inputRef, type: "file", className: "i-input-file-hidden", multiple: multiple, onChange: handleChange }), trigger] })))] }) }));
6143
6261
  };
6144
6262
 
6145
6263
  const stores = new Map();
@@ -0,0 +1,6 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { IListItem } from './type.js';
3
+
4
+ declare const Item: (props: IListItem) => react_jsx_runtime.JSX.Element;
5
+
6
+ export { Item as default };
@@ -1,9 +1,7 @@
1
- import { IList, IListItem } from './type.js';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import Item from './item.js';
3
2
 
4
- declare const List: {
5
- (props: IList): react_jsx_runtime.JSX.Element;
6
- Item: (props: IListItem) => react_jsx_runtime.JSX.Element;
3
+ declare const List: typeof List & {
4
+ Item: typeof Item;
7
5
  };
8
6
 
9
7
  export { List as default };
@@ -32,7 +32,10 @@ interface IColorPicker extends Omit<ColorPickerProps, "value" | "onChange"> {
32
32
  label?: ReactNode;
33
33
  required?: boolean;
34
34
  type?: "HEX" | "RGB" | "HSB";
35
- children?: ReactNode;
35
+ children?: ReactNode | ((params: {
36
+ type: string;
37
+ value: any;
38
+ }) => ReactNode);
36
39
  popupProps?: IPopup;
37
40
  usePanel?: boolean;
38
41
  handle?: "text" | "square" | "both";
@@ -34,7 +34,7 @@ interface RefTabs {
34
34
  open: (key: string) => void;
35
35
  close: (key: string) => void;
36
36
  add: (tab: ITabItem, position?: number) => void;
37
- navs: RefObject<HTMLDivElement>;
37
+ navs: RefObject<HTMLDivElement | null>;
38
38
  }
39
39
  interface CompositionTabs extends ForwardRefExoticComponent<ITabs> {
40
40
  Item: typeof Item;
@@ -1,11 +1,9 @@
1
- import { InputHTMLAttributes, RefObject, ReactNode, ChangeEvent } from 'react';
1
+ import { InputHTMLAttributes, ReactNode, ChangeEvent } from 'react';
2
2
  import { BaseInput } from '../../type/index.js';
3
3
  import { IButton } from '../button/type.js';
4
4
 
5
5
  interface IUpload extends Omit<BaseInput, "ref">, Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange"> {
6
- ref?: RefObject<RefUpload | null>;
7
6
  files?: IFile[];
8
- initialFiles?: IFile[] | File[];
9
7
  accept?: string;
10
8
  multiple?: boolean;
11
9
  directory?: boolean;
@@ -13,6 +11,8 @@ interface IUpload extends Omit<BaseInput, "ref">, Omit<InputHTMLAttributes<HTMLI
13
11
  sortable?: boolean;
14
12
  mode?: "default" | "card";
15
13
  droppable?: boolean;
14
+ dropbox?: (dragging?: boolean) => ReactNode;
15
+ getDropboxContainer?: () => HTMLElement;
16
16
  cardSize?: string;
17
17
  defaultButtonProps?: IButton;
18
18
  shouldUpload?: (file: IFile) => boolean;
@@ -29,9 +29,5 @@ interface IFile extends File {
29
29
  url?: string;
30
30
  [key: string]: any;
31
31
  }
32
- interface RefUpload {
33
- getFileList: () => IFile[];
34
- setFileList: (files?: IFile[] | File[]) => void;
35
- }
36
32
 
37
- export type { IFile, IUpload, RefUpload };
33
+ export type { IFile, IUpload };