chore(form): limit avatar image types
This commit is contained in:
parent
bac534f5d1
commit
d5c3847f94
8 changed files with 457 additions and 74 deletions
|
@ -54,6 +54,7 @@
|
||||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
|
"@radix-ui/react-toast": "^1.1.5",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
|
@ -76,13 +77,12 @@
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"tailwind-merge": "^2.0.0",
|
"tailwind-merge": "^2.0.0",
|
||||||
"type-fest": "^4.8.2",
|
"type-fest": "^4.8.2",
|
||||||
"valibot": "^0.21.0",
|
"valibot": "^0.21.0"
|
||||||
"zod": "^3.22.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^18.4.3",
|
"@commitlint/cli": "^18.4.3",
|
||||||
"@commitlint/config-conventional": "^18.4.3",
|
"@commitlint/config-conventional": "^18.4.3",
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.1",
|
||||||
"@types/react": "^18.2.39",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-dom": "^18.2.17",
|
"@types/react-dom": "^18.2.17",
|
||||||
"@vitejs/plugin-react": "^4.2.0",
|
"@vitejs/plugin-react": "^4.2.0",
|
||||||
|
|
181
pnpm-lock.yaml
181
pnpm-lock.yaml
|
@ -38,6 +38,9 @@ dependencies:
|
||||||
'@radix-ui/react-switch':
|
'@radix-ui/react-switch':
|
||||||
specifier: ^1.0.3
|
specifier: ^1.0.3
|
||||||
version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-toast':
|
||||||
|
specifier: ^1.1.5
|
||||||
|
version: 1.1.5(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@tailwindcss/typography':
|
'@tailwindcss/typography':
|
||||||
specifier: ^0.5.10
|
specifier: ^0.5.10
|
||||||
version: 0.5.10(tailwindcss@3.3.5)
|
version: 0.5.10(tailwindcss@3.3.5)
|
||||||
|
@ -107,9 +110,6 @@ dependencies:
|
||||||
valibot:
|
valibot:
|
||||||
specifier: ^0.21.0
|
specifier: ^0.21.0
|
||||||
version: 0.21.0
|
version: 0.21.0
|
||||||
zod:
|
|
||||||
specifier: ^3.22.4
|
|
||||||
version: 3.22.4
|
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@commitlint/cli':
|
'@commitlint/cli':
|
||||||
|
@ -119,8 +119,8 @@ devDependencies:
|
||||||
specifier: ^18.4.3
|
specifier: ^18.4.3
|
||||||
version: 18.4.3
|
version: 18.4.3
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20.10.0
|
specifier: ^20.10.1
|
||||||
version: 20.10.0
|
version: 20.10.1
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.2.39
|
specifier: ^18.2.39
|
||||||
version: 18.2.39
|
version: 18.2.39
|
||||||
|
@ -198,7 +198,7 @@ devDependencies:
|
||||||
version: 6.0.1
|
version: 6.0.1
|
||||||
wxt:
|
wxt:
|
||||||
specifier: ^0.10.3
|
specifier: ^0.10.3
|
||||||
version: 0.10.3(@types/node@20.10.0)(idb-keyval@6.2.1)
|
version: 0.10.3(@types/node@20.10.1)(idb-keyval@6.2.1)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
@ -582,10 +582,10 @@ packages:
|
||||||
'@commitlint/execute-rule': 18.4.3
|
'@commitlint/execute-rule': 18.4.3
|
||||||
'@commitlint/resolve-extends': 18.4.3
|
'@commitlint/resolve-extends': 18.4.3
|
||||||
'@commitlint/types': 18.4.3
|
'@commitlint/types': 18.4.3
|
||||||
'@types/node': 18.18.13
|
'@types/node': 18.18.14
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
cosmiconfig: 8.3.6(typescript@5.3.2)
|
cosmiconfig: 8.3.6(typescript@5.3.2)
|
||||||
cosmiconfig-typescript-loader: 5.0.0(@types/node@18.18.13)(cosmiconfig@8.3.6)(typescript@5.3.2)
|
cosmiconfig-typescript-loader: 5.0.0(@types/node@18.18.14)(cosmiconfig@8.3.6)(typescript@5.3.2)
|
||||||
lodash.isplainobject: 4.0.6
|
lodash.isplainobject: 4.0.6
|
||||||
lodash.merge: 4.6.2
|
lodash.merge: 4.6.2
|
||||||
lodash.uniq: 4.5.0
|
lodash.uniq: 4.5.0
|
||||||
|
@ -1782,6 +1782,38 @@ packages:
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@radix-ui/react-toast@1.1.5(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.23.5
|
||||||
|
'@radix-ui/primitive': 1.0.1
|
||||||
|
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.39)(react@18.2.0)
|
||||||
|
'@radix-ui/react-context': 1.0.1(@types/react@18.2.39)(react@18.2.0)
|
||||||
|
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.39)(react@18.2.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.39)(react@18.2.0)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.39)(react@18.2.0)
|
||||||
|
'@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@types/react': 18.2.39
|
||||||
|
'@types/react-dom': 18.2.17
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.39)(react@18.2.0):
|
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.39)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
|
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1884,6 +1916,27 @@ packages:
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.23.5
|
||||||
|
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.39)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@types/react': 18.2.39
|
||||||
|
'@types/react-dom': 18.2.17
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@radix-ui/rect@1.0.1:
|
/@radix-ui/rect@1.0.1:
|
||||||
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
|
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1904,96 +1957,96 @@ packages:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@rollup/rollup-android-arm-eabi@4.6.0:
|
/@rollup/rollup-android-arm-eabi@4.6.1:
|
||||||
resolution: {integrity: sha512-keHkkWAe7OtdALGoutLY3utvthkGF+Y17ws9LYT8pxMBYXaCoH/8dXS2uzo6e8+sEhY7y/zi5RFo22Dy2lFpDw==}
|
resolution: {integrity: sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-android-arm64@4.6.0:
|
/@rollup/rollup-android-arm64@4.6.1:
|
||||||
resolution: {integrity: sha512-y3Kt+34smKQNWilicPbBz/MXEY7QwDzMFNgwEWeYiOhUt9MTWKjHqe3EVkXwT2fR7izOvHpDWZ0o2IyD9SWX7A==}
|
resolution: {integrity: sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-darwin-arm64@4.6.0:
|
/@rollup/rollup-darwin-arm64@4.6.1:
|
||||||
resolution: {integrity: sha512-oLzzxcUIHltHxOCmaXl+pkIlU+uhSxef5HfntW7RsLh1eHm+vJzjD9Oo4oUKso4YuP4PpbFJNlZjJuOrxo8dPg==}
|
resolution: {integrity: sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-darwin-x64@4.6.0:
|
/@rollup/rollup-darwin-x64@4.6.1:
|
||||||
resolution: {integrity: sha512-+ANnmjkcOBaV25n0+M0Bere3roeVAnwlKW65qagtuAfIxXF9YxUneRyAn/RDcIdRa7QrjRNJL3jR7T43ObGe8Q==}
|
resolution: {integrity: sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm-gnueabihf@4.6.0:
|
/@rollup/rollup-linux-arm-gnueabihf@4.6.1:
|
||||||
resolution: {integrity: sha512-tBTSIkjSVUyrekddpkAqKOosnj1Fc0ZY0rJL2bIEWPKqlEQk0paORL9pUIlt7lcGJi3LzMIlUGXvtNi1Z6MOCQ==}
|
resolution: {integrity: sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm64-gnu@4.6.0:
|
/@rollup/rollup-linux-arm64-gnu@4.6.1:
|
||||||
resolution: {integrity: sha512-Ed8uJI3kM11de9S0j67wAV07JUNhbAqIrDYhQBrQW42jGopgheyk/cdcshgGO4fW5Wjq97COCY/BHogdGvKVNQ==}
|
resolution: {integrity: sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm64-musl@4.6.0:
|
/@rollup/rollup-linux-arm64-musl@4.6.1:
|
||||||
resolution: {integrity: sha512-mZoNQ/qK4D7SSY8v6kEsAAyDgznzLLuSFCA3aBHZTmf3HP/dW4tNLTtWh9+LfyO0Z1aUn+ecpT7IQ3WtIg3ViQ==}
|
resolution: {integrity: sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-x64-gnu@4.6.0:
|
/@rollup/rollup-linux-x64-gnu@4.6.1:
|
||||||
resolution: {integrity: sha512-rouezFHpwCqdEXsqAfNsTgSWO0FoZ5hKv5p+TGO5KFhyN/dvYXNMqMolOb8BkyKcPqjYRBeT+Z6V3aM26rPaYg==}
|
resolution: {integrity: sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-x64-musl@4.6.0:
|
/@rollup/rollup-linux-x64-musl@4.6.1:
|
||||||
resolution: {integrity: sha512-Bbm+fyn3S6u51urfj3YnqBXg5vI2jQPncRRELaucmhBVyZkbWClQ1fEsRmdnCPpQOQfkpg9gZArvtMVkOMsh1w==}
|
resolution: {integrity: sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-arm64-msvc@4.6.0:
|
/@rollup/rollup-win32-arm64-msvc@4.6.1:
|
||||||
resolution: {integrity: sha512-+MRMcyx9L2kTrTUzYmR61+XVsliMG4odFb5UmqtiT8xOfEicfYAGEuF/D1Pww1+uZkYhBqAHpvju7VN+GnC3ng==}
|
resolution: {integrity: sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-ia32-msvc@4.6.0:
|
/@rollup/rollup-win32-ia32-msvc@4.6.1:
|
||||||
resolution: {integrity: sha512-rxfeE6K6s/Xl2HGeK6cO8SiQq3k/3BYpw7cfhW5Bk2euXNEpuzi2cc7llxx1si1QgwfjNtdRNTGqdBzGlFZGFw==}
|
resolution: {integrity: sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-x64-msvc@4.6.0:
|
/@rollup/rollup-win32-x64-msvc@4.6.1:
|
||||||
resolution: {integrity: sha512-QqmCsydHS172Y0Kc13bkMXvipbJSvzeglBncJG3LsYJSiPlxYACz7MmJBs4A8l1oU+jfhYEIC/+AUSlvjmiX/g==}
|
resolution: {integrity: sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
|
@ -2103,14 +2156,14 @@ packages:
|
||||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/node@18.18.13:
|
/@types/node@18.18.14:
|
||||||
resolution: {integrity: sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==}
|
resolution: {integrity: sha512-iSOeNeXYNYNLLOMDSVPvIFojclvMZ/HDY2dU17kUlcsOsSQETbWIslJbYLZgA+ox8g2XQwSHKTkght1a5X26lQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 5.26.5
|
undici-types: 5.26.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/node@20.10.0:
|
/@types/node@20.10.1:
|
||||||
resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==}
|
resolution: {integrity: sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 5.26.5
|
undici-types: 5.26.5
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2298,7 +2351,7 @@ packages:
|
||||||
'@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.5)
|
'@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.5)
|
||||||
'@types/babel__core': 7.20.5
|
'@types/babel__core': 7.20.5
|
||||||
react-refresh: 0.14.0
|
react-refresh: 0.14.0
|
||||||
vite: 5.0.4(@types/node@20.10.0)
|
vite: 5.0.4(@types/node@20.10.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2670,7 +2723,7 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001565
|
caniuse-lite: 1.0.30001565
|
||||||
electron-to-chromium: 1.4.596
|
electron-to-chromium: 1.4.597
|
||||||
node-releases: 2.0.13
|
node-releases: 2.0.13
|
||||||
update-browserslist-db: 1.0.13(browserslist@4.22.1)
|
update-browserslist-db: 1.0.13(browserslist@4.22.1)
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2876,7 +2929,7 @@ packages:
|
||||||
engines: {node: '>=12.13.0'}
|
engines: {node: '>=12.13.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.10.0
|
'@types/node': 20.10.1
|
||||||
escape-string-regexp: 4.0.0
|
escape-string-regexp: 4.0.0
|
||||||
is-wsl: 2.2.0
|
is-wsl: 2.2.0
|
||||||
lighthouse-logger: 1.4.2
|
lighthouse-logger: 1.4.2
|
||||||
|
@ -3090,7 +3143,7 @@ packages:
|
||||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/cosmiconfig-typescript-loader@5.0.0(@types/node@18.18.13)(cosmiconfig@8.3.6)(typescript@5.3.2):
|
/cosmiconfig-typescript-loader@5.0.0(@types/node@18.18.14)(cosmiconfig@8.3.6)(typescript@5.3.2):
|
||||||
resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==}
|
resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==}
|
||||||
engines: {node: '>=v16'}
|
engines: {node: '>=v16'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -3098,7 +3151,7 @@ packages:
|
||||||
cosmiconfig: '>=8.2'
|
cosmiconfig: '>=8.2'
|
||||||
typescript: '>=4'
|
typescript: '>=4'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.18.13
|
'@types/node': 18.18.14
|
||||||
cosmiconfig: 8.3.6(typescript@5.3.2)
|
cosmiconfig: 8.3.6(typescript@5.3.2)
|
||||||
jiti: 1.21.0
|
jiti: 1.21.0
|
||||||
typescript: 5.3.2
|
typescript: 5.3.2
|
||||||
|
@ -3451,8 +3504,8 @@ packages:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/electron-to-chromium@1.4.596:
|
/electron-to-chromium@1.4.597:
|
||||||
resolution: {integrity: sha512-zW3zbZ40Icb2BCWjm47nxwcFGYlIgdXkAx85XDO7cyky9J4QQfq8t0W19/TLZqq3JPQXtlv8BPIGmfa9Jb4scg==}
|
resolution: {integrity: sha512-0XOQNqHhg2YgRVRUrS4M4vWjFCFIP2ETXcXe/0KIQBjXE9Cpy+tgzzYfuq6HGai3hWq0YywtG+5XK8fyG08EjA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/emoji-regex@10.3.0:
|
/emoji-regex@10.3.0:
|
||||||
|
@ -7221,23 +7274,23 @@ packages:
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup@4.6.0:
|
/rollup@4.6.1:
|
||||||
resolution: {integrity: sha512-R8i5Her4oO1LiMQ3jKf7MUglYV/mhQ5g5OKeld5CnkmPdIGo79FDDQYqPhq/PCVuTQVuxsWgIbDy9F+zdHn80w==}
|
resolution: {integrity: sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==}
|
||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@rollup/rollup-android-arm-eabi': 4.6.0
|
'@rollup/rollup-android-arm-eabi': 4.6.1
|
||||||
'@rollup/rollup-android-arm64': 4.6.0
|
'@rollup/rollup-android-arm64': 4.6.1
|
||||||
'@rollup/rollup-darwin-arm64': 4.6.0
|
'@rollup/rollup-darwin-arm64': 4.6.1
|
||||||
'@rollup/rollup-darwin-x64': 4.6.0
|
'@rollup/rollup-darwin-x64': 4.6.1
|
||||||
'@rollup/rollup-linux-arm-gnueabihf': 4.6.0
|
'@rollup/rollup-linux-arm-gnueabihf': 4.6.1
|
||||||
'@rollup/rollup-linux-arm64-gnu': 4.6.0
|
'@rollup/rollup-linux-arm64-gnu': 4.6.1
|
||||||
'@rollup/rollup-linux-arm64-musl': 4.6.0
|
'@rollup/rollup-linux-arm64-musl': 4.6.1
|
||||||
'@rollup/rollup-linux-x64-gnu': 4.6.0
|
'@rollup/rollup-linux-x64-gnu': 4.6.1
|
||||||
'@rollup/rollup-linux-x64-musl': 4.6.0
|
'@rollup/rollup-linux-x64-musl': 4.6.1
|
||||||
'@rollup/rollup-win32-arm64-msvc': 4.6.0
|
'@rollup/rollup-win32-arm64-msvc': 4.6.1
|
||||||
'@rollup/rollup-win32-ia32-msvc': 4.6.0
|
'@rollup/rollup-win32-ia32-msvc': 4.6.1
|
||||||
'@rollup/rollup-win32-x64-msvc': 4.6.0
|
'@rollup/rollup-win32-x64-msvc': 4.6.1
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -8346,7 +8399,7 @@ packages:
|
||||||
vfile-message: 4.0.2
|
vfile-message: 4.0.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vite@5.0.4(@types/node@20.10.0):
|
/vite@5.0.4(@types/node@20.10.1):
|
||||||
resolution: {integrity: sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==}
|
resolution: {integrity: sha512-RzAr8LSvM8lmhB4tQ5OPcBhpjOZRZjuxv9zO5UcxeoY2bd3kP3Ticd40Qma9/BqZ8JS96Ll/jeBX9u+LJZrhVg==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -8374,10 +8427,10 @@ packages:
|
||||||
terser:
|
terser:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.10.0
|
'@types/node': 20.10.1
|
||||||
esbuild: 0.19.8
|
esbuild: 0.19.8
|
||||||
postcss: 8.4.31
|
postcss: 8.4.31
|
||||||
rollup: 4.6.0
|
rollup: 4.6.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -8586,7 +8639,7 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/wxt@0.10.3(@types/node@20.10.0)(idb-keyval@6.2.1):
|
/wxt@0.10.3(@types/node@20.10.1)(idb-keyval@6.2.1):
|
||||||
resolution: {integrity: sha512-2IDbcx/em7T7+f0IJN1D3Y4Hvsb34bRyOwz8nx/vTXaoysoOHG7SsR/BowTNkX0DPxUUjCdaJpzmxeWy0dHUNQ==}
|
resolution: {integrity: sha512-2IDbcx/em7T7+f0IJN1D3Y4Hvsb34bRyOwz8nx/vTXaoysoOHG7SsR/BowTNkX0DPxUUjCdaJpzmxeWy0dHUNQ==}
|
||||||
engines: {node: '>=18', pnpm: '>=8'}
|
engines: {node: '>=18', pnpm: '>=8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -8619,7 +8672,7 @@ packages:
|
||||||
rollup-plugin-visualizer: 5.9.3
|
rollup-plugin-visualizer: 5.9.3
|
||||||
unimport: 3.6.0
|
unimport: 3.6.0
|
||||||
unstorage: 1.10.1(idb-keyval@6.2.1)
|
unstorage: 1.10.1(idb-keyval@6.2.1)
|
||||||
vite: 5.0.4(@types/node@20.10.0)
|
vite: 5.0.4(@types/node@20.10.1)
|
||||||
web-ext-run: 0.1.1
|
web-ext-run: 0.1.1
|
||||||
webextension-polyfill: 0.10.0
|
webextension-polyfill: 0.10.0
|
||||||
zip-dir: 2.0.0
|
zip-dir: 2.0.0
|
||||||
|
@ -8719,10 +8772,6 @@ packages:
|
||||||
jszip: 3.10.1
|
jszip: 3.10.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/zod@3.22.4:
|
|
||||||
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/zwitch@2.0.4:
|
/zwitch@2.0.4:
|
||||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import Layout from './components/Layout'
|
import Layout from './components/Layout'
|
||||||
import ProfileForm from './components/ProfileForm'
|
import ProfileForm from './components/ProfileForm'
|
||||||
|
import { Toaster } from '@/components/ui/Toaster'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<ProfileForm></ProfileForm>
|
<ProfileForm></ProfileForm>
|
||||||
|
<Toaster />
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import React from 'react'
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/Avatar'
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/Avatar'
|
||||||
import { Label } from '@/components/ui/Label'
|
import { Label } from '@/components/ui/Label'
|
||||||
import { cn, compressImage } from '@/utils'
|
import { cn, compressImage } from '@/utils'
|
||||||
|
import { useToast } from '@/components/ui/useToast'
|
||||||
|
|
||||||
export interface AvatarSelectProps {
|
export interface AvatarSelectProps {
|
||||||
value?: string
|
value?: string
|
||||||
|
@ -17,10 +18,20 @@ export interface AvatarSelectProps {
|
||||||
|
|
||||||
const AvatarSelect = React.forwardRef<HTMLInputElement, AvatarSelectProps>(
|
const AvatarSelect = React.forwardRef<HTMLInputElement, AvatarSelectProps>(
|
||||||
({ onChange, value, onerror, onload, className, disabled }, ref) => {
|
({ onChange, value, onerror, onload, className, disabled }, ref) => {
|
||||||
|
const { toast } = useToast()
|
||||||
|
|
||||||
const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0]
|
const file = e.target.files?.[0]
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
|
if (!/image\/(png|jpeg)/.test(file.type)) {
|
||||||
|
toast({
|
||||||
|
variant: 'destructive',
|
||||||
|
title: 'Invalid file type',
|
||||||
|
description: 'Only PNG and JPEG files are supported'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Compress to 10kb
|
// Compress to 10kb
|
||||||
const blob = await compressImage(file, 10 * 1024)
|
const blob = await compressImage(file, 10 * 1024)
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
|
@ -28,7 +39,6 @@ const AvatarSelect = React.forwardRef<HTMLInputElement, AvatarSelectProps>(
|
||||||
onload?.call(reader, e)
|
onload?.call(reader, e)
|
||||||
const src = e.target?.result as string
|
const src = e.target?.result as string
|
||||||
onChange?.(src)
|
onChange?.(src)
|
||||||
console.log(file.size, blob.size)
|
|
||||||
}
|
}
|
||||||
reader.onerror = (e) => onerror?.call(reader, e)
|
reader.onerror = (e) => onerror?.call(reader, e)
|
||||||
reader.readAsDataURL(blob)
|
reader.readAsDataURL(blob)
|
||||||
|
@ -52,7 +62,7 @@ const AvatarSelect = React.forwardRef<HTMLInputElement, AvatarSelectProps>(
|
||||||
<ImagePlusIcon size={30} className="text-slate-400 group-hover:text-slate-500" />
|
<ImagePlusIcon size={30} className="text-slate-400 group-hover:text-slate-500" />
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<input ref={ref} hidden disabled={disabled} type="file" accept="image/*" onChange={handleChange} />
|
<input ref={ref} hidden disabled={disabled} type="file" accept="image/png,image/jpeg" onChange={handleChange} />
|
||||||
</Label>
|
</Label>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
111
src/components/ui/Toast.tsx
Normal file
111
src/components/ui/Toast.tsx
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
import { Cross2Icon } from '@radix-ui/react-icons'
|
||||||
|
import * as ToastPrimitives from '@radix-ui/react-toast'
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority'
|
||||||
|
|
||||||
|
import { cn } from '@/utils/index'
|
||||||
|
|
||||||
|
const ToastProvider = ToastPrimitives.Provider
|
||||||
|
|
||||||
|
const ToastViewport = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Viewport
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
||||||
|
|
||||||
|
const toastVariants = cva(
|
||||||
|
'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: 'border bg-background text-foreground',
|
||||||
|
destructive: 'destructive group border-destructive bg-destructive text-destructive-foreground'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const Toast = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
|
||||||
|
>(({ className, variant, ...props }, ref) => {
|
||||||
|
return <ToastPrimitives.Root ref={ref} className={cn(toastVariants({ variant }), className)} {...props} />
|
||||||
|
})
|
||||||
|
Toast.displayName = ToastPrimitives.Root.displayName
|
||||||
|
|
||||||
|
const ToastAction = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Action>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Action
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
ToastAction.displayName = ToastPrimitives.Action.displayName
|
||||||
|
|
||||||
|
const ToastClose = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Close>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Close
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
toast-close=""
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Cross2Icon className="h-4 w-4" />
|
||||||
|
</ToastPrimitives.Close>
|
||||||
|
))
|
||||||
|
ToastClose.displayName = ToastPrimitives.Close.displayName
|
||||||
|
|
||||||
|
const ToastTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Title ref={ref} className={cn('text-sm font-semibold [&+div]:text-xs', className)} {...props} />
|
||||||
|
))
|
||||||
|
ToastTitle.displayName = ToastPrimitives.Title.displayName
|
||||||
|
|
||||||
|
const ToastDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ToastPrimitives.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<ToastPrimitives.Description ref={ref} className={cn('text-sm opacity-90', className)} {...props} />
|
||||||
|
))
|
||||||
|
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
||||||
|
|
||||||
|
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
||||||
|
|
||||||
|
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
||||||
|
|
||||||
|
export {
|
||||||
|
type ToastProps,
|
||||||
|
type ToastActionElement,
|
||||||
|
ToastProvider,
|
||||||
|
ToastViewport,
|
||||||
|
Toast,
|
||||||
|
ToastTitle,
|
||||||
|
ToastDescription,
|
||||||
|
ToastClose,
|
||||||
|
ToastAction
|
||||||
|
}
|
24
src/components/ui/Toaster.tsx
Normal file
24
src/components/ui/Toaster.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from '@/components/ui/Toast'
|
||||||
|
import { useToast } from '@/components/ui/useToast'
|
||||||
|
|
||||||
|
export function Toaster() {
|
||||||
|
const { toasts } = useToast()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastProvider>
|
||||||
|
{toasts.map(function ({ id, title, description, action, ...props }) {
|
||||||
|
return (
|
||||||
|
<Toast key={id} {...props}>
|
||||||
|
<div className="grid gap-1">
|
||||||
|
{title && <ToastTitle>{title}</ToastTitle>}
|
||||||
|
{description && <ToastDescription>{description}</ToastDescription>}
|
||||||
|
</div>
|
||||||
|
{action}
|
||||||
|
<ToastClose />
|
||||||
|
</Toast>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<ToastViewport />
|
||||||
|
</ToastProvider>
|
||||||
|
)
|
||||||
|
}
|
187
src/components/ui/useToast.ts
Normal file
187
src/components/ui/useToast.ts
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
// Inspired by react-hot-toast library
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import type { ToastActionElement, ToastProps } from '@/components/ui/Toast'
|
||||||
|
|
||||||
|
const TOAST_LIMIT = 1
|
||||||
|
const TOAST_REMOVE_DELAY = 1000000
|
||||||
|
|
||||||
|
type ToasterToast = ToastProps & {
|
||||||
|
id: string
|
||||||
|
title?: React.ReactNode
|
||||||
|
description?: React.ReactNode
|
||||||
|
action?: ToastActionElement
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionTypes = {
|
||||||
|
ADD_TOAST: 'ADD_TOAST',
|
||||||
|
UPDATE_TOAST: 'UPDATE_TOAST',
|
||||||
|
DISMISS_TOAST: 'DISMISS_TOAST',
|
||||||
|
REMOVE_TOAST: 'REMOVE_TOAST'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
function genId() {
|
||||||
|
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
||||||
|
return count.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionType = typeof actionTypes
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| {
|
||||||
|
type: ActionType['ADD_TOAST']
|
||||||
|
toast: ToasterToast
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType['UPDATE_TOAST']
|
||||||
|
toast: Partial<ToasterToast>
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType['DISMISS_TOAST']
|
||||||
|
toastId?: ToasterToast['id']
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionType['REMOVE_TOAST']
|
||||||
|
toastId?: ToasterToast['id']
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
toasts: ToasterToast[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||||
|
|
||||||
|
const addToRemoveQueue = (toastId: string) => {
|
||||||
|
if (toastTimeouts.has(toastId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
toastTimeouts.delete(toastId)
|
||||||
|
dispatch({
|
||||||
|
type: 'REMOVE_TOAST',
|
||||||
|
toastId
|
||||||
|
})
|
||||||
|
}, TOAST_REMOVE_DELAY)
|
||||||
|
|
||||||
|
toastTimeouts.set(toastId, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reducer = (state: State, action: Action): State => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'ADD_TOAST':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'UPDATE_TOAST':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t))
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'DISMISS_TOAST': {
|
||||||
|
const { toastId } = action
|
||||||
|
|
||||||
|
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||||
|
// but I'll keep it here for simplicity
|
||||||
|
if (toastId) {
|
||||||
|
addToRemoveQueue(toastId)
|
||||||
|
} else {
|
||||||
|
state.toasts.forEach((toast) => {
|
||||||
|
addToRemoveQueue(toast.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: state.toasts.map((t) =>
|
||||||
|
t.id === toastId || toastId === undefined
|
||||||
|
? {
|
||||||
|
...t,
|
||||||
|
open: false
|
||||||
|
}
|
||||||
|
: t
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'REMOVE_TOAST':
|
||||||
|
if (action.toastId === undefined) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toasts: state.toasts.filter((t) => t.id !== action.toastId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const listeners: Array<(state: State) => void> = []
|
||||||
|
|
||||||
|
let memoryState: State = { toasts: [] }
|
||||||
|
|
||||||
|
function dispatch(action: Action) {
|
||||||
|
memoryState = reducer(memoryState, action)
|
||||||
|
listeners.forEach((listener) => {
|
||||||
|
listener(memoryState)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Toast = Omit<ToasterToast, 'id'>
|
||||||
|
|
||||||
|
function toast({ ...props }: Toast) {
|
||||||
|
const id = genId()
|
||||||
|
|
||||||
|
const update = (props: ToasterToast) =>
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_TOAST',
|
||||||
|
toast: { ...props, id }
|
||||||
|
})
|
||||||
|
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'ADD_TOAST',
|
||||||
|
toast: {
|
||||||
|
...props,
|
||||||
|
id,
|
||||||
|
open: true,
|
||||||
|
onOpenChange: (open) => {
|
||||||
|
if (!open) dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
dismiss,
|
||||||
|
update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useToast() {
|
||||||
|
const [state, setState] = React.useState<State>(memoryState)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
listeners.push(setState)
|
||||||
|
return () => {
|
||||||
|
const index = listeners.indexOf(setState)
|
||||||
|
if (index > -1) {
|
||||||
|
listeners.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
toast,
|
||||||
|
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useToast, toast }
|
|
@ -46,8 +46,6 @@ const compressImage = async (inputBlob: Blob, targetSize: number) => {
|
||||||
return inputBlob
|
return inputBlob
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageBitmap = await createImageBitmap(inputBlob)
|
|
||||||
|
|
||||||
// Initialize the range of quality
|
// Initialize the range of quality
|
||||||
const low = 0
|
const low = 0
|
||||||
const high = 1
|
const high = 1
|
||||||
|
@ -55,6 +53,8 @@ const compressImage = async (inputBlob: Blob, targetSize: number) => {
|
||||||
// Initialize bestBlob with the original input Blob
|
// Initialize bestBlob with the original input Blob
|
||||||
const bestBlob = inputBlob
|
const bestBlob = inputBlob
|
||||||
|
|
||||||
|
const imageBitmap = await createImageBitmap(inputBlob)
|
||||||
|
|
||||||
// Call the recursive function
|
// Call the recursive function
|
||||||
return await compress(imageBitmap, targetSize, low, high, bestBlob)
|
return await compress(imageBitmap, targetSize, low, high, bestBlob)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue