@webhandle/tree-file-browser 1.0.0 → 1.0.2
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 +41 -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/guilded-image-upload-form.tri +4 -0
- 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
|
+
}
|
|
@@ -33,20 +33,32 @@ export async function _uploadGuidedImageFile(file) {
|
|
|
33
33
|
baseFileName: result.name
|
|
34
34
|
, outputFormat: result.outputFormat
|
|
35
35
|
, singleDensityWidth: parseInt(result.width)
|
|
36
|
+
, quality: parseFloat(result.quality)
|
|
36
37
|
, altText: result.altText
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
let note = this._addPending(file)
|
|
40
41
|
let files = await makeImageSet(file, makeImageData)
|
|
42
|
+
let meta = JSON.parse(files[baseFileName + '.json'])
|
|
41
43
|
|
|
44
|
+
let mainUrl
|
|
42
45
|
for (let fileName of Object.keys(files)) {
|
|
43
|
-
await this._uploadData(fileName, files[fileName])
|
|
46
|
+
let path = await this._uploadData(fileName, files[fileName])
|
|
47
|
+
if(fileName === meta.name + '.' + meta.fallback) {
|
|
48
|
+
mainUrl = path
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
|
|
52
|
+
let base = this._transformRelativeUrlToPublic(mainUrl)
|
|
53
|
+
|
|
54
|
+
let ext = this.getSelectedUrlExtFromMeta(meta)
|
|
55
|
+
base += ext
|
|
56
|
+
|
|
46
57
|
if (note) {
|
|
47
58
|
note.remove()
|
|
48
59
|
}
|
|
49
|
-
|
|
60
|
+
|
|
61
|
+
return base
|
|
50
62
|
}
|
|
51
63
|
}
|
|
52
64
|
|
|
@@ -71,12 +83,13 @@ export async function _uploadGuidedFile(file) {
|
|
|
71
83
|
if (result) {
|
|
72
84
|
let note = this._addPending(file)
|
|
73
85
|
|
|
74
|
-
await this._uploadData(result.name, file)
|
|
86
|
+
let mainUrl = await this._uploadData(result.name, file)
|
|
87
|
+
let base = this._transformRelativeUrlToPublic(mainUrl)
|
|
75
88
|
|
|
76
89
|
if (note) {
|
|
77
90
|
note.remove()
|
|
78
91
|
}
|
|
79
|
-
return
|
|
92
|
+
return base
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
|
|
@@ -89,18 +102,27 @@ export async function _uploadAutomaticImageFile(file) {
|
|
|
89
102
|
baseFileName: baseFileName,
|
|
90
103
|
outputFormat: file.type
|
|
91
104
|
})
|
|
105
|
+
let meta = JSON.parse(files[baseFileName + '.json'])
|
|
92
106
|
|
|
107
|
+
let mainUrl
|
|
93
108
|
for (let fileName of Object.keys(files)) {
|
|
94
|
-
await this._uploadData(fileName, files[fileName])
|
|
109
|
+
let path = await this._uploadData(fileName, files[fileName])
|
|
110
|
+
if(fileName === meta.name + '.' + meta.fallback) {
|
|
111
|
+
mainUrl = path
|
|
112
|
+
}
|
|
95
113
|
}
|
|
114
|
+
let base = this._transformRelativeUrlToPublic(mainUrl)
|
|
115
|
+
let ext = this.getSelectedUrlExtFromMeta(meta)
|
|
116
|
+
base += ext
|
|
96
117
|
|
|
97
118
|
if (note) {
|
|
98
119
|
note.remove()
|
|
99
120
|
}
|
|
100
|
-
return
|
|
121
|
+
return base
|
|
101
122
|
}
|
|
102
123
|
|
|
103
124
|
export async function uploadFiles(files, { uploadType } = {}) {
|
|
125
|
+
let uploadedAccessUrls = []
|
|
104
126
|
for (let file of files) {
|
|
105
127
|
|
|
106
128
|
let uploaded = false
|
|
@@ -115,17 +137,19 @@ export async function uploadFiles(files, { uploadType } = {}) {
|
|
|
115
137
|
}
|
|
116
138
|
else {
|
|
117
139
|
let note = this._addPending(file)
|
|
140
|
+
let path
|
|
118
141
|
if (uploadType === 'automatic') {
|
|
119
142
|
let parts = nameParts(file)
|
|
120
|
-
await this._uploadData(parts.join('.'), file)
|
|
143
|
+
path = await this._uploadData(parts.join('.'), file)
|
|
121
144
|
}
|
|
122
145
|
else {
|
|
123
|
-
await this._uploadData(file.name, file)
|
|
146
|
+
path = await this._uploadData(file.name, file)
|
|
124
147
|
}
|
|
148
|
+
uploaded = this._transformRelativeUrlToPublic(path)
|
|
149
|
+
|
|
125
150
|
if (note) {
|
|
126
151
|
note.remove()
|
|
127
152
|
}
|
|
128
|
-
uploaded = true
|
|
129
153
|
}
|
|
130
154
|
if (this.eventNotificationPanel && uploaded) {
|
|
131
155
|
this.eventNotificationPanel.addNotification({
|
|
@@ -136,8 +160,14 @@ export async function uploadFiles(files, { uploadType } = {}) {
|
|
|
136
160
|
, ttl: 2000
|
|
137
161
|
})
|
|
138
162
|
}
|
|
163
|
+
uploadedAccessUrls.push(uploaded)
|
|
139
164
|
}
|
|
140
165
|
this.setCurrentNode(this.currentNode)
|
|
166
|
+
this.emitter.emit('upload', {
|
|
167
|
+
type: 'upload'
|
|
168
|
+
, accessUrls: uploadedAccessUrls
|
|
169
|
+
})
|
|
170
|
+
return uploadedAccessUrls
|
|
141
171
|
}
|
|
142
172
|
|
|
143
173
|
|
|
@@ -153,7 +183,8 @@ export async function _uploadFile(evt, selected) {
|
|
|
153
183
|
}
|
|
154
184
|
let files = await this._getFilesFromEvent(evt)
|
|
155
185
|
if(files.length > 0) {
|
|
156
|
-
this.uploadFiles(files, { uploadType: 'guided' })
|
|
186
|
+
let result = this.uploadFiles(files, { uploadType: 'guided' })
|
|
157
187
|
input.value = ''
|
|
188
|
+
return result
|
|
158
189
|
}
|
|
159
190
|
}
|
|
@@ -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 */
|