layers drag drope done

This commit is contained in:
Artur AGH
2023-12-08 15:06:18 +01:00
parent 35b7eaad23
commit d96efb5303
27 changed files with 936 additions and 214 deletions

View File

@@ -11,6 +11,9 @@
"start": "next start --port 3333"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@headlessui/react": "^1.7.17",
"@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.1.1",
@@ -38,6 +41,7 @@
"@trpc/react-query": "^10.37.1",
"@trpc/server": "^10.37.1",
"@types/uuid": "^9.0.3",
"@uiw/react-color": "^2.0.3",
"axios": "^1.5.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",

372
pnpm-lock.yaml generated
View File

@@ -5,6 +5,15 @@ settings:
excludeLinksFromLockfile: false
dependencies:
'@dnd-kit/core':
specifier: ^6.1.0
version: 6.1.0(react-dom@18.2.0)(react@18.2.0)
'@dnd-kit/sortable':
specifier: ^8.0.0
version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0)
'@dnd-kit/utilities':
specifier: ^3.2.2
version: 3.2.2(react@18.2.0)
'@headlessui/react':
specifier: ^1.7.17
version: 1.7.17(react-dom@18.2.0)(react@18.2.0)
@@ -86,6 +95,9 @@ dependencies:
'@types/uuid':
specifier: ^9.0.3
version: 9.0.6
'@uiw/react-color':
specifier: ^2.0.3
version: 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
axios:
specifier: ^1.5.1
version: 1.5.1
@@ -217,6 +229,49 @@ packages:
dependencies:
regenerator-runtime: 0.14.0
/@dnd-kit/accessibility@3.1.0(react@18.2.0):
resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==}
peerDependencies:
react: '>=16.8.0'
dependencies:
react: 18.2.0
tslib: 2.6.2
dev: false
/@dnd-kit/core@6.1.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@dnd-kit/accessibility': 3.1.0(react@18.2.0)
'@dnd-kit/utilities': 3.2.2(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
tslib: 2.6.2
dev: false
/@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0):
resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==}
peerDependencies:
'@dnd-kit/core': ^6.1.0
react: '>=16.8.0'
dependencies:
'@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0)
'@dnd-kit/utilities': 3.2.2(react@18.2.0)
react: 18.2.0
tslib: 2.6.2
dev: false
/@dnd-kit/utilities@3.2.2(react@18.2.0):
resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
peerDependencies:
react: '>=16.8.0'
dependencies:
react: 18.2.0
tslib: 2.6.2
dev: false
/@eslint-community/eslint-utils@4.4.0(eslint@8.52.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2175,6 +2230,323 @@ packages:
eslint-visitor-keys: 3.4.3
dev: true
/@uiw/color-convert@2.0.3(@babel/runtime@7.23.2):
resolution: {integrity: sha512-lP/dnZzaiUDMrn3F+WYAG/TLhIanoy5A0HAPEO6Z/s3Jv5AI0nOM2bVOm0HI+vNiHZkVTkE1oGP0k+endEA+9A==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
dependencies:
'@babel/runtime': 7.23.2
dev: false
/@uiw/react-color-alpha@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-bTO0jMTT2fFZr8lGpDrOCbMSbidkgYMuCmyc6A8p6WfyCznKZvKG5WG9KukDOB2ReJUZKIaiR20CgUa6opS3Hw==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-drag-event-interactive': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-block@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-KDl4PBtv8/IAQ/HeonxsFka+9ppBTjKBhI7U/j6oxqvVPWuvqOZSGlAtr9oIGZnEG14CXZPg4xNVLkZQkfJ8vg==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-editable-input': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-swatch': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-chrome@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-qT9mdTf1wc1zTAb5K2sAs4VtNM9GwcR36ZeCbrLYLRMpovu4ynsZBAn0hR4RYc1xJJsedRZtelWPInAKLvwyEA==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-alpha': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input-hsla': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input-rgba': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-github': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-hue': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-saturation': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-circle@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-IBMzGOSVQvN/x5dAFk2j1bkGzWX/nWz9kmbcdETRhIQtBcP1AmrQHaRL3E+GX7oTB6cRMg2W20baxHgj97NQIQ==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-swatch': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-colorful@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-IjnvYyroW/Kmz1ICSwQ6HUpl7cs6Rkh2mVuFGvYd8FOxBjbQuAI/pfVi8I54rgkesn8uRVYlbvl4ROfAl3RrHw==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-alpha': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-hue': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-saturation': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-compact@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-xjPn46zuFZZ0RqEB8ZelSdtU0ySpp4Ekgw+Wu/W7ZZrUGBXypgFSbQqudggOq7ovhU+4qxGOGqrN4SGHqH36mg==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-editable-input': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input-rgba': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-swatch': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-editable-input-hsla@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-RwFgPlOv/OjAtegCYIsoIFbWHhAEE44SMT/44zGlRfYq6PX3KvXjEfHpM7F7EFJHYuRfKFpQRhikc1kSkOeWVw==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-editable-input-rgba': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-editable-input-rgba@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-ROz25EVr5F9XgNLG9p0RKMTgzg5ORh2RxMKC8Ch3vXrdTt4FL350Kq0N5KMHs7gyQAzcr4GQmuaCLlbEHHYAwA==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-editable-input': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-editable-input@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Zy+BuvtQ5c2XcSPN+zgVQ0gnz4fl+1YB1kpKnTYNyZptL2HXWPygwz2OdfNB/bVNRR34s5/4x3jD/zNQTBe60w==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-github@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-pMv6lc76UmReAKtKlNSyHoFTpeeyD4z2Vr51uiiQqHS09wXisyPRX0KvpvWnGtBXXci9dkOx8kpohwhiKGpPHw==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-swatch': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-hue@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Bi9/JkLH6FSxULC52y7kjLlunkRbL6TRuAzm5+BBs/4tSf+VL8Xp5Dr2gCPJG1vuA4ohDx9vl3P5QO4Rk9h8wQ==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-alpha': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-material@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-xgJ76LvF+LM+8emUby1EwcIwdtTLlA0JpQePwZi78J1DY3gQs9tTWHCaAkqZoprHiNlzXTHtqi8Wg/i0MIPLPw==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-editable-input': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input-rgba': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-saturation@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-+AZM3WemWv7w/uRGmSY5lXWdSJoKKysKMW+C7/qkWNQmN/WjG6YmC4ZtcEqPr7BbWFXSS2DJTn15fNBvxCFKDg==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-drag-event-interactive': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-shade-slider@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-TTxNkzExgurWKklC90+D1HbtRMD37wKoK9kOLOvLynEm3r5SgcifjvZLNIBC8CUTbQutazAKl1IXgQCST09ang==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-alpha': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-sketch@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OS7nD7DlsYhtQJrSQOwM2kXcLrpqYzbLcY6dy55odmgMPx1ixbhhgXfDp8zAPOT5+guPAOdfy4PV4fpUGGYiSg==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-alpha': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input-rgba': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-hue': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-saturation': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-swatch': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-slider@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-ejIVLFVwNYGlGqesJOwOgw1DYBljeHB2pznJCQI/YZyVVq9pM/fjDCjpxFQ/BgU7k/Ezs/UW90QHIxOy+tct4g==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-alpha': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-swatch@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-xKUASrSB4wPQ+kmMV1Yr1gQ9BZvMzkdotr9o+CYFGvZNSLclEur0cIhu5RkBhZ6PGP6Vd54mod43amY/a905lw==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color-wheel@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-IEQ75777ZNvaA2dunTc/zD7OmcwbTjKeLGZ7Mo8ID+Em0rLF7SCC+9g2pF8G468MAc9HwyfRjv8K6Z69PWUyNg==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-drag-event-interactive': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-color@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-W2zin6cxq1clQAJoUSbFbp07SkMKtYaoCOK1dLGE0OUM/oYMEw9eXTa8VbvVCY1e3plkXnA+/HgiCFBm6tVsQg==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
'@uiw/color-convert': 2.0.3(@babel/runtime@7.23.2)
'@uiw/react-color-alpha': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-block': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-chrome': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-circle': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-colorful': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-compact': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input-hsla': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-editable-input-rgba': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-github': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-hue': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-material': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-saturation': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-shade-slider': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-sketch': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-slider': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-swatch': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
'@uiw/react-color-wheel': 2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@uiw/react-drag-event-interactive@2.0.3(@babel/runtime@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-r9VQbvpk2z0ux3GenmT5K4BVQqT91eBSDQjYuR43Ya0kJaD2yJPuPe8JqvKTFR3/C9kldolJoWbxl0mbuu1X0w==}
peerDependencies:
'@babel/runtime': '>=7.19.0'
react: '>=16.9.0'
react-dom: '>=16.9.0'
dependencies:
'@babel/runtime': 7.23.2
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@ungap/structured-clone@1.2.0:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
dev: true

11
src/assets/12.svg Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CDA150;}
.st1{font-family:'GaramondThree';}
.st2{font-size:16px;}
</style>
<text transform="matrix(0.4439 0 0 1 283.2148 425.3496)" class="st0 st1 st2">12% vol.</text>
</svg>

After

Width:  |  Height:  |  Size: 575 B

27
src/assets/FE.svg Normal file
View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CDA150;}
</style>
<g>
<path class="st0" d="M296.14,421.35c-0.14-0.22-0.71-0.39-0.53-0.4c0.18-0.01,0.53-0.47,0.71-0.56c0.18-0.08,0.21,0,0.45,1.3
c0,0.01,0,0.02,0.01,0.04C296.56,421.64,296.23,421.5,296.14,421.35z M296.45,417.75c0,0,0.2-0.01,0.22,0.1
c0.01,0.11,0.01,0.57-0.25,0.67C296.42,418.52,296.27,417.96,296.45,417.75z M298.97,417.72c0.02-0.05,0.09-0.16,0.23-0.28
c0.15-0.12,0.31-0.19,0.36-0.21C299.5,417.61,299.11,417.7,298.97,417.72z M300.03,421.44c0.15-0.06,0.54-2.56,0.56-2.76
c0.01-0.19-0.08-0.32-0.11-0.5c-0.03-0.18-0.39-0.17-0.39-0.17s-0.52-0.85-0.52-0.84c-0.01,0-0.21,0.07-0.4,0.23
c-0.19,0.16-0.25,0.31-0.25,0.32h0l0.67,0.72c-0.07,0.19,0.35,0.25,0.42,0.49c0.07,0.24-0.38,1.42-0.53,1.49
c-0.15,0.07-1-0.91-1.18-1.14c-0.18-0.24-0.7-0.42-0.7-0.42c-0.1-0.14-0.11-0.4,0.03-0.61c0.14-0.21,0.4-0.11,0.63-0.14
c0.22-0.03,0.08-0.45,0.07-0.57c-0.01-0.13,0.11-0.08,0.18-0.11c0.07-0.03-0.05-0.24-0.14-0.34c-0.08-0.1-0.1-0.27-0.13-0.61
c-0.03-0.35-0.64-0.68-1.11-0.67c-0.47,0.01-0.71,0.25-0.86,0.49c-0.15,0.24-0.14,0.27-0.32,0.17c-0.18-0.1-0.24,0.55-0.42,0.77
c-0.18,0.22-0.21,1.17-0.21,1.17l0.58,0.88c-0.6,0.4-1.35,1.53-1.44,1.73c-0.08,0.19-0.04,0.24,0.6,0.61
c0.52,0.3,1.43,0.59,1.76,0.69c-0.07,0.88-0.61,1.36-0.69,1.68c-0.1,0.39,0.18,0.85,0.39,1.1c0.21,0.25,0.11,0.61,0.11,0.61
l0.92,0.11l0.91-0.13l0.95-0.25c-0.14-0.24-0.06-0.68,0-1c0.06-0.32,0.78-0.64,0.84-1.39c0.05-0.75-0.99-1.85-0.99-1.85
C299.38,421.42,299.87,421.5,300.03,421.44z"/>
<path class="st0" d="M300.21,424.88l-5.92-8.32c0.85-0.63,1.9-1.01,3.04-1.01c2.82,0,5.11,2.29,5.11,5.11
C302.43,422.41,301.55,423.96,300.21,424.88z M292.22,420.66c0-1.48,0.63-2.81,1.63-3.74l5.87,8.25c-0.72,0.38-1.53,0.6-2.4,0.6
C294.51,425.77,292.22,423.48,292.22,420.66z M297.32,414.99c-3.13,0-5.67,2.54-5.67,5.67c0,3.13,2.54,5.67,5.67,5.67
c3.13,0,5.67-2.54,5.67-5.67C302.99,417.54,300.45,414.99,297.32,414.99z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CDA150;}
.st1{font-family:'Helvetica';}
.st2{font-size:6px;}
</style>
<text transform="matrix(0.66 0 0 1 289.8221 375.1115)" class="st0 st1 st2">PRODUCE OF FRANCE</text>
</svg>

