@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.
@@ -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() {
@@ -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
- return true
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 true
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 true
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 = [...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.1",
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 */