From f4465584e3c142f08bb5a440347914aeda62229c Mon Sep 17 00:00:00 2001 From: yusixian <3450414733@qq.com> Date: Mon, 4 Sep 2023 02:02:14 +0800 Subject: [PATCH 1/2] Squashed commit of the following: commit cbcf7f898c71cee3d96d27a8d59477f004cb709b Merge: aed906f c3039da Author: cos <43498495+yusixian@users.noreply.github.com> Date: Mon Sep 4 02:01:02 2023 +0800 Merge pull request #54 from dancing-team/changeset-release/main Version Packages commit c3039dab59c1ab51fd9151026c2577570a67347d Author: yusixian <3450414733@qq.com> Date: Mon Sep 4 01:59:21 2023 +0800 chore: update pnpm-lock.yaml commit f460e3516f1b4769fdc02866f6ee9c9e0f5722c7 Author: github-actions[bot] Date: Sun Sep 3 17:58:18 2023 +0000 ci: release commit aed906f7f68a3eab188c81608f863c56e9fd2fea Merge: 5d08125 deed38d Author: cos <43498495+yusixian@users.noreply.github.com> Date: Mon Sep 4 01:55:31 2023 +0800 Merge pull request #53 from dancing-team/develop Develop --- .changeset/brown-lobsters-cry.md | 6 ------ .changeset/loud-carpets-pull.md | 7 ------- packages/components/CHANGELOG.md | 10 ++++++++++ packages/components/package.json | 2 +- packages/demo/CHANGELOG.md | 12 ++++++++++++ packages/demo/package.json | 4 ++-- packages/example/CHANGELOG.md | 13 +++++++++++++ packages/example/package.json | 4 ++-- pnpm-lock.yaml | 4 ++-- 9 files changed, 42 insertions(+), 20 deletions(-) delete mode 100644 .changeset/brown-lobsters-cry.md delete mode 100644 .changeset/loud-carpets-pull.md diff --git a/.changeset/brown-lobsters-cry.md b/.changeset/brown-lobsters-cry.md deleted file mode 100644 index ea83a24..0000000 --- a/.changeset/brown-lobsters-cry.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@dance-ui/ui': patch -'@dance-ui/example': patch ---- - -fix: Button styles & Tab Indicator diff --git a/.changeset/loud-carpets-pull.md b/.changeset/loud-carpets-pull.md deleted file mode 100644 index d03f91f..0000000 --- a/.changeset/loud-carpets-pull.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@dance-ui/ui': minor -'@dance-ui/example': minor -'@dance-ui/demo': minor ---- - -新增 RadioGroup 组件 diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 2712921..0b5401d 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,5 +1,15 @@ # @dance-ui/ui +## 0.3.0 + +### Minor Changes + +- 8b4100f: 新增 RadioGroup 组件 + +### Patch Changes + +- aa8702b: fix: Button styles & Tab Indicator + ## 0.2.0 ### Minor Changes diff --git a/packages/components/package.json b/packages/components/package.json index bc7e1fd..5bfec24 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@dance-ui/ui", - "version": "0.2.0", + "version": "0.3.0", "description": "🌸 A simple and elegant component library.", "keywords": [ "components", diff --git a/packages/demo/CHANGELOG.md b/packages/demo/CHANGELOG.md index 3fe7ccd..16dfa50 100644 --- a/packages/demo/CHANGELOG.md +++ b/packages/demo/CHANGELOG.md @@ -1,5 +1,17 @@ # @dance-ui/demo +## 0.2.0 + +### Minor Changes + +- 8b4100f: 新增 RadioGroup 组件 + +### Patch Changes + +- Updated dependencies [aa8702b] +- Updated dependencies [8b4100f] + - @dance-ui/ui@0.3.0 + ## 0.1.1 ### Patch Changes diff --git a/packages/demo/package.json b/packages/demo/package.json index 62622a7..cd52d39 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -1,7 +1,7 @@ { "name": "@dance-ui/demo", "private": true, - "version": "0.1.1", + "version": "0.2.0", "type": "module", "scripts": { "dev": "vite", @@ -11,7 +11,7 @@ "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", - "@dance-ui/ui": "workspace:^0.2.0" + "@dance-ui/ui": "workspace:^0.3.0" }, "devDependencies": { "@types/react": "^18.0.26", diff --git a/packages/example/CHANGELOG.md b/packages/example/CHANGELOG.md index 8ba0d59..9501db7 100644 --- a/packages/example/CHANGELOG.md +++ b/packages/example/CHANGELOG.md @@ -1,5 +1,18 @@ # @dance-ui/example +## 0.4.0 + +### Minor Changes + +- 8b4100f: 新增 RadioGroup 组件 + +### Patch Changes + +- aa8702b: fix: Button styles & Tab Indicator +- Updated dependencies [aa8702b] +- Updated dependencies [8b4100f] + - @dance-ui/ui@0.3.0 + ## 0.3.0 ### Minor Changes diff --git a/packages/example/package.json b/packages/example/package.json index 46e8d57..8939f2c 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -1,6 +1,6 @@ { "name": "@dance-ui/example", - "version": "0.3.0", + "version": "0.4.0", "description": "🌸 A simple and elegant component library document site.", "authors": [ "cosine_yu@qq.com" @@ -23,7 +23,7 @@ "typecheck": "tsc" }, "dependencies": { - "@dance-ui/ui": "workspace:^0.2.0", + "@dance-ui/ui": "workspace:^0.3.0", "@docusaurus/core": "2.2.0", "@docusaurus/preset-classic": "2.2.0", "@mdx-js/react": "^1.6.22", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5033ae3..697b5b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -158,7 +158,7 @@ importers: packages/demo: dependencies: '@dance-ui/ui': - specifier: workspace:^0.2.0 + specifier: workspace:^0.3.0 version: link:../components react: specifier: ^18.2.0 @@ -186,7 +186,7 @@ importers: packages/example: dependencies: '@dance-ui/ui': - specifier: workspace:^0.2.0 + specifier: workspace:^0.3.0 version: link:../components '@docusaurus/core': specifier: 2.2.0 From d7d13e83d02e89c0879ec313a2e5991d9a1ad0c2 Mon Sep 17 00:00:00 2001 From: yusixian <3450414733@qq.com> Date: Sun, 10 Sep 2023 21:04:34 +0800 Subject: [PATCH 2/2] feat: add ImageSelector --- .changeset/tidy-humans-pump.md | 7 + .../src/ImageSelector/ImageSelector.tsx | 135 ++++++++++++++++++ .../src/ImageSelector/demo/index.tsx | 72 ++++++++++ .../components/src/ImageSelector/index.ts | 3 + packages/components/src/index.ts | 2 + .../example/docs/components/ImageSelector.mdx | 57 ++++++++ 6 files changed, 276 insertions(+) create mode 100644 .changeset/tidy-humans-pump.md create mode 100644 packages/components/src/ImageSelector/ImageSelector.tsx create mode 100644 packages/components/src/ImageSelector/demo/index.tsx create mode 100644 packages/components/src/ImageSelector/index.ts create mode 100644 packages/example/docs/components/ImageSelector.mdx diff --git a/.changeset/tidy-humans-pump.md b/.changeset/tidy-humans-pump.md new file mode 100644 index 0000000..dbe425b --- /dev/null +++ b/.changeset/tidy-humans-pump.md @@ -0,0 +1,7 @@ +--- +'@dance-ui/ui': patch +'@dance-ui/example': patch +'@dance-ui/demo': patch +--- + +feat: 新增图片选择器组件 ImageSelector diff --git a/packages/components/src/ImageSelector/ImageSelector.tsx b/packages/components/src/ImageSelector/ImageSelector.tsx new file mode 100644 index 0000000..e34fba0 --- /dev/null +++ b/packages/components/src/ImageSelector/ImageSelector.tsx @@ -0,0 +1,135 @@ +import { ChangeEvent, Ref, forwardRef, useCallback, useImperativeHandle, useRef } from 'react' +import { twMerge } from 'tailwind-merge' +import Icon, { IconType } from '../Icon' + +export type ImageSelectorProps = { + name?: string + className?: string + itemClass?: string + defaultImages?: string[] + maxSize?: number + images: string[] + onChange: (images: string[]) => void + upload: (file: File) => Promise // 上传函数,成功返回图片url,失败返回false + onError?: (file: File) => void + addButtonClass?: string + renderAddButton?: ({ triggerFileInput }: { triggerFileInput: () => void }) => JSX.Element + closeIconClass?: string + renderCloseIcon?: ({ handleRemoveImage, index }: { index: number; handleRemoveImage: (index: number) => void }) => JSX.Element +} +const MAX_IMAGE_SIZE = 3 * 1024 * 1024 // 3M in bytes +const ImageSelector = forwardRef( + ( + { + name, + className, + itemClass, + defaultImages, + images, + onChange, + maxSize = MAX_IMAGE_SIZE, + upload, + onError, + + addButtonClass, + renderAddButton, + closeIconClass, + renderCloseIcon, + }: ImageSelectorProps, + ref: Ref, + ) => { + const fileInputRef = useRef(null) + const handleImageChange = useCallback( + async (e: ChangeEvent) => { + if (e.target.files) { + const filesArray = Array.from(e.target.files).filter((file) => { + if (file.size > maxSize) { + onError?.(file) + return false + } + return true + }) + try { + const res = await Promise.all(filesArray.map(upload)) + const urls = res.filter((url) => { + if (url) return true + return false + }) + onChange([...images, ...urls]) + } catch (e) { + console.log(e) + } + } + }, + [images, maxSize, onChange, onError, upload], + ) + + const handleRemoveImage = (index: number) => { + const updatedImages = [...images] + updatedImages.splice(index, 1) + onChange(updatedImages) + } + // 使用useImperativeHandle来同步内部ref和外部ref + useImperativeHandle(ref, () => fileInputRef.current) + + const triggerFileInput = () => { + fileInputRef.current?.click() + } + + return ( +
+ {defaultImages?.length + ? defaultImages.map((url) => ( +
+ {url} +
+ )) + : null} + {images.map((url, index) => ( +
+ {url} + {renderCloseIcon ? ( + renderCloseIcon({ handleRemoveImage, index }) + ) : ( + { + handleRemoveImage(index) + }} + type={IconType.CLOSE} + className={twMerge('absolute -right-3 -top-3 h-6 w-6 cursor-pointer fill-red-500', closeIconClass)} + /> + )} +
+ ))} + { + void handleImageChange(e) + }} + className="hidden" + /> + {renderAddButton ? ( + renderAddButton({ triggerFileInput }) + ) : ( +
+ + +
+ )} +
+ ) + }, +) +ImageSelector.displayName = 'ImageSelector' +ImageSelector.defaultProps = { + maxSize: MAX_IMAGE_SIZE, +} + +export default ImageSelector diff --git a/packages/components/src/ImageSelector/demo/index.tsx b/packages/components/src/ImageSelector/demo/index.tsx new file mode 100644 index 0000000..8f6669f --- /dev/null +++ b/packages/components/src/ImageSelector/demo/index.tsx @@ -0,0 +1,72 @@ +import React, { useState } from 'react' +import { Space, ImageSelector } from '@dance-ui/ui' + +export default () => { + const [selectedImages, setSelectedImages] = useState([]) + + const upload = (file: File) => { + console.log(`uploadingImg`, file) + return 'https://fakeimg.pl/350x200/?text=MockUploadBackUrl' + } + const handleImagesSelected = (urls: string[]) => { + console.log(`handleImagesSelected`, urls) + setSelectedImages(urls) + } + + const handleFileError = (file: File) => { + console.error(`File upload error: ${file.name}`) + } + + const renderAddButton = ({ triggerFileInput }: { triggerFileInput: () => void }) => ( +
+ Custom Add Button +
+ ) + + const renderCloseIcon = ({ handleRemoveImage, index }: { index: number; handleRemoveImage: (index: number) => void }) => ( +
handleRemoveImage(index)} + className="flex items-center justify-center" + style={{ + position: 'absolute', + backgroundColor: 'red', + color: 'white', + top: '-12px', + right: '-12px', + borderRadius: '100%', + width: '24px', + height: '24px', + }}> + x +
+ ) + return ( + + +

基础使用

+ +
+ +

default不可修改的主图

+ +
+ +

自定义添加按钮和关闭图标

+ +
+
+ ) +} diff --git a/packages/components/src/ImageSelector/index.ts b/packages/components/src/ImageSelector/index.ts new file mode 100644 index 0000000..23e2830 --- /dev/null +++ b/packages/components/src/ImageSelector/index.ts @@ -0,0 +1,3 @@ +import ImageSelector from './ImageSelector' +export type { ImageSelectorProps } from './ImageSelector' +export default ImageSelector diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index cc8952b..410d232 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -33,3 +33,5 @@ export { default as Tabs } from './Tabs' export { default as DatePicker } from './DatePicker' export { default as RadioGroup } from './RadioGroup' + +export { default as ImageSelector } from './ImageSelector' diff --git a/packages/example/docs/components/ImageSelector.mdx b/packages/example/docs/components/ImageSelector.mdx new file mode 100644 index 0000000..8702aed --- /dev/null +++ b/packages/example/docs/components/ImageSelector.mdx @@ -0,0 +1,57 @@ +--- +sidebar_position: 6 +--- + +import ComponentSource from '!!raw-loader!../../../components/src/ImageSelector/ImageSelector' +import { ImageSelector } from '@dance-ui/ui' + +# ImageSelector 图片选择器 + +`ImageSelector` 是一个用于上传和显示图片的组件。它允许用户选择多张图片,并提供了一个简单的界面来查看和删除已上传的图片。 + +### API + +#### ImageSelectorProps + +| 属性 | 说明 | 类型 | 默认值 | +| --------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | --------------- | +| name | input 文件输入的 name 属性 | string | - | +| className | 组件的类名 | string | - | +| itemClass | 图片项的类名 | string | - | +| defaultImages | 默认显示的图片数组 | string[] | - | +| maxSize | 允许上传的最大文件大小(以字节为单位) | number | 3 _ 1024 _ 1024 | +| images | 当前已上传的图片的 URL 数组 | string[] | - | +| onChange | 当图片数组发生变化时的回调函数,参数为当前的图片 URL 数组 | (images: string[]) => void | - | +| upload | 上传函数,成功返回图片 url,失败返回 null | `(file: File) => Promise` | - | +| onError | 文件上传错误时的回调函数 | (file: File) => void | - | +| addButtonClass | 添加按钮的类名 | string | - | +| renderAddButton | 自定义渲染添加按钮的函数 | ({ triggerFileInput }: { triggerFileInput: () => void }) => JSX.Element | - | +| closeIconClass | 关闭图标的类名 | string | - | +| renderCloseIcon | 自定义渲染关闭图标的函数 | ({ handleRemoveImage, index }: { index: number; handleRemoveImage: (index: number) => void }) => JSX.Element | - | + +### 代码演示 + +#### 基本使用 + +在这个示例中,我们展示了 `ImageSelector` 组件的基本使用。用户可以通过点击 "+" 按钮来上传新的图片。上传的图片 URL 是一个模拟的 URL。 + +import DemoSrc from '!!raw-loader!../../../components/src/ImageSelector/demo' +import Demo from '../../../components/src/ImageSelector/demo' + + + + + +### 注意 + +1. `maxSize` 属性定义了可以上传的最大文件大小字节数,其默认值为 3MB (3*1024*1024)。 +2. 上传函数 `upload` 是一个必须实现的函数,它接收一个文件对象作为参数,并返回一个 Promise。如果上传成功,Promise 应该解析为图片的 URL;如果上传失败,应该解析为 null。 +3. `onError` 函数是一个可选的回调,它在文件上传错误时被调用,接收失败的文件对象作为参数。 +4. `renderAddButton` 和 `renderCloseIcon` 允许你自定义添加按钮和关闭图标的渲染。 +5. 删除图片时,目前的实现有一个小错误,它总是删除第一张图片而不是选定的图片。你应该使用 `updatedImages.splice(index, 1)` 而不是 `updatedImages.shift()` 来修复这个问题。 + +### 组件源码 + + + {ComponentSource} +