@webhandle/tree-file-browser 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client-lib/add-soft-breaks.mjs +25 -0
- package/client-lib/file-select-dialog.mjs +16 -0
- package/client-lib/image-browser-view-methods/drag-and-drop.mjs +10 -0
- package/client-lib/image-browser-view-methods/file-obj-manipulation.mjs +31 -9
- package/client-lib/image-browser-view-methods/sink.mjs +1 -0
- package/client-lib/image-browser-view-methods/upload.mjs +40 -10
- package/client-lib/image-browser-view-methods/utils.mjs +19 -0
- package/client-lib/image-browser-view-methods/view-interactions.mjs +65 -7
- package/client-lib/image-browser-view.mjs +29 -39
- package/less/components.less +29 -0
- package/package.json +1 -1
- package/public/tree-file-browser/resources/css/tree-browser.css +21 -0
- package/public/tree-file-browser/resources/css/tree-browser.css.map +1 -1
- package/public/tree-file-browser/resources/js/tree-file-browser.js +1 -1
- package/public/tree-file-browser/resources/js/tree-file-browser.js.map +1 -1
- package/views/load-browser-views.js +1 -1
- package/views/webhandle-tree-image-browser/image-browser-frame.tri +3 -0
- package/views/webhandle-tree-image-browser/variant-choice-box.tri +2 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
function breakOnChars(chrs, txt) {
|
|
3
|
+
for (let c of chrs) {
|
|
4
|
+
txt = txt.split(c).join('<wbr>' + c)
|
|
5
|
+
}
|
|
6
|
+
return txt
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function addSoftBreaks(txt) {
|
|
10
|
+
if(!txt || typeof txt !== 'string') {
|
|
11
|
+
return txt
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let chars = [...txt]
|
|
15
|
+
txt = ''
|
|
16
|
+
for(let i = 0; i < chars.length; i++) {
|
|
17
|
+
txt += chars[i]
|
|
18
|
+
if(i % 10 === 0) {
|
|
19
|
+
txt += '<wbr>'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
txt = breakOnChars(['_'], txt)
|
|
23
|
+
|
|
24
|
+
return txt
|
|
25
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import Dialog from 'ei-dialog'
|
|
2
2
|
import ImageBrowserView from './image-browser-view.mjs'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Selects a file.
|
|
6
|
+
* @param {Object} [options]
|
|
7
|
+
* @param {string} [options.title] The dialog box title
|
|
8
|
+
* @param {boolean} [options.chooseOnUpload] If true, uploading a file causes the dialog to close and the URL of uploaded
|
|
9
|
+
* file to be returned
|
|
10
|
+
*/
|
|
4
11
|
export class FileSelectDialog extends Dialog {
|
|
5
12
|
constructor(options) {
|
|
6
13
|
super(Object.assign({
|
|
@@ -31,8 +38,17 @@ export class FileSelectDialog extends Dialog {
|
|
|
31
38
|
imageBrowserView.emitter.on('select', async function (evt) {
|
|
32
39
|
|
|
33
40
|
})
|
|
41
|
+
imageBrowserView.emitter.on('upload', async function (evt) {
|
|
42
|
+
if(dialog.chooseOnUpload) {
|
|
43
|
+
dialog.close()
|
|
44
|
+
dialog.resolve({
|
|
45
|
+
url: evt.accessUrls[0]
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
})
|
|
34
49
|
}
|
|
35
50
|
}
|
|
51
|
+
, chooseOnUpload: true
|
|
36
52
|
}, options,
|
|
37
53
|
{
|
|
38
54
|
on: {
|
|
@@ -54,4 +54,14 @@ export function dragOver(evt, selected) {
|
|
|
54
54
|
export function _cleanupDropDone() {
|
|
55
55
|
this.overCount = 0;
|
|
56
56
|
[...this.el.querySelectorAll('.file-dropping')].forEach(cover => cover.classList.remove('file-dropping'))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function handlePaste(evt) {
|
|
60
|
+
if (this.ignoreGlobalEvents) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
evt.preventDefault()
|
|
64
|
+
if (evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length > 0) {
|
|
65
|
+
this.uploadFiles(evt.clipboardData.files, { uploadType: 'guided' })
|
|
66
|
+
}
|
|
57
67
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import condense from '@dankolz/webp-detection/lib/condense-image-variants.js'
|
|
2
|
+
import escapeHtmlAttributeValue from '@dankolz/escape-html-attribute-value'
|
|
3
|
+
import addSoftBreaks from '../add-soft-breaks.mjs'
|
|
2
4
|
|
|
3
5
|
export function createVariantValues(info) {
|
|
4
6
|
let variants = condense(info.children)
|
|
@@ -19,6 +21,7 @@ export function createVariantValues(info) {
|
|
|
19
21
|
child.thumbnailIcon = 'image'
|
|
20
22
|
if (child.preview) {
|
|
21
23
|
child.thumbnail = this._createAccessUrl(child.preview.file)
|
|
24
|
+
child.safeThumbnail = escapeHtmlAttributeValue(child.thumbnail)
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -36,7 +39,7 @@ export function createVariantValues(info) {
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
|
|
39
|
-
// Determine extensions
|
|
42
|
+
// Determine extensions, add additional top level info (safeBaseName)
|
|
40
43
|
for (let item of variantValues) {
|
|
41
44
|
item.extensions = this._determineExtensions(item)
|
|
42
45
|
item.sizes = this._determineSizes(item)
|
|
@@ -46,6 +49,7 @@ export function createVariantValues(info) {
|
|
|
46
49
|
else {
|
|
47
50
|
item.size = this._formatBytes(item.sizes[0]) + ' - ' + this._formatBytes(item.sizes[1])
|
|
48
51
|
}
|
|
52
|
+
item.safeBaseName = addSoftBreaks(escapeHtmlAttributeValue(item.baseName))
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
variantValues.sort(this._compareVariants)
|
|
@@ -117,7 +121,18 @@ export function _getAssociatedRealFiles(variant) {
|
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
export function _createAccessUrl(file) {
|
|
120
|
-
return file.accessUrl
|
|
124
|
+
return this.escapeAccessUrl(file.accessUrl)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function urlEscapeChars(chrs, url) {
|
|
128
|
+
for (let c of chrs) {
|
|
129
|
+
url = url.split(c).join(encodeURIComponent(c))
|
|
130
|
+
}
|
|
131
|
+
return url
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function escapeAccessUrl(url) {
|
|
135
|
+
return urlEscapeChars(['%', ' ', '#', '?', '<', '>', '$', '@', '^', '&'], url)
|
|
121
136
|
}
|
|
122
137
|
|
|
123
138
|
|
|
@@ -173,12 +188,7 @@ export async function getSelectedUrl(selectedFiles) {
|
|
|
173
188
|
let defData = await this.sink.read(variant.definitionFile.relPath)
|
|
174
189
|
try {
|
|
175
190
|
let data = JSON.parse(defData)
|
|
176
|
-
|
|
177
|
-
base += `#format=webp2x&width=${sizes[0]}&height=${sizes[1]}`
|
|
178
|
-
|
|
179
|
-
if(data.altText) {
|
|
180
|
-
base += '&alt=' + encodeURIComponent(data.altText)
|
|
181
|
-
}
|
|
191
|
+
base += this.getSelectedUrlExtFromMeta(data)
|
|
182
192
|
}
|
|
183
193
|
catch(e) {
|
|
184
194
|
|
|
@@ -187,4 +197,16 @@ export async function getSelectedUrl(selectedFiles) {
|
|
|
187
197
|
|
|
188
198
|
return base
|
|
189
199
|
|
|
190
|
-
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function getSelectedUrlExtFromMeta(data) {
|
|
203
|
+
let url = ''
|
|
204
|
+
let sizes = data.displaySize.split('x')
|
|
205
|
+
url += `#format=webp2x&width=${sizes[0]}&height=${sizes[1]}`
|
|
206
|
+
|
|
207
|
+
if(data.altText) {
|
|
208
|
+
url += '&alt=' + encodeURIComponent(data.altText)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return url
|
|
212
|
+
}
|
|
@@ -38,15 +38,26 @@ export async function _uploadGuidedImageFile(file) {
|
|
|
38
38
|
|
|
39
39
|
let note = this._addPending(file)
|
|
40
40
|
let files = await makeImageSet(file, makeImageData)
|
|
41
|
+
let meta = JSON.parse(files[baseFileName + '.json'])
|
|
41
42
|
|
|
43
|
+
let mainUrl
|
|
42
44
|
for (let fileName of Object.keys(files)) {
|
|
43
|
-
await this._uploadData(fileName, files[fileName])
|
|
45
|
+
let path = await this._uploadData(fileName, files[fileName])
|
|
46
|
+
if(fileName === meta.name + '.' + meta.fallback) {
|
|
47
|
+
mainUrl = path
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
let base = this._transformRelativeUrlToPublic(mainUrl)
|
|
52
|
+
|
|
53
|
+
let ext = this.getSelectedUrlExtFromMeta(meta)
|
|
54
|
+
base += ext
|
|
55
|
+
|
|
46
56
|
if (note) {
|
|
47
57
|
note.remove()
|
|
48
58
|
}
|
|
49
|
-
|
|
59
|
+
|
|
60
|
+
return base
|
|
50
61
|
}
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -71,12 +82,13 @@ export async function _uploadGuidedFile(file) {
|
|
|
71
82
|
if (result) {
|
|
72
83
|
let note = this._addPending(file)
|
|
73
84
|
|
|
74
|
-
await this._uploadData(result.name, file)
|
|
85
|
+
let mainUrl = await this._uploadData(result.name, file)
|
|
86
|
+
let base = this._transformRelativeUrlToPublic(mainUrl)
|
|
75
87
|
|
|
76
88
|
if (note) {
|
|
77
89
|
note.remove()
|
|
78
90
|
}
|
|
79
|
-
return
|
|
91
|
+
return base
|
|
80
92
|
}
|
|
81
93
|
}
|
|
82
94
|
|
|
@@ -89,18 +101,27 @@ export async function _uploadAutomaticImageFile(file) {
|
|
|
89
101
|
baseFileName: baseFileName,
|
|
90
102
|
outputFormat: file.type
|
|
91
103
|
})
|
|
104
|
+
let meta = JSON.parse(files[baseFileName + '.json'])
|
|
92
105
|
|
|
106
|
+
let mainUrl
|
|
93
107
|
for (let fileName of Object.keys(files)) {
|
|
94
|
-
await this._uploadData(fileName, files[fileName])
|
|
108
|
+
let path = await this._uploadData(fileName, files[fileName])
|
|
109
|
+
if(fileName === meta.name + '.' + meta.fallback) {
|
|
110
|
+
mainUrl = path
|
|
111
|
+
}
|
|
95
112
|
}
|
|
113
|
+
let base = this._transformRelativeUrlToPublic(mainUrl)
|
|
114
|
+
let ext = this.getSelectedUrlExtFromMeta(meta)
|
|
115
|
+
base += ext
|
|
96
116
|
|
|
97
117
|
if (note) {
|
|
98
118
|
note.remove()
|
|
99
119
|
}
|
|
100
|
-
return
|
|
120
|
+
return base
|
|
101
121
|
}
|
|
102
122
|
|
|
103
123
|
export async function uploadFiles(files, { uploadType } = {}) {
|
|
124
|
+
let uploadedAccessUrls = []
|
|
104
125
|
for (let file of files) {
|
|
105
126
|
|
|
106
127
|
let uploaded = false
|
|
@@ -115,17 +136,19 @@ export async function uploadFiles(files, { uploadType } = {}) {
|
|
|
115
136
|
}
|
|
116
137
|
else {
|
|
117
138
|
let note = this._addPending(file)
|
|
139
|
+
let path
|
|
118
140
|
if (uploadType === 'automatic') {
|
|
119
141
|
let parts = nameParts(file)
|
|
120
|
-
await this._uploadData(parts.join('.'), file)
|
|
142
|
+
path = await this._uploadData(parts.join('.'), file)
|
|
121
143
|
}
|
|
122
144
|
else {
|
|
123
|
-
await this._uploadData(file.name, file)
|
|
145
|
+
path = await this._uploadData(file.name, file)
|
|
124
146
|
}
|
|
147
|
+
uploaded = this._transformRelativeUrlToPublic(path)
|
|
148
|
+
|
|
125
149
|
if (note) {
|
|
126
150
|
note.remove()
|
|
127
151
|
}
|
|
128
|
-
uploaded = true
|
|
129
152
|
}
|
|
130
153
|
if (this.eventNotificationPanel && uploaded) {
|
|
131
154
|
this.eventNotificationPanel.addNotification({
|
|
@@ -136,8 +159,14 @@ export async function uploadFiles(files, { uploadType } = {}) {
|
|
|
136
159
|
, ttl: 2000
|
|
137
160
|
})
|
|
138
161
|
}
|
|
162
|
+
uploadedAccessUrls.push(uploaded)
|
|
139
163
|
}
|
|
140
164
|
this.setCurrentNode(this.currentNode)
|
|
165
|
+
this.emitter.emit('upload', {
|
|
166
|
+
type: 'upload'
|
|
167
|
+
, accessUrls: uploadedAccessUrls
|
|
168
|
+
})
|
|
169
|
+
return uploadedAccessUrls
|
|
141
170
|
}
|
|
142
171
|
|
|
143
172
|
|
|
@@ -153,7 +182,8 @@ export async function _uploadFile(evt, selected) {
|
|
|
153
182
|
}
|
|
154
183
|
let files = await this._getFilesFromEvent(evt)
|
|
155
184
|
if(files.length > 0) {
|
|
156
|
-
this.uploadFiles(files, { uploadType: 'guided' })
|
|
185
|
+
let result = this.uploadFiles(files, { uploadType: 'guided' })
|
|
157
186
|
input.value = ''
|
|
187
|
+
return result
|
|
158
188
|
}
|
|
159
189
|
}
|
|
@@ -106,3 +106,22 @@ export function _isImageFile(file) {
|
|
|
106
106
|
}
|
|
107
107
|
return false
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
export function setIfNotSet(key, value) {
|
|
111
|
+
if (!(key in this)) {
|
|
112
|
+
this[key] = value
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function _addPending(file) {
|
|
117
|
+
let note
|
|
118
|
+
if (this.eventNotificationPanel) {
|
|
119
|
+
note = this.eventNotificationPanel.addNotification({
|
|
120
|
+
model: {
|
|
121
|
+
status: 'pending',
|
|
122
|
+
headline: `uploading ${file.name}`
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
return note
|
|
127
|
+
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { InfoDialog } from '../info-dialog.mjs'
|
|
2
|
+
import escapeHtmlAttributeValue from '@dankolz/escape-html-attribute-value'
|
|
2
3
|
|
|
3
4
|
export function changeFilesView(evt, selected) {
|
|
5
|
+
let className = selected.getAttribute('data-show-class')
|
|
6
|
+
this.changeFilesViewToClass(className)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function changeFilesViewToClass(className) {
|
|
4
10
|
let choiceBoxes = this.el.querySelector('.choice-boxes')
|
|
5
|
-
let classes = [...
|
|
11
|
+
let classes = [...this.el.querySelector('.view-icons').querySelectorAll('button')].map(button => button.getAttribute('data-show-class'))
|
|
6
12
|
classes.forEach(item => {
|
|
7
13
|
choiceBoxes.classList.remove(item)
|
|
8
14
|
})
|
|
9
|
-
choiceBoxes.classList.add(
|
|
15
|
+
choiceBoxes.classList.add(className)
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
export function applyFilter(evt, selected) {
|
|
@@ -16,12 +22,14 @@ export function applyFilter(evt, selected) {
|
|
|
16
22
|
for (let variant of allVariants) {
|
|
17
23
|
variant.classList.remove('hidden')
|
|
18
24
|
if (value) {
|
|
19
|
-
|
|
25
|
+
value = value.toLowerCase()
|
|
26
|
+
let searchString = variant.variant.baseName.toLowerCase() + variant.variant.extensions.map(ext => ext.toLowerCase()).join()
|
|
20
27
|
if (searchString.indexOf(value) < 0) {
|
|
21
28
|
variant.classList.add('hidden')
|
|
22
29
|
}
|
|
23
30
|
}
|
|
24
31
|
}
|
|
32
|
+
this.setFolderInfo()
|
|
25
33
|
})
|
|
26
34
|
}
|
|
27
35
|
|
|
@@ -69,14 +77,23 @@ export function showVariantDetails(evt, selected) {
|
|
|
69
77
|
|
|
70
78
|
let files = this._getAssociatedRealFiles(variant)
|
|
71
79
|
|
|
72
|
-
let content = '<
|
|
80
|
+
let content = '<div class="variant-details-information">'
|
|
81
|
+
if (variant.safeThumbnail) {
|
|
82
|
+
content += `<div class="details-preview-image">
|
|
83
|
+
<img loading="lazy" src="${variant.safeThumbnail}" />
|
|
84
|
+
</div>`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
content += '<ul class="variants">'
|
|
73
88
|
for (let file of files) {
|
|
74
|
-
content += '<li><a target="_blank" href="' + file.accessUrl + '">'
|
|
75
|
-
content += file.name + '</a> - ' + this._formatBytes(file.stat.size)
|
|
89
|
+
content += '<li><a target="_blank" href="' + escapeHtmlAttributeValue(this.escapeAccessUrl(file.accessUrl)) + '">'
|
|
90
|
+
content += escapeHtmlAttributeValue(file.name) + '</a> - ' + this._formatBytes(file.stat.size)
|
|
76
91
|
content += '</li>'
|
|
77
92
|
}
|
|
78
93
|
content += '</ul>'
|
|
79
94
|
|
|
95
|
+
content += '</div>'
|
|
96
|
+
|
|
80
97
|
let dialog = new InfoDialog({
|
|
81
98
|
title: 'File Details: ' + variant.baseName
|
|
82
99
|
, body: content
|
|
@@ -92,5 +109,46 @@ export function showVariantDetails(evt, selected) {
|
|
|
92
109
|
if (data) {
|
|
93
110
|
}
|
|
94
111
|
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function setFolderInfo() {
|
|
115
|
+
let fileCount = 0
|
|
116
|
+
let variantCount = 0
|
|
117
|
+
let byteCount = 0
|
|
118
|
+
let nonImages = 0
|
|
119
|
+
let allVariants = this.el.querySelectorAll('.choice-boxes .variant-choice-box')
|
|
120
|
+
for (let variant of allVariants) {
|
|
121
|
+
if (variant.classList.contains('hidden')) {
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
variantCount++
|
|
125
|
+
|
|
126
|
+
if (variant.variant.variants) {
|
|
127
|
+
variant.variant.variants.forEach(variant => {
|
|
128
|
+
fileCount++
|
|
129
|
+
byteCount += variant.file.stat.size
|
|
130
|
+
})
|
|
95
131
|
|
|
96
|
-
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
fileCount++
|
|
135
|
+
byteCount += variant.variant.file.stat.size
|
|
136
|
+
nonImages++
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
this.el.querySelector('.folder-info').innerHTML = `${variantCount} items / ${fileCount} files / ${this._formatBytes(byteCount)} `
|
|
140
|
+
if (variantCount > this.listTriggerSize) {
|
|
141
|
+
this.changeFilesViewToClass('list-text')
|
|
142
|
+
}
|
|
143
|
+
this.el.querySelector('.view-icons').classList.remove('no-img')
|
|
144
|
+
if (variantCount - nonImages > this.listLockSize) {
|
|
145
|
+
this.el.querySelector('.view-icons').classList.add('no-img')
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function cleanFileInfo() {
|
|
150
|
+
this.el.querySelector('.folder-info').innerHTML = ''
|
|
151
|
+
let choicesBoxes = this.el.querySelector('.choice-boxes')
|
|
152
|
+
choicesBoxes.innerHTML = '<div class="loading-info">Loading Information Now</div>'
|
|
153
|
+
this.el.querySelector('.view-icons').classList.add('no-img')
|
|
154
|
+
}
|
|
@@ -8,14 +8,14 @@ import Emitter from '@webhandle/minimal-browser-event-emitter'
|
|
|
8
8
|
// method imports
|
|
9
9
|
import { deleteFile, deleteDirectory } from './image-browser-view-methods/delete.mjs'
|
|
10
10
|
import {
|
|
11
|
-
_join, _determineParentPath, _fileToKalpaNode, _determineExtensions,
|
|
12
|
-
_determineSizes, _sortFiles, _compareVariants, sanitizeFileName, _isImageFile
|
|
11
|
+
_join, _determineParentPath, _fileToKalpaNode, _determineExtensions, _addPending,
|
|
12
|
+
_determineSizes, _sortFiles, _compareVariants, sanitizeFileName, _isImageFile, setIfNotSet
|
|
13
13
|
} from './image-browser-view-methods/utils.mjs'
|
|
14
|
-
import { changeFilesView, applyFilter, clearFilter, selectVariant, showVariantDetails } from './image-browser-view-methods/view-interactions.mjs'
|
|
15
|
-
import { getDropCoverSelector, handleDrop, isFileTypeDrag, dragEnter, dragLeave, dragOver, _cleanupDropDone } from './image-browser-view-methods/drag-and-drop.mjs'
|
|
14
|
+
import { changeFilesView, changeFilesViewToClass, applyFilter, clearFilter, selectVariant, showVariantDetails, setFolderInfo, cleanFileInfo } from './image-browser-view-methods/view-interactions.mjs'
|
|
15
|
+
import { getDropCoverSelector, handleDrop, isFileTypeDrag, dragEnter, dragLeave, dragOver, _cleanupDropDone, handlePaste } from './image-browser-view-methods/drag-and-drop.mjs'
|
|
16
16
|
import { createDirectory } from './image-browser-view-methods/create-directory.mjs'
|
|
17
|
-
import { createVariantValues, _getFilesFromEvent, _getAssociatedRealFiles, _createAccessUrl,
|
|
18
|
-
getSelectedFiles, _transformRelativeUrlToPublic, getSelectedUrl } from './image-browser-view-methods/file-obj-manipulation.mjs'
|
|
17
|
+
import { createVariantValues, _getFilesFromEvent, _getAssociatedRealFiles, _createAccessUrl, escapeAccessUrl,
|
|
18
|
+
getSelectedFiles, _transformRelativeUrlToPublic, getSelectedUrl, getSelectedUrlExtFromMeta } from './image-browser-view-methods/file-obj-manipulation.mjs'
|
|
19
19
|
import { _uploadGuidedImageFile, _uploadGuidedFile, _uploadAutomaticImageFile, uploadFiles, _uploadFileButton, _uploadFile } from './image-browser-view-methods/upload.mjs'
|
|
20
20
|
import { _uploadData, findDirectories } from './image-browser-view-methods/sink.mjs'
|
|
21
21
|
|
|
@@ -33,10 +33,18 @@ export default class ImageBrowserView extends View {
|
|
|
33
33
|
* @param {boolean} [options.ignoreGlobalEvents] False by default, if true it will not listen to events like paste or keypresses
|
|
34
34
|
* which occur on the document
|
|
35
35
|
* @param {Emitter} [options.emitter] Emitter for various file events
|
|
36
|
+
* @param {int} [options.listTriggerSize] If the number of items are over this limit, they are shown as a plain list by default
|
|
37
|
+
* @param {int} [options.listLockSize] If the number of items are over this limit, they can't use anyting other than the plain list
|
|
36
38
|
*/
|
|
37
39
|
constructor(options) {
|
|
38
40
|
super(options)
|
|
41
|
+
this.setIfNotSet('overCount', 0)
|
|
42
|
+
this.setIfNotSet('emitter', new Emitter())
|
|
43
|
+
this.setIfNotSet('fileUploadSelector', 'input[name="fileUpload"]')
|
|
44
|
+
this.setIfNotSet('listTriggerSize', 100)
|
|
45
|
+
this.setIfNotSet('listLockSize', 200)
|
|
39
46
|
}
|
|
47
|
+
|
|
40
48
|
preinitialize() {
|
|
41
49
|
this.className = 'image-browser'
|
|
42
50
|
this.idInd = 1
|
|
@@ -59,42 +67,11 @@ export default class ImageBrowserView extends View {
|
|
|
59
67
|
, 'dragover .': 'dragOver'
|
|
60
68
|
, 'drop .': 'handleDrop'
|
|
61
69
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (!this.emitter) {
|
|
65
|
-
this.emitter = new Emitter()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
this.fileUploadSelector = 'input[name="fileUpload"]'
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
document.addEventListener('paste', this.handlePaste.bind(this))
|
|
70
|
+
document.addEventListener('paste', handlePaste.bind(this))
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
async handlePaste(evt) {
|
|
75
|
-
if(this.ignoreGlobalEvents) {
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
evt.preventDefault()
|
|
79
|
-
if(evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length > 0) {
|
|
80
|
-
this.uploadFiles(evt.clipboardData.files, { uploadType: 'guided' })
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
73
|
|
|
84
74
|
|
|
85
|
-
_addPending(file) {
|
|
86
|
-
let note
|
|
87
|
-
if (this.eventNotificationPanel) {
|
|
88
|
-
note = this.eventNotificationPanel.addNotification({
|
|
89
|
-
model: {
|
|
90
|
-
status: 'pending',
|
|
91
|
-
headline: `uploading ${file.name}`
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
}
|
|
95
|
-
return note
|
|
96
|
-
}
|
|
97
|
-
|
|
98
75
|
async render() {
|
|
99
76
|
this.el.innerHTML = imageBrowserFrame(this.model)
|
|
100
77
|
this.data = []
|
|
@@ -147,17 +124,22 @@ export default class ImageBrowserView extends View {
|
|
|
147
124
|
|
|
148
125
|
async setCurrentNode(node) {
|
|
149
126
|
this.currentNode = node
|
|
127
|
+
this.cleanFileInfo()
|
|
128
|
+
let choicesBoxes = this.el.querySelector('.choice-boxes')
|
|
150
129
|
let info = await this.sink.getFullFileInfo(node.file.relPath)
|
|
151
130
|
let variantValues = this.createVariantValues(info)
|
|
152
131
|
|
|
153
132
|
|
|
133
|
+
if(variantValues.length > this.listTriggerSize) {
|
|
134
|
+
this.changeFilesViewToClass('list-text')
|
|
135
|
+
}
|
|
136
|
+
|
|
154
137
|
let content = ''
|
|
155
138
|
for (let child of variantValues) {
|
|
156
139
|
content += variantChoiceBox(child)
|
|
157
140
|
}
|
|
158
141
|
|
|
159
142
|
|
|
160
|
-
let choicesBoxes = this.el.querySelector('.choice-boxes')
|
|
161
143
|
choicesBoxes.innerHTML = ''
|
|
162
144
|
choicesBoxes.insertAdjacentHTML('beforeend', content)
|
|
163
145
|
|
|
@@ -181,9 +163,11 @@ export default class ImageBrowserView extends View {
|
|
|
181
163
|
_getFilesFromEvent = _getFilesFromEvent
|
|
182
164
|
_getAssociatedRealFiles = _getAssociatedRealFiles
|
|
183
165
|
_createAccessUrl = _createAccessUrl
|
|
166
|
+
escapeAccessUrl = escapeAccessUrl
|
|
184
167
|
getSelectedFiles = getSelectedFiles
|
|
185
168
|
_transformRelativeUrlToPublic = _transformRelativeUrlToPublic
|
|
186
169
|
getSelectedUrl = getSelectedUrl
|
|
170
|
+
getSelectedUrlExtFromMeta = getSelectedUrlExtFromMeta
|
|
187
171
|
|
|
188
172
|
// create-directory
|
|
189
173
|
createDirectory = createDirectory
|
|
@@ -195,14 +179,18 @@ export default class ImageBrowserView extends View {
|
|
|
195
179
|
dragEnter = dragEnter
|
|
196
180
|
dragLeave = dragLeave
|
|
197
181
|
dragOver = dragOver
|
|
182
|
+
handlePaste = handlePaste
|
|
198
183
|
_cleanupDropDone = _cleanupDropDone
|
|
199
184
|
|
|
200
185
|
// view-interactions
|
|
201
186
|
changeFilesView = changeFilesView
|
|
187
|
+
changeFilesViewToClass = changeFilesViewToClass
|
|
202
188
|
applyFilter = applyFilter
|
|
203
189
|
clearFilter = clearFilter
|
|
204
190
|
selectVariant = selectVariant
|
|
205
191
|
showVariantDetails = showVariantDetails
|
|
192
|
+
setFolderInfo = setFolderInfo
|
|
193
|
+
cleanFileInfo = cleanFileInfo
|
|
206
194
|
|
|
207
195
|
// utils
|
|
208
196
|
sanitizeFileName = sanitizeFileName
|
|
@@ -215,6 +203,8 @@ export default class ImageBrowserView extends View {
|
|
|
215
203
|
_fileToKalpaNode = _fileToKalpaNode
|
|
216
204
|
_formatBytes = formatBytes
|
|
217
205
|
_isImageFile = _isImageFile
|
|
206
|
+
setIfNotSet = setIfNotSet
|
|
207
|
+
_addPending = _addPending
|
|
218
208
|
|
|
219
209
|
// delete
|
|
220
210
|
deleteFile = deleteFile
|
package/less/components.less
CHANGED
|
@@ -109,6 +109,14 @@
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
+
|
|
113
|
+
.view-icons {
|
|
114
|
+
&.no-img {
|
|
115
|
+
.show-large-tiles, .show-small-tiles, .show-list-rows {
|
|
116
|
+
display: none;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
112
120
|
|
|
113
121
|
|
|
114
122
|
.node-content {
|
|
@@ -181,6 +189,13 @@
|
|
|
181
189
|
}
|
|
182
190
|
}
|
|
183
191
|
}
|
|
192
|
+
|
|
193
|
+
.loading-info {
|
|
194
|
+
position: absolute;
|
|
195
|
+
top: 50%;
|
|
196
|
+
left: 50%;
|
|
197
|
+
transform: translate(-50%, -50%);
|
|
198
|
+
}
|
|
184
199
|
}
|
|
185
200
|
|
|
186
201
|
.variant-choice-box {
|
|
@@ -355,3 +370,17 @@
|
|
|
355
370
|
|
|
356
371
|
}
|
|
357
372
|
|
|
373
|
+
.variant-details-information {
|
|
374
|
+
.details-preview-image {
|
|
375
|
+
img {
|
|
376
|
+
display: block;
|
|
377
|
+
margin: auto;
|
|
378
|
+
max-width: 300px;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
.variants {
|
|
382
|
+
list-style: none;
|
|
383
|
+
padding: 10px 0;
|
|
384
|
+
margin: 0;
|
|
385
|
+
}
|
|
386
|
+
}
|
package/package.json
CHANGED
|
@@ -86,6 +86,11 @@
|
|
|
86
86
|
.webhandle-file-tree-image-browser .image-browser .image-browser-frame > .directory-side .directory-controls {
|
|
87
87
|
background-color: #393C40;
|
|
88
88
|
}
|
|
89
|
+
.webhandle-file-tree-image-browser .image-browser .view-icons.no-img .show-large-tiles,
|
|
90
|
+
.webhandle-file-tree-image-browser .image-browser .view-icons.no-img .show-small-tiles,
|
|
91
|
+
.webhandle-file-tree-image-browser .image-browser .view-icons.no-img .show-list-rows {
|
|
92
|
+
display: none;
|
|
93
|
+
}
|
|
89
94
|
.webhandle-file-tree-image-browser .image-browser .node-content {
|
|
90
95
|
position: relative;
|
|
91
96
|
}
|
|
@@ -256,6 +261,12 @@
|
|
|
256
261
|
.webhandle-file-tree-image-browser .image-browser .choice-boxes.list-text .img .thumbnail-icon {
|
|
257
262
|
font-size: 0;
|
|
258
263
|
}
|
|
264
|
+
.webhandle-file-tree-image-browser .image-browser .choice-boxes .loading-info {
|
|
265
|
+
position: absolute;
|
|
266
|
+
top: 50%;
|
|
267
|
+
left: 50%;
|
|
268
|
+
transform: translate(-50%, -50%);
|
|
269
|
+
}
|
|
259
270
|
.webhandle-file-tree-image-browser .image-browser .variant-choice-box {
|
|
260
271
|
padding: 10px;
|
|
261
272
|
border-radius: 5px;
|
|
@@ -391,4 +402,14 @@
|
|
|
391
402
|
left: -10000px;
|
|
392
403
|
top: 0;
|
|
393
404
|
}
|
|
405
|
+
.variant-details-information .details-preview-image img {
|
|
406
|
+
display: block;
|
|
407
|
+
margin: auto;
|
|
408
|
+
max-width: 300px;
|
|
409
|
+
}
|
|
410
|
+
.variant-details-information .variants {
|
|
411
|
+
list-style: none;
|
|
412
|
+
padding: 10px 0;
|
|
413
|
+
margin: 0;
|
|
414
|
+
}
|
|
394
415
|
/*# sourceMappingURL=tree-browser.css.map */
|