After

Width:  |  Height:  |  Size: 577 B

11
src/assets/QUALITÉ.svg Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CDA150;}
.st1{font-family:'Helvetica';}
.st2{font-size:14px;}
</style>
<text transform="matrix(0.66 0 0 1 299.1375 375.1115)" class="st0 st1 st2">BRUT</text>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CDA150;}
.st1{font-family:'GaramondThree';}
.st2{font-size:20px;}
</style>
<text transform="matrix(0.4084 0 0 1 283.9296 426.5225)" class="st0 st1 st2">750 ml</text>
</svg>

After

Width:  |  Height:  |  Size: 573 B

11
src/assets/sulfites.svg Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CDA150;}
.st1{font-family:'Helvetica';}
.st2{font-size:6px;}
</style>
<text transform="matrix(0.66 0 0 1 286.9607 375.1115)" class="st0 st1 st2">Contient des sulfites</text>
</svg>

After

Width:  |  Height:  |  Size: 581 B

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 595.28 841.89" style="enable-background:new 0 0 595.28 841.89;" xml:space="preserve">
<style type="text/css">
.st0{fill:#CDA150;}
.st1{font-family:'Helvetica';}
.st2{font-size:6px;}
</style>
<text transform="matrix(0.66 0 0 1 254.2979 375.1115)" class="st0 st1 st2">ÉLABORÉ PAR ERAL CHARLES DELROCHE, 51200 ÉPERNAY, FRANCE - RM-00000-01</text>
</svg>

After

Width:  |  Height:  |  Size: 633 B

View File

@@ -12,6 +12,8 @@ import {
import { useEffect, useState } from "react";
import { Toolbar } from "@/components/toolbar";
import TransformableText from "@/components/transformable-text";
import LayerBorder from "@/components/layer-border";
import { CANVAS_PADDING_X, CANVAS_PADDING_Y } from "@/consts/canvas-params";
const Canvas = () => {
const dispatch = useAppDispatch();
@@ -33,7 +35,6 @@ const Canvas = () => {
if (!clickedOnEmpty) {
return;
}
console.log(target);
dispatch(deselectItem());
};
@@ -54,42 +55,6 @@ const Canvas = () => {
setSelected(!isEditing);
}
// function toggleTransforming() {
// setIsTransforming(!isTransforming);
// setSelected(!isTransforming);
// }
//
// const handleWheel = (e: Konva.KonvaEventObject<WheelEvent>) => {
// e.evt.preventDefault();
//
// const scaleBy = 1.02;
// const targetStage = e.target.getStage()!;
// const oldScale = targetStage.scaleX();
//
// const mousePointTo = {
// x:
// targetStage.getPointerPosition().x / oldScale -
// targetStage.x() / oldScale,
// y:
// targetStage.getPointerPosition().y / oldScale -
// targetStage.y() / oldScale,
// };
//
// const newScale = e.evt.deltaY < 0 ? oldScale * scaleBy : oldScale / scaleBy;
//
// dispatch(
// setStageScale({
// scale: newScale,
// x:
// (targetStage.getPointerPosition().x / newScale - mousePointTo.x) *
// newScale,
// y:
// (targetStage.getPointerPosition().y / newScale - mousePointTo.y) *
// newScale,
// }),
// );
// };
return (
<div className="relative flex h-full w-full flex-col items-center">
<Toolbar />
@@ -112,7 +77,6 @@ const Canvas = () => {
y={stage.y}
scaleX={stage.scale}
scaleY={stage.scale}
// onWheel={handleWheel}
>
<Layer>
{!!backgroundRect && (
@@ -125,7 +89,14 @@ const Canvas = () => {
id={backgroundId}
/>
)}
{items.map((item) => {
</Layer>
<Layer
clipX={CANVAS_PADDING_X}
clipY={CANVAS_PADDING_Y}
clipWidth={stage.width - 2 * CANVAS_PADDING_X}
clipHeight={stage.height - 2 * CANVAS_PADDING_Y}
>
{items.toReversed().map((item) => {
if (item.type === StageItemType.Image) {
return (
<TransformableImage
@@ -158,24 +129,9 @@ const Canvas = () => {
}
})}
</Layer>
{/* <Layer>
<Rect
x={50}
y={220}
fontSize={48}
width={100}
height={100}
fill="red"
/>
<Rect
x={30}
y={200}
fontSize={48}
width={100}
height={100}
fill="yellow"
/>
</Layer>*/}
{/*<Legales />*/}
<LayerBorder />
</Stage>
</div>
</div>

View File

@@ -1,10 +1,12 @@
import React from "react";
import { TbFlipHorizontal, TbFlipVertical } from "react-icons/tb";
import { BsArrowsFullscreen } from "react-icons/Bs";
import { Toggle } from "@/components/ui/toggle";
import { useAppDispatch } from "@/hooks";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { type StageImageItem, updateImage } from "@/store/app.slice";
import ImageOpacityTool from "@/components/image-editing-tools/image-opacity-tool";
import { Separator } from "@/components/ui/separator";
import { CANVAS_PADDING_X, CANVAS_PADDING_Y } from "@/consts/canvas-params";
type Props = {
selectedItemId: string;
@@ -13,6 +15,9 @@ type Props = {
export function ImageToolbar({ selectedItemId, currentImage }: Props) {
const dispatch = useAppDispatch();
const stageParams = useAppSelector((state) => state.app.stage);
const flipImageVerticaly = () => {
const currentScale = currentImage.scaleY;
let editOffsetY = currentImage.height;
@@ -40,6 +45,19 @@ export function ImageToolbar({ selectedItemId, currentImage }: Props) {
}),
);
};
const setAsBackground = () => {
dispatch(
updateImage({
id: selectedItemId,
x: CANVAS_PADDING_X,
y: CANVAS_PADDING_Y,
width: stageParams.width - 2 * CANVAS_PADDING_X,
height: stageParams.height - 2 * CANVAS_PADDING_Y,
}),
);
};
return (
<>
<Toggle onClick={() => flipImageHorizontaly()}>
@@ -53,6 +71,9 @@ export function ImageToolbar({ selectedItemId, currentImage }: Props) {
selectedItemId={selectedItemId}
currentImage={currentImage}
/>
<Toggle onClick={() => setAsBackground()}>
<BsArrowsFullscreen />
</Toggle>
</>
);
}

View File

@@ -0,0 +1,63 @@
import React from "react";
import { Layer, Line } from "react-konva";
import {
CANVAS_PADDING_X,
CANVAS_PADDING_Y,
DASH_SPACING,
DASH_STROKE_COLOR,
DASH_STROKE_WIDTH,
DASH_WIDTH,
} from "@/consts/canvas-params";
import { useAppSelector } from "@/hooks";
export default function LayerBorder() {
const stage = useAppSelector((state) => state.app.stage);
return (
<Layer>
<Line
points={[
CANVAS_PADDING_X,
CANVAS_PADDING_Y,
stage.width - CANVAS_PADDING_X,
CANVAS_PADDING_Y,
]}
stroke={DASH_STROKE_COLOR}
strokeWidth={DASH_STROKE_WIDTH}
dash={[DASH_WIDTH, DASH_SPACING]}
/>
<Line
points={[
CANVAS_PADDING_X,
CANVAS_PADDING_Y,
CANVAS_PADDING_X,
stage.height - CANVAS_PADDING_Y,
]}
stroke={DASH_STROKE_COLOR}
strokeWidth={DASH_STROKE_WIDTH}
dash={[DASH_WIDTH, DASH_SPACING]}
/>
<Line
points={[
CANVAS_PADDING_X,
stage.height - CANVAS_PADDING_Y,
stage.width - CANVAS_PADDING_X,
stage.height - CANVAS_PADDING_Y,
]}
stroke={DASH_STROKE_COLOR}
strokeWidth={DASH_STROKE_WIDTH}
dash={[DASH_WIDTH, DASH_SPACING]}
/>
<Line
points={[
stage.width - CANVAS_PADDING_X,
CANVAS_PADDING_Y,
stage.width - CANVAS_PADDING_X,
stage.height - CANVAS_PADDING_Y,
]}
stroke={DASH_STROKE_COLOR}
strokeWidth={DASH_STROKE_WIDTH}
dash={[DASH_WIDTH, DASH_SPACING]}
/>
</Layer>
);
}

View File

@@ -0,0 +1,42 @@
import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { type StageItem, StageItemType } from "@/store/app.slice";
import { useAppDispatch } from "@/hooks";
export default function LayerItem({ item }: { item: StageItem }) {
const dispatch = useAppDispatch();
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({
id: item.id,
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div
style={style}
ref={setNodeRef}
{...attributes}
{...listeners}
className="m-2 flex items-center justify-between rounded-md border p-2 text-black"
>
{item.type === StageItemType.Text ? (
item.params.text < 5 ? (
item.params.text
) : (
item.params.text?.substring(0, 5) + "..."
)
) : (
<img
className="max-h-8 w-8"
alt={item.params.imageUrl}
src={item.params.imageUrl}
/>
)}
<span className="">{item.type}</span>
</div>
);
}

View File

@@ -1,6 +1,7 @@
import { UserNav } from "./user-nav";
import Image from "next/image";
import logo from "@/assets/logo.png";
import { Switch } from "@/components/ui/switch";
export const Navbar = () => {
return (
@@ -12,7 +13,7 @@ export const Navbar = () => {
alt="Logo"
layout="fill" // required
objectFit="cover" // change as you like
className="rounded-full" // you can use other classes here too
className="rounded-full object-cover" // you can use other classes here too
/>
</div>
<div className="ml-auto flex items-center space-x-4">

View File

@@ -8,12 +8,13 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { RxLayout } from "react-icons/rx";
import { Label } from "@/components/ui/label";
import { useAppDispatch } from "@/hooks";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { selectBackground } from "@/store/app.slice";
export const BackgroundSelect = () => {
const dispatch = useAppDispatch();
const [open, setOpen] = useState(false);
const bgColor = useAppSelector((state) => state.app.background);
const handleBackgroundSelect = (color: string) => {
dispatch(selectBackground(color));
@@ -40,30 +41,17 @@ export const BackgroundSelect = () => {
</CardHeader>
<CardContent className="grid grid-cols-2 gap-2">
<div>
<div
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
onClick={() => handleBackgroundSelect("red")}
>
Red
</div>
</div>
<div>
<Label
htmlFor="paypal"
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
onClick={() => handleBackgroundSelect("blue")}
>
Blue
</Label>
</div>
<div>
<Label
htmlFor="apple"
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
onClick={() => handleBackgroundSelect("green")}
>
Green
<input
className="m-2 "
type="color"
value={bgColor}
onChange={(e) => handleBackgroundSelect(e.target.value)}
/>
</Label>
</div>
</CardContent>

View File

@@ -8,19 +8,26 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { RxStack } from "react-icons/rx";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { OrderDirection, updateItemOrder } from "@/store/app.slice";
import { selectItem, setStageItems } from "@/store/app.slice";
import { closestCenter, DndContext, type DragEndEvent } from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import LayerItem from "@/components/layer-item";
export const Layers = () => {
const dispatch = useAppDispatch();
const items = useAppSelector((state) => state.app.items);
const updateImageLayer = (id: string, direction: OrderDirection) => {
dispatch(
updateItemOrder({
id,
direction,
}),
);
const handleDragEnd = (e: DragEndEvent) => {
const { active, over } = e;
const oldIndex = items.findIndex((item) => item.id === active.id);
const newIndex = items.findIndex((item) => item.id === over?.id);
const result = arrayMove(items, oldIndex, newIndex);
dispatch(setStageItems(result));
dispatch(selectItem(active.id));
};
return (
@@ -33,26 +40,22 @@ export const Layers = () => {
<PopoverContent side="right">
<Card className="p-2">
<CardHeader>
<CardTitle className="text-center">Layer</CardTitle>
<CardTitle className="text-center">Layers</CardTitle>
</CardHeader>
<CardContent className="grid gap-2">
{items.map((item) => (
<div key={item.id} className="flex items-center gap-2 ">
{item.id}
<Button
className="h-full"
onClick={() => updateImageLayer(item.id, OrderDirection.Up)}
>
+
</Button>
<Button
className="h-full"
onClick={() => updateImageLayer(item.id, OrderDirection.Down)}
>
-
</Button>
</div>
))}
<CardContent className="grid items-center gap-2 p-2 ">
<DndContext
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext
items={items}
strategy={verticalListSortingStrategy}
>
{items.map((item) => (
<LayerItem item={item} key={item.id} />
))}
</SortableContext>
</DndContext>
</CardContent>
</Card>
</PopoverContent>

View File

View File

@@ -1,33 +1,69 @@
import React from "react";
import React, { useState } from "react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { PiFrameCornersDuotone } from "react-icons/pi";
import { useAppDispatch } from "@/hooks";
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
import { useAppDispatch } from "@/hooks";
import { LuLayoutTemplate } from "react-icons/lu";
import { ScrollArea } from "@/components/ui/scroll-area";
export function SelectTemplate() {
export const SelectTemplate = () => {
const dispatch = useAppDispatch();
const [open, setOpen] = useState(false);
const handleSizeSelect = () => {
setOpen(false);
};
return (
<Popover>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="secondary" className="text-xl">
<PiFrameCornersDuotone />
<Button
onClick={() => setOpen(true)}
variant="outline"
className="text-xl"
>
<LuLayoutTemplate />
</Button>
</PopoverTrigger>
<PopoverContent side="right">
{/*<Input type="file" onChange={handleImageUploaded} />*/}
<Card className="p-2">
<CardHeader>
<CardTitle className="text-center">Cadre</CardTitle>
<PopoverContent side="right" className="mt-4">
<Card className="p-3">
<CardHeader className="mb-2 p-2 text-center">
<CardTitle>Template</CardTitle>
</CardHeader>
<ScrollArea>
<div className="flex h-64 w-full flex-col items-center justify-between gap-4">
<div className="rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>
<div className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>{" "}
<div className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>{" "}
<div className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>{" "}
<div className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>{" "}
<div className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>{" "}
<div className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>{" "}
<div className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary">
Image
</div>
</div>
</ScrollArea>
</Card>
</PopoverContent>
</Popover>
);
}
};

View File

@@ -3,6 +3,8 @@ import ImageInput from "@/components/layout/sidebar/image-input";
import SizeSelect from "@/components/layout/sidebar/size-select";
import { BackgroundSelect } from "@/components/layout/sidebar/background-select";
import { Layers } from "@/components/layout/sidebar/layers";
import { SelectTemplate } from "@/components/layout/sidebar/select-template";
import { Switch } from "@/components/ui/switch";
export function Sidebar() {
return (
@@ -10,7 +12,7 @@ export function Sidebar() {
<SizeSelect />
<TextInput />
<ImageInput />
{/*<SelectTemplate />*/}
<SelectTemplate />
<BackgroundSelect />
<Layers />
</div>

View File

@@ -42,13 +42,13 @@ const SizeSelect = () => {
</CardHeader>
<div className="flex flex-col gap-2">
<Button onClick={() => handleStageSizeSelect(350, 400)}>
<Button onClick={() => handleStageSizeSelect(269.32, 165.87)}>
Petit Format
</Button>
<Button onClick={() => handleStageSizeSelect(500, 500)}>
<Button onClick={() => handleStageSizeSelect(323.15, 227)}>
Moyen Format
</Button>
<Button onClick={() => handleStageSizeSelect(800, 600)}>
<Button onClick={() => handleStageSizeSelect(410, 289)}>
Grand Format
</Button>
</div>

View File

@@ -0,0 +1,20 @@
import React from "react";
import { Image, Layer } from "react-konva";
import women from "../assets/logo.png";
import useImage from "use-image";
export default function Legales() {
const [test] = useImage(women);
return (
<Layer>
<Image
width={100}
height={100}
x={200}
y={200}
alt={women}
image={test}
/>
</Layer>
);
}

View File

@@ -0,0 +1,46 @@
import * as React from "react";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { cn } from "@/lib/utils";
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
export { ScrollArea, ScrollBar };

View File

@@ -0,0 +1,27 @@
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }

117
src/components/ui/table.tsx Normal file
View File

@@ -0,0 +1,117 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

View File

@@ -0,0 +1,6 @@
export const CANVAS_PADDING_X = 20;
export const CANVAS_PADDING_Y = 20;
export const DASH_WIDTH = 5;
export const DASH_SPACING = 3;
export const DASH_STROKE_WIDTH = 1;
export const DASH_STROKE_COLOR = "#c6a25e";

View File

@@ -13,7 +13,6 @@ export enum StageItemType {
type StageItemCommon = {
id: string;
order: number;
};
export type StageTextItem = {
@@ -28,7 +27,7 @@ export type StageImageItem = {
type StageItemSpecific = StageTextItem | StageImageItem;
type StageItem = StageItemCommon & StageItemSpecific;
export type StageItem = StageItemCommon & StageItemSpecific;
const initialState = {
stage: { width: 500, height: 500, scale: 1, x: 0, y: 0 },
@@ -57,15 +56,13 @@ const defaultImageConfig = {
scaleY: 1,
};
export enum OrderDirection {
Up = "up",
Down = "down",
}
export const appSlice = createSlice({
name: "app",
initialState,
reducers: {
setStageItems: (state, action: PayloadAction<StageItem[]>) => {
state.items = action.payload;
},
setStageScale: (
state,
action: PayloadAction<{ x: number; y: number; scale: number }>,
@@ -80,7 +77,6 @@ export const appSlice = createSlice({
state.items.push({
type: StageItemType.Text,
id: textId,
order: state.items.length + 1,
params: {
text: action.payload.initialValue,
@@ -102,7 +98,6 @@ export const appSlice = createSlice({
state.items.push({
type: StageItemType.Image,
id: imageId,
order: state.items.length + 1,
params: {
imageUrl: action.payload.imageUrl,
width: action.payload.width,
@@ -112,83 +107,6 @@ export const appSlice = createSlice({
});
},
updateImageOrder: (
state,
action: PayloadAction<{ id: string; direction: OrderDirection }>,
) => {
const imageToUpdateIndex = state.items.findIndex(
(img) => img.id === action.payload.id,
);
const imageToUpdate = state.items[imageToUpdateIndex];
if (!imageToUpdate) return;
const isLast = imageToUpdate.order === state.items.length;
const isFirst = imageToUpdate.order === 1;
let newOrder = imageToUpdate.order;
if (action.payload.direction === OrderDirection.Up && !isLast) {
newOrder = imageToUpdate.order + 1;
}
if (action.payload.direction === OrderDirection.Down && !isFirst) {
newOrder = imageToUpdate.order - 1;
}
state.items[imageToUpdateIndex] = {
...imageToUpdate,
order: newOrder,
};
state.items.sort((a, b) => {
if (a === b) return 0;
if (a < b) return 1;
return -1;
});
},
updateItemOrder: (
state,
action: PayloadAction<{ id: string; direction: OrderDirection }>,
) => {
const itemToUpdateIndex = state.items.findIndex(
(item) => item.id === action.payload.id,
);
const itemToUpdate = state.items[itemToUpdateIndex];
if (!itemToUpdate) return;
const isLast = itemToUpdate.order === state.items.length;
const isFirst = itemToUpdate.order === 1;
let newOrder = itemToUpdate.order;
if (action.payload.direction === OrderDirection.Up && !isLast) {
newOrder = itemToUpdate.order + 1;
}
if (action.payload.direction === OrderDirection.Down && !isFirst) {
newOrder = itemToUpdate.order - 1;
}
const prevItemIndex = state.items.findIndex(
(item) => item.order === newOrder,
);
const prevItem = state.items[prevItemIndex];
if (!prevItem) return;
state.items[prevItemIndex] = {
...prevItem,
order: itemToUpdate.order,
};
state.items[itemToUpdateIndex] = {
...itemToUpdate,
order: newOrder,
};
state.items.sort((a, b) => {
if (a === b) return 0;
if (a < b) return 1;
return -1;
});
},
selectBackground: (state, action: PayloadAction<string>) => {
state.background = {
fill: action.payload,
@@ -258,15 +176,14 @@ export const appSlice = createSlice({
});
export const {
setStageScale,
addImage,
addText,
selectItem,
deselectItem,
updateText,
updateImage,
deleteStageItem,
updateStage,
selectBackground,
updateItemOrder,
setStageItems,
selectItem,
} = appSlice.actions;

10
todo.md
View File

@@ -1,4 +1,12 @@
- text transform box doesn't update it's size on font size change
- layout order doesn't work correctly with > 2 objects
- Authorization provider
- Template properties ( assets )
- Canvas sizes
- what to add or change?
-