feat(s3): add drag-and-drop and context-menu file uploads, inline text editing in preview, and increase preview width
This commit is contained in:
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-01-27 - 1.7.0 - feat(s3)
|
||||||
|
add drag-and-drop and context-menu file uploads, inline text editing in preview, and increase preview width
|
||||||
|
|
||||||
|
- Add drag-and-drop uploading into columns and folders with visual drag-over / drag-target states and a delayed folder-hover target
|
||||||
|
- Add context-menu 'Upload Files' action and hidden file input to trigger multi-file uploads; show upload overlay with progress and refresh column after upload
|
||||||
|
- Implement upload handling using apiService.putObject and base64 file reads, with upload progress tracking and error logging
|
||||||
|
- Add inline text editing in the preview (Edit / Save / Discard flow) using dees-input-code for editing and dees-preview for read-only display
|
||||||
|
- Increase preview default width from 350px to 700px and raise max resize limit from 600px to 1000px
|
||||||
|
- Bump dependencies: @git.zone/tstest -> ^3.1.8, @design.estate/dees-catalog -> ^3.41.1, @design.estate/dees-element -> ^2.1.6
|
||||||
|
|
||||||
## 2026-01-26 - 1.6.1 - fix(ci)
|
## 2026-01-26 - 1.6.1 - fix(ci)
|
||||||
add Gitea CI workflows, documentation updates, and packaging metadata tweaks
|
add Gitea CI workflows, documentation updates, and packaging metadata tweaks
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"@git.zone/tsbuild": "^4.1.2",
|
"@git.zone/tsbuild": "^4.1.2",
|
||||||
"@git.zone/tsbundle": "^2.8.3",
|
"@git.zone/tsbundle": "^2.8.3",
|
||||||
"@git.zone/tsrun": "^2.0.1",
|
"@git.zone/tsrun": "^2.0.1",
|
||||||
"@git.zone/tstest": "^3.1.7",
|
"@git.zone/tstest": "^3.1.8",
|
||||||
"@git.zone/tswatch": "3.0.1",
|
"@git.zone/tswatch": "3.0.1",
|
||||||
"@push.rocks/smartrx": "^3.0.10",
|
"@push.rocks/smartrx": "^3.0.10",
|
||||||
"@types/node": "^25.0.10"
|
"@types/node": "^25.0.10"
|
||||||
@@ -34,8 +34,8 @@
|
|||||||
"@api.global/typedrequest-interfaces": "^3.0.19",
|
"@api.global/typedrequest-interfaces": "^3.0.19",
|
||||||
"@api.global/typedserver": "^8.3.0",
|
"@api.global/typedserver": "^8.3.0",
|
||||||
"@aws-sdk/client-s3": "^3.975.0",
|
"@aws-sdk/client-s3": "^3.975.0",
|
||||||
"@design.estate/dees-catalog": "^3.37.1",
|
"@design.estate/dees-catalog": "^3.41.1",
|
||||||
"@design.estate/dees-element": "^2.1.5",
|
"@design.estate/dees-element": "^2.1.6",
|
||||||
"@push.rocks/early": "^4.0.4",
|
"@push.rocks/early": "^4.0.4",
|
||||||
"@push.rocks/npmextra": "^5.3.3",
|
"@push.rocks/npmextra": "^5.3.3",
|
||||||
"@push.rocks/smartbucket": "^4.4.1",
|
"@push.rocks/smartbucket": "^4.4.1",
|
||||||
|
|||||||
76
pnpm-lock.yaml
generated
76
pnpm-lock.yaml
generated
@@ -21,11 +21,11 @@ importers:
|
|||||||
specifier: ^3.975.0
|
specifier: ^3.975.0
|
||||||
version: 3.975.0
|
version: 3.975.0
|
||||||
'@design.estate/dees-catalog':
|
'@design.estate/dees-catalog':
|
||||||
specifier: ^3.37.1
|
specifier: ^3.41.1
|
||||||
version: 3.37.1(@tiptap/pm@2.27.2)
|
version: 3.41.1(@tiptap/pm@2.27.2)
|
||||||
'@design.estate/dees-element':
|
'@design.estate/dees-element':
|
||||||
specifier: ^2.1.5
|
specifier: ^2.1.6
|
||||||
version: 2.1.5
|
version: 2.1.6
|
||||||
'@push.rocks/early':
|
'@push.rocks/early':
|
||||||
specifier: ^4.0.4
|
specifier: ^4.0.4
|
||||||
version: 4.0.4
|
version: 4.0.4
|
||||||
@@ -79,8 +79,8 @@ importers:
|
|||||||
specifier: ^2.0.1
|
specifier: ^2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
'@git.zone/tstest':
|
'@git.zone/tstest':
|
||||||
specifier: ^3.1.7
|
specifier: ^3.1.8
|
||||||
version: 3.1.7(@push.rocks/smartserve@2.0.1)(socks@2.8.7)(typescript@5.9.3)
|
version: 3.1.8(@push.rocks/smartserve@2.0.1)(socks@2.8.7)(typescript@5.9.3)
|
||||||
'@git.zone/tswatch':
|
'@git.zone/tswatch':
|
||||||
specifier: 3.0.1
|
specifier: 3.0.1
|
||||||
version: 3.0.1(@tiptap/pm@2.27.2)
|
version: 3.0.1(@tiptap/pm@2.27.2)
|
||||||
@@ -325,26 +325,26 @@ packages:
|
|||||||
'@cloudflare/workers-types@4.20260123.0':
|
'@cloudflare/workers-types@4.20260123.0':
|
||||||
resolution: {integrity: sha512-pQccZ8IDLFKkvdKBXZRPkbMtWtS7vVz1giJGkAAZ5cZH2RHK5Bs6p1OoVZA8Z2Sry8Q0tZbZ5Yjud4R7SrG3KQ==}
|
resolution: {integrity: sha512-pQccZ8IDLFKkvdKBXZRPkbMtWtS7vVz1giJGkAAZ5cZH2RHK5Bs6p1OoVZA8Z2Sry8Q0tZbZ5Yjud4R7SrG3KQ==}
|
||||||
|
|
||||||
'@cloudflare/workers-types@4.20260124.0':
|
'@cloudflare/workers-types@4.20260127.0':
|
||||||
resolution: {integrity: sha512-h6TJlew6AtGuEXFc+k5ifalk+tg3fkg0lla6XbMAb2AKKfJGwlFNTwW2xyT/Ha92KY631CIJ+Ace08DPdFohdA==}
|
resolution: {integrity: sha512-4M1HLcWViSdT/pAeDGEB5x5P3sqW7UIi34QrBRnxXbqjAY9if8vBU/lWRWnM+UqKzxWGB2LYjEVOzZrp0jZL+w==}
|
||||||
|
|
||||||
'@configvault.io/interfaces@1.0.17':
|
'@configvault.io/interfaces@1.0.17':
|
||||||
resolution: {integrity: sha512-bEcCUR2VBDJsTin8HQh8Uw/mlYl2v8A3jMIaQ+MTB9Hrqd6CZL2dL7iJdWyFl/3EIX+LDxWFR+Oq7liIq7w+1Q==}
|
resolution: {integrity: sha512-bEcCUR2VBDJsTin8HQh8Uw/mlYl2v8A3jMIaQ+MTB9Hrqd6CZL2dL7iJdWyFl/3EIX+LDxWFR+Oq7liIq7w+1Q==}
|
||||||
|
|
||||||
'@design.estate/dees-catalog@3.37.1':
|
'@design.estate/dees-catalog@3.41.1':
|
||||||
resolution: {integrity: sha512-NCgzzCG3NJVF7C7aa1nExCMhB+7nA6glFgZpsff32CpvdtbAuBQiuOngU0suVw65uK7Y0a2r/y2CEPGNNmj3TA==}
|
resolution: {integrity: sha512-AMD0VNLQEWXYRUYWwjLA8K8KEKAiUO7GiriWQm+ld7cj+LrCMsJO0jjVfOCsd4G7fURMqmab9ereBJyxqjoFgQ==}
|
||||||
|
|
||||||
'@design.estate/dees-comms@1.0.30':
|
'@design.estate/dees-comms@1.0.30':
|
||||||
resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==}
|
resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==}
|
||||||
|
|
||||||
'@design.estate/dees-domtools@2.3.7':
|
'@design.estate/dees-domtools@2.3.8':
|
||||||
resolution: {integrity: sha512-MXoDBrP7JTOpni8b12aFXHJKnKBoQppM8cYBuL9cesRmCVGdB7p39XMRQ7dRyMhmmyr66L3cOczhiCV6febCwg==}
|
resolution: {integrity: sha512-jUG9GMvPxKMwmRIZ9oLTL3c8hHvHuiwIk8cTrYnuZzGO/uJJ5/czk9o6LRXUuCOOG7TRLtqgOpK8EEQgaadfZA==}
|
||||||
|
|
||||||
'@design.estate/dees-element@2.1.5':
|
'@design.estate/dees-element@2.1.6':
|
||||||
resolution: {integrity: sha512-czUOFvBiUKi34I+/keDRDc71fuORZS0NfbSuD2jJ4D1ODiTPjaZ6A6SkdQ2QqCEzVsx73XF99Pu8pxPnaOLnHg==}
|
resolution: {integrity: sha512-7zyHkUjB8UEQgT9VbB2IJtc/yuPt9CI5JGel3b6BxA1kecY64ceIjFvof1uIkc0QP8q2fMLLY45r1c+9zDTjzg==}
|
||||||
|
|
||||||
'@design.estate/dees-wcctools@3.7.1':
|
'@design.estate/dees-wcctools@3.8.0':
|
||||||
resolution: {integrity: sha512-BiNWghUoC05RTQOGVCTK+wis6d18LyLY+2p8vHC0q2OBw9hrPoY8k9EplpQgY40MvP0sTXWUwaa7VPXra8ASjA==}
|
resolution: {integrity: sha512-CC14iVKUrguzD9jIrdPBd9fZ4egVJEZMxl5y8iy0l7WLumeoYvGsoXj5INVkRPLRVLqziIdi4Je1hXqHt2NU+g==}
|
||||||
|
|
||||||
'@emnapi/core@1.8.1':
|
'@emnapi/core@1.8.1':
|
||||||
resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
|
resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
|
||||||
@@ -547,8 +547,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-NEcnsjvlC1o3Z6SS3VhKCf6Ev+Sh4EAinmggslrIR/ppMrvjDbXNFXoyr3PB+GLeSAR0JRZ1fGvVYjpEzjBdIg==}
|
resolution: {integrity: sha512-NEcnsjvlC1o3Z6SS3VhKCf6Ev+Sh4EAinmggslrIR/ppMrvjDbXNFXoyr3PB+GLeSAR0JRZ1fGvVYjpEzjBdIg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@git.zone/tstest@3.1.7':
|
'@git.zone/tstest@3.1.8':
|
||||||
resolution: {integrity: sha512-YCDA+65LJhoY3WJxrNduKlpGf37aq4bFe+fdRqE0dZ2W1f7j3sUunBaBzckShSHKRjkMdPZKr0W0sXFXUK/PcA==}
|
resolution: {integrity: sha512-nmiLGeOkKMkLDyIk5BUBLx5ExskFbKHKlPdrWCARPVFkU4cAAiuIyJWVfLwISoS0TO/zSInLqArPwIc76yvaNw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@git.zone/tswatch@3.0.1':
|
'@git.zone/tswatch@3.0.1':
|
||||||
@@ -1924,8 +1924,8 @@ packages:
|
|||||||
bare-abort-controller:
|
bare-abort-controller:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
bare-fs@4.5.2:
|
bare-fs@4.5.3:
|
||||||
resolution: {integrity: sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==}
|
resolution: {integrity: sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ==}
|
||||||
engines: {bare: '>=1.16.0'}
|
engines: {bare: '>=1.16.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
bare-buffer: '*'
|
bare-buffer: '*'
|
||||||
@@ -4017,7 +4017,7 @@ snapshots:
|
|||||||
'@api.global/typedrequest': 3.2.5
|
'@api.global/typedrequest': 3.2.5
|
||||||
'@api.global/typedrequest-interfaces': 3.0.19
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
'@api.global/typedsocket': 3.1.1(@push.rocks/smartserve@2.0.1)
|
'@api.global/typedsocket': 3.1.1(@push.rocks/smartserve@2.0.1)
|
||||||
'@cloudflare/workers-types': 4.20260124.0
|
'@cloudflare/workers-types': 4.20260127.0
|
||||||
'@design.estate/dees-comms': 1.0.30
|
'@design.estate/dees-comms': 1.0.30
|
||||||
'@push.rocks/lik': 6.2.2
|
'@push.rocks/lik': 6.2.2
|
||||||
'@push.rocks/smartchok': 1.2.0
|
'@push.rocks/smartchok': 1.2.0
|
||||||
@@ -4066,7 +4066,7 @@ snapshots:
|
|||||||
'@api.global/typedrequest-interfaces': 3.0.19
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
'@api.global/typedsocket': 4.1.0(@push.rocks/smartserve@2.0.1)
|
'@api.global/typedsocket': 4.1.0(@push.rocks/smartserve@2.0.1)
|
||||||
'@cloudflare/workers-types': 4.20260123.0
|
'@cloudflare/workers-types': 4.20260123.0
|
||||||
'@design.estate/dees-catalog': 3.37.1(@tiptap/pm@2.27.2)
|
'@design.estate/dees-catalog': 3.41.1(@tiptap/pm@2.27.2)
|
||||||
'@design.estate/dees-comms': 1.0.30
|
'@design.estate/dees-comms': 1.0.30
|
||||||
'@push.rocks/lik': 6.2.2
|
'@push.rocks/lik': 6.2.2
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
@@ -4688,17 +4688,17 @@ snapshots:
|
|||||||
|
|
||||||
'@cloudflare/workers-types@4.20260123.0': {}
|
'@cloudflare/workers-types@4.20260123.0': {}
|
||||||
|
|
||||||
'@cloudflare/workers-types@4.20260124.0': {}
|
'@cloudflare/workers-types@4.20260127.0': {}
|
||||||
|
|
||||||
'@configvault.io/interfaces@1.0.17':
|
'@configvault.io/interfaces@1.0.17':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedrequest-interfaces': 3.0.19
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
|
|
||||||
'@design.estate/dees-catalog@3.37.1(@tiptap/pm@2.27.2)':
|
'@design.estate/dees-catalog@3.41.1(@tiptap/pm@2.27.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-domtools': 2.3.7
|
'@design.estate/dees-domtools': 2.3.8
|
||||||
'@design.estate/dees-element': 2.1.5
|
'@design.estate/dees-element': 2.1.6
|
||||||
'@design.estate/dees-wcctools': 3.7.1
|
'@design.estate/dees-wcctools': 3.8.0
|
||||||
'@fortawesome/fontawesome-svg-core': 7.1.0
|
'@fortawesome/fontawesome-svg-core': 7.1.0
|
||||||
'@fortawesome/free-brands-svg-icons': 7.1.0
|
'@fortawesome/free-brands-svg-icons': 7.1.0
|
||||||
'@fortawesome/free-regular-svg-icons': 7.1.0
|
'@fortawesome/free-regular-svg-icons': 7.1.0
|
||||||
@@ -4736,7 +4736,7 @@ snapshots:
|
|||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
broadcast-channel: 7.3.0
|
broadcast-channel: 7.3.0
|
||||||
|
|
||||||
'@design.estate/dees-domtools@2.3.7':
|
'@design.estate/dees-domtools@2.3.8':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedrequest': 3.2.5
|
'@api.global/typedrequest': 3.2.5
|
||||||
'@design.estate/dees-comms': 1.0.30
|
'@design.estate/dees-comms': 1.0.30
|
||||||
@@ -4762,9 +4762,9 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@design.estate/dees-element@2.1.5':
|
'@design.estate/dees-element@2.1.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-domtools': 2.3.7
|
'@design.estate/dees-domtools': 2.3.8
|
||||||
'@push.rocks/isounique': 1.0.5
|
'@push.rocks/isounique': 1.0.5
|
||||||
'@push.rocks/smartrx': 3.0.10
|
'@push.rocks/smartrx': 3.0.10
|
||||||
lit: 3.3.2
|
lit: 3.3.2
|
||||||
@@ -4774,10 +4774,10 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@design.estate/dees-wcctools@3.7.1':
|
'@design.estate/dees-wcctools@3.8.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-domtools': 2.3.7
|
'@design.estate/dees-domtools': 2.3.8
|
||||||
'@design.estate/dees-element': 2.1.5
|
'@design.estate/dees-element': 2.1.6
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
lit: 3.3.2
|
lit: 3.3.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -4973,7 +4973,7 @@ snapshots:
|
|||||||
'@push.rocks/smartshell': 3.3.0
|
'@push.rocks/smartshell': 3.3.0
|
||||||
tsx: 4.21.0
|
tsx: 4.21.0
|
||||||
|
|
||||||
'@git.zone/tstest@3.1.7(@push.rocks/smartserve@2.0.1)(socks@2.8.7)(typescript@5.9.3)':
|
'@git.zone/tstest@3.1.8(@push.rocks/smartserve@2.0.1)(socks@2.8.7)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedserver': 3.0.80(@push.rocks/smartserve@2.0.1)
|
'@api.global/typedserver': 3.0.80(@push.rocks/smartserve@2.0.1)
|
||||||
'@git.zone/tsbundle': 2.8.3
|
'@git.zone/tsbundle': 2.8.3
|
||||||
@@ -5882,7 +5882,7 @@ snapshots:
|
|||||||
|
|
||||||
'@push.rocks/smartntml@2.0.8':
|
'@push.rocks/smartntml@2.0.8':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-element': 2.1.5
|
'@design.estate/dees-element': 2.1.6
|
||||||
'@happy-dom/global-registrator': 15.11.7
|
'@happy-dom/global-registrator': 15.11.7
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
fake-indexeddb: 6.2.5
|
fake-indexeddb: 6.2.5
|
||||||
@@ -6130,7 +6130,7 @@ snapshots:
|
|||||||
|
|
||||||
'@push.rocks/taskbuffer@3.5.0':
|
'@push.rocks/taskbuffer@3.5.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-element': 2.1.5
|
'@design.estate/dees-element': 2.1.6
|
||||||
'@push.rocks/lik': 6.2.2
|
'@push.rocks/lik': 6.2.2
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartlog': 3.1.10
|
'@push.rocks/smartlog': 3.1.10
|
||||||
@@ -7139,7 +7139,7 @@ snapshots:
|
|||||||
|
|
||||||
bare-events@2.8.2: {}
|
bare-events@2.8.2: {}
|
||||||
|
|
||||||
bare-fs@4.5.2:
|
bare-fs@4.5.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
bare-events: 2.8.2
|
bare-events: 2.8.2
|
||||||
bare-path: 3.0.0
|
bare-path: 3.0.0
|
||||||
@@ -9319,7 +9319,7 @@ snapshots:
|
|||||||
pump: 3.0.3
|
pump: 3.0.3
|
||||||
tar-stream: 3.1.7
|
tar-stream: 3.1.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
bare-fs: 4.5.2
|
bare-fs: 4.5.3
|
||||||
bare-path: 3.0.0
|
bare-path: 3.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bare-abort-controller
|
- bare-abort-controller
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tsview',
|
name: '@git.zone/tsview',
|
||||||
version: '1.6.1',
|
version: '1.7.0',
|
||||||
description: 'A CLI tool for viewing S3 and MongoDB data with a web UI'
|
description: 'A CLI tool for viewing S3 and MongoDB data with a web UI'
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tsview',
|
name: '@git.zone/tsview',
|
||||||
version: '1.6.1',
|
version: '1.7.0',
|
||||||
description: 'A CLI tool for viewing S3 and MongoDB data with a web UI'
|
description: 'A CLI tool for viewing S3 and MongoDB data with a web UI'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class TsviewS3Browser extends DeesElement {
|
|||||||
private accessor refreshKey: number = 0;
|
private accessor refreshKey: number = 0;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private accessor previewWidth: number = 350;
|
private accessor previewWidth: number = 700;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private accessor isResizingPreview: boolean = false;
|
private accessor isResizingPreview: boolean = false;
|
||||||
@@ -123,7 +123,7 @@ export class TsviewS3Browser extends DeesElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content.has-preview {
|
.content.has-preview {
|
||||||
grid-template-columns: 1fr 4px var(--preview-width, 350px);
|
grid-template-columns: 1fr 4px var(--preview-width, 700px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resize-divider {
|
.resize-divider {
|
||||||
@@ -305,7 +305,7 @@ export class TsviewS3Browser extends DeesElement {
|
|||||||
const contentEl = this.shadowRoot?.querySelector('.content');
|
const contentEl = this.shadowRoot?.querySelector('.content');
|
||||||
if (!contentEl) return;
|
if (!contentEl) return;
|
||||||
const containerRect = contentEl.getBoundingClientRect();
|
const containerRect = contentEl.getBoundingClientRect();
|
||||||
const newWidth = Math.min(Math.max(containerRect.right - e.clientX, 250), 600);
|
const newWidth = Math.min(Math.max(containerRect.right - e.clientX, 250), 1000);
|
||||||
this.previewWidth = newWidth;
|
this.previewWidth = newWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,10 +43,25 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
@state()
|
@state()
|
||||||
private accessor createDialogName: string = '';
|
private accessor createDialogName: string = '';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private accessor dragOverColumnIndex: number = -1;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private accessor dragOverFolderPrefix: string | null = null;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private accessor uploading: boolean = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private accessor uploadProgress: { current: number; total: number } | null = null;
|
||||||
|
|
||||||
private resizing: { columnIndex: number; startX: number; startWidth: number } | null = null;
|
private resizing: { columnIndex: number; startX: number; startWidth: number } | null = null;
|
||||||
private readonly DEFAULT_COLUMN_WIDTH = 250;
|
private readonly DEFAULT_COLUMN_WIDTH = 250;
|
||||||
private readonly MIN_COLUMN_WIDTH = 150;
|
private readonly MIN_COLUMN_WIDTH = 150;
|
||||||
private readonly MAX_COLUMN_WIDTH = 500;
|
private readonly MAX_COLUMN_WIDTH = 500;
|
||||||
|
private dragCounters: Map<number, number> = new Map();
|
||||||
|
private folderHoverTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
private fileInputElement: HTMLInputElement | null = null;
|
||||||
|
|
||||||
public static styles = [
|
public static styles = [
|
||||||
cssManager.defaultStyles,
|
cssManager.defaultStyles,
|
||||||
@@ -77,6 +92,7 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resize-handle {
|
.resize-handle {
|
||||||
@@ -183,6 +199,58 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column.drag-over {
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
outline: 2px dashed rgba(59, 130, 246, 0.4);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-item.folder.drag-target {
|
||||||
|
background: rgba(59, 130, 246, 0.2) !important;
|
||||||
|
outline: 1px solid rgba(59, 130, 246, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-overlay .upload-text {
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-overlay .upload-progress {
|
||||||
|
color: #888;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-hint {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 8px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: rgba(59, 130, 246, 0.9);
|
||||||
|
color: white;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
pointer-events: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-overlay {
|
.dialog-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -288,6 +356,13 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
await this.loadInitialColumn();
|
await this.loadInitialColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this.clearFolderHover();
|
||||||
|
this.dragCounters.clear();
|
||||||
|
if (this.fileInputElement) { this.fileInputElement.remove(); this.fileInputElement = null; }
|
||||||
|
}
|
||||||
|
|
||||||
updated(changedProperties: Map<string, unknown>) {
|
updated(changedProperties: Map<string, unknown>) {
|
||||||
// Only reset columns when bucket changes or refresh is triggered
|
// Only reset columns when bucket changes or refresh is triggered
|
||||||
// Internal folder navigation is handled by selectFolder() which appends columns
|
// Internal folder navigation is handled by selectFolder() which appends columns
|
||||||
@@ -457,6 +532,11 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
iconName: 'lucide:filePlus',
|
iconName: 'lucide:filePlus',
|
||||||
action: async () => this.openCreateDialog('file', prefix),
|
action: async () => this.openCreateDialog('file', prefix),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Upload Files',
|
||||||
|
iconName: 'lucide:upload',
|
||||||
|
action: async () => this.triggerFileUpload(prefix),
|
||||||
|
},
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{
|
{
|
||||||
name: 'Delete Folder',
|
name: 'Delete Folder',
|
||||||
@@ -535,6 +615,11 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
iconName: 'lucide:filePlus',
|
iconName: 'lucide:filePlus',
|
||||||
action: async () => this.openCreateDialog('file', prefix),
|
action: async () => this.openCreateDialog('file', prefix),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Upload Files',
|
||||||
|
iconName: 'lucide:upload',
|
||||||
|
action: async () => this.triggerFileUpload(prefix),
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,6 +657,145 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
return defaults[ext] || '';
|
return defaults[ext] || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- File upload helpers ---
|
||||||
|
|
||||||
|
private ensureFileInput(): HTMLInputElement {
|
||||||
|
if (!this.fileInputElement) {
|
||||||
|
this.fileInputElement = document.createElement('input');
|
||||||
|
this.fileInputElement.type = 'file';
|
||||||
|
this.fileInputElement.multiple = true;
|
||||||
|
this.fileInputElement.style.display = 'none';
|
||||||
|
this.shadowRoot!.appendChild(this.fileInputElement);
|
||||||
|
}
|
||||||
|
return this.fileInputElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
private triggerFileUpload(targetPrefix: string) {
|
||||||
|
const input = this.ensureFileInput();
|
||||||
|
input.value = '';
|
||||||
|
const handler = async () => {
|
||||||
|
input.removeEventListener('change', handler);
|
||||||
|
if (input.files && input.files.length > 0) {
|
||||||
|
await this.uploadFiles(Array.from(input.files), targetPrefix);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
input.addEventListener('change', handler);
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async uploadFiles(files: File[], targetPrefix: string) {
|
||||||
|
this.uploading = true;
|
||||||
|
this.uploadProgress = { current: 0, total: files.length };
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
this.uploadProgress = { current: i + 1, total: files.length };
|
||||||
|
try {
|
||||||
|
const base64 = await this.readFileAsBase64(file);
|
||||||
|
const key = targetPrefix + file.name;
|
||||||
|
const contentType = file.type || this.getContentType(file.name.split('.').pop()?.toLowerCase() || '');
|
||||||
|
await apiService.putObject(this.bucketName, key, base64, contentType);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to upload ${file.name}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uploading = false;
|
||||||
|
this.uploadProgress = null;
|
||||||
|
await this.refreshColumnByPrefix(targetPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readFileAsBase64(file: File): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
const result = reader.result as string;
|
||||||
|
resolve(result.split(',')[1] || '');
|
||||||
|
};
|
||||||
|
reader.onerror = () => reject(reader.error);
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async refreshColumnByPrefix(prefix: string) {
|
||||||
|
const columnIndex = this.columns.findIndex(col => col.prefix === prefix);
|
||||||
|
if (columnIndex === -1) {
|
||||||
|
await this.loadInitialColumn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const result = await apiService.listObjects(this.bucketName, prefix, '/');
|
||||||
|
this.columns = this.columns.map((col, i) =>
|
||||||
|
i === columnIndex ? { ...col, objects: result.objects, prefixes: result.prefixes } : col
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
await this.loadInitialColumn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Drag-and-drop handlers ---
|
||||||
|
|
||||||
|
private handleColumnDragEnter(e: DragEvent, columnIndex: number) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const count = (this.dragCounters.get(columnIndex) || 0) + 1;
|
||||||
|
this.dragCounters.set(columnIndex, count);
|
||||||
|
this.dragOverColumnIndex = columnIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleColumnDragOver(e: DragEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleColumnDragLeave(e: DragEvent, columnIndex: number) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const count = (this.dragCounters.get(columnIndex) || 1) - 1;
|
||||||
|
this.dragCounters.set(columnIndex, count);
|
||||||
|
if (count <= 0) {
|
||||||
|
this.dragCounters.delete(columnIndex);
|
||||||
|
if (this.dragOverColumnIndex === columnIndex) this.dragOverColumnIndex = -1;
|
||||||
|
this.clearFolderHover();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleColumnDrop(e: DragEvent, columnIndex: number) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.dragCounters.clear();
|
||||||
|
this.dragOverColumnIndex = -1;
|
||||||
|
|
||||||
|
const files = e.dataTransfer?.files;
|
||||||
|
if (!files || files.length === 0) return;
|
||||||
|
|
||||||
|
const targetPrefix = this.dragOverFolderPrefix ?? this.columns[columnIndex].prefix;
|
||||||
|
this.clearFolderHover();
|
||||||
|
this.uploadFiles(Array.from(files), targetPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleFolderDragEnter(e: DragEvent, folderPrefix: string) {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.clearFolderHover();
|
||||||
|
this.folderHoverTimer = setTimeout(() => {
|
||||||
|
this.dragOverFolderPrefix = folderPrefix;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleFolderDragLeave(e: DragEvent, folderPrefix: string) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (this.dragOverFolderPrefix === folderPrefix) this.dragOverFolderPrefix = null;
|
||||||
|
if (this.folderHoverTimer) {
|
||||||
|
clearTimeout(this.folderHoverTimer);
|
||||||
|
this.folderHoverTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearFolderHover() {
|
||||||
|
if (this.folderHoverTimer) { clearTimeout(this.folderHoverTimer); this.folderHoverTimer = null; }
|
||||||
|
this.dragOverFolderPrefix = null;
|
||||||
|
}
|
||||||
|
|
||||||
private async handleCreate() {
|
private async handleCreate() {
|
||||||
if (!this.createDialogName.trim()) return;
|
if (!this.createDialogName.trim()) return;
|
||||||
|
|
||||||
@@ -675,7 +899,14 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
: this.bucketName;
|
: this.bucketName;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="column" style="width: ${column.width}px">
|
<div
|
||||||
|
class="column ${this.dragOverColumnIndex === index ? 'drag-over' : ''}"
|
||||||
|
style="width: ${column.width}px"
|
||||||
|
@dragenter=${(e: DragEvent) => this.handleColumnDragEnter(e, index)}
|
||||||
|
@dragover=${(e: DragEvent) => this.handleColumnDragOver(e)}
|
||||||
|
@dragleave=${(e: DragEvent) => this.handleColumnDragLeave(e, index)}
|
||||||
|
@drop=${(e: DragEvent) => this.handleColumnDrop(e, index)}
|
||||||
|
>
|
||||||
<div class="column-header" title=${column.prefix || this.bucketName}>
|
<div class="column-header" title=${column.prefix || this.bucketName}>
|
||||||
${headerName}
|
${headerName}
|
||||||
</div>
|
</div>
|
||||||
@@ -686,9 +917,11 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
${column.prefixes.map(
|
${column.prefixes.map(
|
||||||
(prefix) => html`
|
(prefix) => html`
|
||||||
<div
|
<div
|
||||||
class="column-item folder ${column.selectedItem === prefix ? 'selected' : ''}"
|
class="column-item folder ${column.selectedItem === prefix ? 'selected' : ''} ${this.dragOverFolderPrefix === prefix ? 'drag-target' : ''}"
|
||||||
@click=${() => this.selectFolder(index, prefix)}
|
@click=${() => this.selectFolder(index, prefix)}
|
||||||
@contextmenu=${(e: MouseEvent) => this.handleFolderContextMenu(e, index, prefix)}
|
@contextmenu=${(e: MouseEvent) => this.handleFolderContextMenu(e, index, prefix)}
|
||||||
|
@dragenter=${(e: DragEvent) => this.handleFolderDragEnter(e, prefix)}
|
||||||
|
@dragleave=${(e: DragEvent) => this.handleFolderDragLeave(e, prefix)}
|
||||||
>
|
>
|
||||||
<svg class="icon" viewBox="0 0 24 24" fill="currentColor">
|
<svg class="icon" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
|
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
|
||||||
@@ -715,6 +948,21 @@ export class TsviewS3Columns extends DeesElement {
|
|||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
${this.dragOverColumnIndex === index ? html`
|
||||||
|
<div class="drag-hint">
|
||||||
|
${this.dragOverFolderPrefix
|
||||||
|
? `Drop to upload into ${getFileName(this.dragOverFolderPrefix)}`
|
||||||
|
: 'Drop to upload here'}
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
${this.uploading ? html`
|
||||||
|
<div class="upload-overlay">
|
||||||
|
<div class="upload-text">Uploading...</div>
|
||||||
|
${this.uploadProgress ? html`
|
||||||
|
<div class="upload-progress">${this.uploadProgress.current} / ${this.uploadProgress.total} files</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
@state()
|
@state()
|
||||||
private accessor hasChanges: boolean = false;
|
private accessor hasChanges: boolean = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private accessor editing: boolean = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private accessor contentType: string = '';
|
private accessor contentType: string = '';
|
||||||
|
|
||||||
@@ -83,8 +86,12 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
|
|
||||||
.preview-content {
|
.preview-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
padding: 12px;
|
}
|
||||||
|
|
||||||
|
.preview-content dees-preview {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-content.code-editor {
|
.preview-content.code-editor {
|
||||||
@@ -96,25 +103,6 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-image {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-text {
|
|
||||||
font-family: 'Monaco', 'Menlo', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.5;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
color: #ccc;
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-actions {
|
.preview-actions {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-top: 1px solid #333;
|
border-top: 1px solid #333;
|
||||||
@@ -225,11 +213,6 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.binary-preview {
|
|
||||||
text-align: center;
|
|
||||||
color: #888;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -243,6 +226,7 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
this.error = '';
|
this.error = '';
|
||||||
this.originalTextContent = '';
|
this.originalTextContent = '';
|
||||||
this.hasChanges = false;
|
this.hasChanges = false;
|
||||||
|
this.editing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,6 +237,7 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.error = '';
|
this.error = '';
|
||||||
this.hasChanges = false;
|
this.hasChanges = false;
|
||||||
|
this.editing = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await apiService.getObject(this.bucketName, this.objectKey);
|
const result = await apiService.getObject(this.bucketName, this.objectKey);
|
||||||
@@ -395,12 +380,17 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
this.hasChanges = newValue !== this.originalTextContent;
|
this.hasChanges = newValue !== this.originalTextContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleEdit() {
|
||||||
|
this.editing = true;
|
||||||
|
}
|
||||||
|
|
||||||
private handleDiscard() {
|
private handleDiscard() {
|
||||||
const codeEditor = this.shadowRoot?.querySelector('dees-input-code') as any;
|
const codeEditor = this.shadowRoot?.querySelector('dees-input-code') as any;
|
||||||
if (codeEditor) {
|
if (codeEditor) {
|
||||||
codeEditor.value = this.originalTextContent;
|
codeEditor.value = this.originalTextContent;
|
||||||
}
|
}
|
||||||
this.hasChanges = false;
|
this.hasChanges = false;
|
||||||
|
this.editing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleSave() {
|
private async handleSave() {
|
||||||
@@ -428,6 +418,7 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
if (success) {
|
if (success) {
|
||||||
this.originalTextContent = currentContent;
|
this.originalTextContent = currentContent;
|
||||||
this.hasChanges = false;
|
this.hasChanges = false;
|
||||||
|
this.editing = false;
|
||||||
// Update the stored content as well
|
// Update the stored content as well
|
||||||
this.content = base64Content;
|
this.content = base64Content;
|
||||||
}
|
}
|
||||||
@@ -486,10 +477,8 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="preview-content ${this.isText() ? 'code-editor' : ''}">
|
<div class="preview-content ${this.editing ? 'code-editor' : ''}">
|
||||||
${this.isImage()
|
${this.editing
|
||||||
? html`<img class="preview-image" src="data:${this.contentType};base64,${this.content}" />`
|
|
||||||
: this.isText()
|
|
||||||
? html`
|
? html`
|
||||||
<dees-input-code
|
<dees-input-code
|
||||||
.value=${this.originalTextContent}
|
.value=${this.originalTextContent}
|
||||||
@@ -498,28 +487,50 @@ export class TsviewS3Preview extends DeesElement {
|
|||||||
@content-change=${(e: CustomEvent) => this.handleContentChange(e)}
|
@content-change=${(e: CustomEvent) => this.handleContentChange(e)}
|
||||||
></dees-input-code>
|
></dees-input-code>
|
||||||
`
|
`
|
||||||
|
: this.isText()
|
||||||
|
? html`
|
||||||
|
<dees-preview
|
||||||
|
.textContent=${this.originalTextContent}
|
||||||
|
.filename=${getFileName(this.objectKey)}
|
||||||
|
.language=${this.getLanguage()}
|
||||||
|
.showToolbar=${true}
|
||||||
|
.showFilename=${false}
|
||||||
|
></dees-preview>
|
||||||
|
`
|
||||||
: html`
|
: html`
|
||||||
<div class="binary-preview">
|
<dees-preview
|
||||||
<p>Binary file preview not available</p>
|
.base64=${this.content}
|
||||||
<p>Download to view</p>
|
.mimeType=${this.contentType}
|
||||||
</div>
|
.filename=${getFileName(this.objectKey)}
|
||||||
`}
|
.showToolbar=${true}
|
||||||
|
.showFilename=${false}
|
||||||
|
></dees-preview>
|
||||||
|
`
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="preview-actions">
|
<div class="preview-actions">
|
||||||
${this.hasChanges ? html`
|
${this.editing
|
||||||
<button class="action-btn secondary" @click=${this.handleDiscard}>Discard</button>
|
? html`
|
||||||
|
<button class="action-btn secondary" @click=${this.handleDiscard}>
|
||||||
|
${this.hasChanges ? 'Discard' : 'Cancel'}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="action-btn primary"
|
class="action-btn primary"
|
||||||
@click=${this.handleSave}
|
@click=${this.handleSave}
|
||||||
?disabled=${this.saving}
|
?disabled=${this.saving || !this.hasChanges}
|
||||||
>
|
>
|
||||||
${this.saving ? 'Saving...' : 'Save'}
|
${this.saving ? 'Saving...' : 'Save'}
|
||||||
</button>
|
</button>
|
||||||
` : html`
|
`
|
||||||
|
: html`
|
||||||
|
${this.isText()
|
||||||
|
? html`<button class="action-btn" @click=${this.handleEdit}>Edit</button>`
|
||||||
|
: ''}
|
||||||
<button class="action-btn" @click=${this.handleDownload}>Download</button>
|
<button class="action-btn" @click=${this.handleDownload}>Download</button>
|
||||||
<button class="action-btn danger" @click=${this.handleDelete}>Delete</button>
|
<button class="action-btn danger" @click=${this.handleDelete}>Delete</button>
|
||||||
`}
|
`
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user