@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.
@@ -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
- let sizes = data.displaySize.split('x')
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
+ }
@@ -2,6 +2,7 @@
2
2
  export async function _uploadData(name, data) {
3
3
  let path = this.currentNode.file.relPath + '/' + this.sanitizeFileName(name)
4
4
  await this.sink.write(path, data)
5
+ return path
5
6
  }
6
7
 
7
8
  export async function findDirectories() {
@@ -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
- return true
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 true
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 true
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 = [...selected.closest('.view-icons').querySelectorAll('button')].map(button => button.getAttribute('data-show-class'))
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(selected.getAttribute('data-show-class'))
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
- let searchString = variant.variant.baseName + variant.variant.extensions.join()
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 = '<ul>'
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
- this.overCount = 0
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webhandle/tree-file-browser",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "",
5
5
  "main": "client-lib/image-browser-view.mjs",
6
6
  "scripts": {
@@ -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 */