lexgui 0.7.15 → 8.1.0
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/LICENSE +201 -21
- package/README.md +14 -5
- package/build/components/AlertDialog.d.ts +7 -0
- package/build/components/ArrayInput.d.ts +9 -0
- package/build/components/BaseComponent.d.ts +73 -0
- package/build/components/Button.d.ts +14 -0
- package/build/components/Calendar.d.ts +41 -0
- package/build/components/CalendarRange.d.ts +16 -0
- package/build/components/CanvasCurve.d.ts +10 -0
- package/build/components/CanvasDial.d.ts +11 -0
- package/build/components/CanvasMap2D.d.ts +61 -0
- package/build/components/Card.d.ts +8 -0
- package/build/components/Checkbox.d.ts +8 -0
- package/build/components/Color.d.ts +20 -0
- package/build/components/ColorInput.d.ts +13 -0
- package/build/components/ColorPicker.d.ts +29 -0
- package/build/components/ComboButtons.d.ts +8 -0
- package/build/components/ContextMenu.d.ts +16 -0
- package/build/components/Counter.d.ts +9 -0
- package/build/components/Curve.d.ts +10 -0
- package/build/components/DatePicker.d.ts +13 -0
- package/build/components/Dial.d.ts +10 -0
- package/build/components/Dialog.d.ts +20 -0
- package/build/components/DropdownMenu.d.ts +32 -0
- package/build/components/FileInput.d.ts +8 -0
- package/build/components/Footer.d.ts +14 -0
- package/build/components/Form.d.ts +8 -0
- package/build/components/Layers.d.ts +9 -0
- package/build/components/List.d.ts +9 -0
- package/build/components/Map2D.d.ts +12 -0
- package/build/components/Menubar.d.ts +59 -0
- package/build/components/NodeTree.d.ts +26 -0
- package/build/components/NumberInput.d.ts +9 -0
- package/build/components/OTPInput.d.ts +8 -0
- package/build/components/Pad.d.ts +8 -0
- package/build/components/Pagination.d.ts +26 -0
- package/build/components/PocketDialog.d.ts +11 -0
- package/build/components/Popover.d.ts +20 -0
- package/build/components/Progress.d.ts +8 -0
- package/build/components/RadioGroup.d.ts +8 -0
- package/build/components/RangeInput.d.ts +11 -0
- package/build/components/Rate.d.ts +8 -0
- package/build/components/Select.d.ts +10 -0
- package/build/components/Sheet.d.ts +10 -0
- package/build/components/Sidebar.d.ts +84 -0
- package/build/components/SizeInput.d.ts +8 -0
- package/build/components/Skeleton.d.ts +5 -0
- package/build/components/Spinner.d.ts +9 -0
- package/build/components/TabSections.d.ts +11 -0
- package/build/components/Table.d.ts +34 -0
- package/build/components/Tabs.d.ts +20 -0
- package/build/components/Tags.d.ts +9 -0
- package/build/components/TextArea.d.ts +8 -0
- package/build/components/TextInput.d.ts +11 -0
- package/build/components/Title.d.ts +8 -0
- package/build/components/Toggle.d.ts +8 -0
- package/build/components/Tour.d.ts +36 -0
- package/build/components/Vector.d.ts +9 -0
- package/build/core/Area.d.ts +143 -0
- package/build/core/Branch.d.ts +19 -0
- package/build/core/Core.d.ts +1 -0
- package/build/core/Event.d.ts +26 -0
- package/build/core/Icons.d.ts +4 -0
- package/build/core/Namespace.d.ts +2 -0
- package/build/core/Namespace.js +34 -0
- package/build/core/Namespace.js.map +1 -0
- package/build/core/Panel.d.ts +538 -0
- package/build/core/Utils.d.ts +1 -0
- package/build/core/Vec2.d.ts +21 -0
- package/build/extensions/AssetView.d.ts +136 -0
- package/build/extensions/AssetView.js +1367 -0
- package/build/extensions/AssetView.js.map +1 -0
- package/build/extensions/Audio.d.ts +9 -0
- package/build/extensions/Audio.js +163 -0
- package/build/extensions/Audio.js.map +1 -0
- package/build/extensions/CodeEditor.d.ts +350 -0
- package/build/extensions/CodeEditor.js +5022 -0
- package/build/extensions/CodeEditor.js.map +1 -0
- package/build/extensions/DocMaker.d.ts +27 -0
- package/build/extensions/DocMaker.js +327 -0
- package/build/extensions/DocMaker.js.map +1 -0
- package/build/extensions/GraphEditor.d.ts +276 -0
- package/build/extensions/GraphEditor.js +2770 -0
- package/build/extensions/GraphEditor.js.map +1 -0
- package/build/extensions/ImUi.d.ts +46 -0
- package/build/extensions/ImUi.js +227 -0
- package/build/extensions/ImUi.js.map +1 -0
- package/build/extensions/Timeline.d.ts +670 -0
- package/build/extensions/Timeline.js +3955 -0
- package/build/extensions/Timeline.js.map +1 -0
- package/build/extensions/VideoEditor.d.ts +128 -0
- package/build/extensions/VideoEditor.js +898 -0
- package/build/extensions/VideoEditor.js.map +1 -0
- package/build/extensions/index.d.ts +8 -0
- package/build/extensions/index.js +10 -0
- package/build/extensions/index.js.map +1 -0
- package/build/index.all.d.ts +2 -0
- package/build/index.css.d.ts +4 -0
- package/build/index.d.ts +56 -0
- package/build/lexgui.all.js +28498 -0
- package/build/lexgui.all.js.map +1 -0
- package/build/lexgui.all.min.js +1 -0
- package/build/lexgui.all.module.js +28422 -0
- package/build/lexgui.all.module.js.map +1 -0
- package/build/lexgui.all.module.min.js +1 -0
- package/build/lexgui.css +939 -346
- package/build/lexgui.js +13406 -17286
- package/build/lexgui.js.map +1 -0
- package/build/lexgui.min.css +3 -10
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +12762 -16698
- package/build/lexgui.module.js.map +1 -0
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +170 -74
- package/demo.js +162 -48
- package/examples/all-components.html +45 -14
- package/examples/asset-view.html +110 -47
- package/examples/code-editor.html +5 -5
- package/examples/dialogs.html +3 -3
- package/examples/editor.html +27 -13
- package/examples/index.html +19 -14
- package/examples/node-graph.html +2 -2
- package/examples/previews/video-editor.png +0 -0
- package/examples/timeline.html +1 -1
- package/examples/video-editor.html +2 -2
- package/package.json +25 -9
- package/build/extensions/audio.js +0 -212
- package/build/extensions/codeeditor.js +0 -6319
- package/build/extensions/docmaker.js +0 -432
- package/build/extensions/imui.js +0 -325
- package/build/extensions/nodegraph.js +0 -3696
- package/build/extensions/timeline.js +0 -4636
- package/build/extensions/videoeditor.js +0 -953
- package/build/lexgui-docs.css +0 -352
|
@@ -0,0 +1,1367 @@
|
|
|
1
|
+
// This is a generated file. Do not edit.
|
|
2
|
+
import { LX } from '../core/Namespace.js';
|
|
3
|
+
|
|
4
|
+
// AssetView.ts @jxarco
|
|
5
|
+
if (!LX) {
|
|
6
|
+
throw ('Missing LX namespace!');
|
|
7
|
+
}
|
|
8
|
+
LX.extensions.push('AssetView');
|
|
9
|
+
const Area = LX.Area;
|
|
10
|
+
LX.Panel;
|
|
11
|
+
LX.NodeTree;
|
|
12
|
+
LX.TreeEvent;
|
|
13
|
+
/**
|
|
14
|
+
* @class AssetView
|
|
15
|
+
* @description Asset container with Tree for file system
|
|
16
|
+
*/
|
|
17
|
+
class AssetView {
|
|
18
|
+
static LAYOUT_GRID = 0;
|
|
19
|
+
static LAYOUT_COMPACT = 1;
|
|
20
|
+
static LAYOUT_LIST = 2;
|
|
21
|
+
static CONTENT_SORT_ASC = 0;
|
|
22
|
+
static CONTENT_SORT_DESC = 1;
|
|
23
|
+
root;
|
|
24
|
+
area = null;
|
|
25
|
+
content; // "!" to avoid TS strict property initialization error
|
|
26
|
+
leftPanel = null;
|
|
27
|
+
toolsPanel;
|
|
28
|
+
contentPanel;
|
|
29
|
+
previewPanel;
|
|
30
|
+
tree = null;
|
|
31
|
+
prevData = [];
|
|
32
|
+
nextData = [];
|
|
33
|
+
data = [];
|
|
34
|
+
currentData = [];
|
|
35
|
+
currentFolder = undefined;
|
|
36
|
+
rootItem;
|
|
37
|
+
path = [];
|
|
38
|
+
rootPath = '';
|
|
39
|
+
selectedItem = undefined;
|
|
40
|
+
allowedTypes;
|
|
41
|
+
searchValue = '';
|
|
42
|
+
filter = 'None';
|
|
43
|
+
gridScale = 1.0;
|
|
44
|
+
// Options
|
|
45
|
+
layout = AssetView.LAYOUT_GRID;
|
|
46
|
+
sortMode = AssetView.CONTENT_SORT_ASC;
|
|
47
|
+
skipBrowser = false;
|
|
48
|
+
skipPreview = false;
|
|
49
|
+
useNativeTitle = false;
|
|
50
|
+
onlyFolders = true;
|
|
51
|
+
allowMultipleSelection = false;
|
|
52
|
+
previewActions = [];
|
|
53
|
+
contextMenu = [];
|
|
54
|
+
onRefreshContent = null;
|
|
55
|
+
itemContextMenuOptions = null;
|
|
56
|
+
onItemDragged = null;
|
|
57
|
+
_assetsPerPage = 24;
|
|
58
|
+
get assetsPerPage() {
|
|
59
|
+
return this._assetsPerPage;
|
|
60
|
+
}
|
|
61
|
+
set assetsPerPage(v) {
|
|
62
|
+
this._setAssetsPerPage(v);
|
|
63
|
+
}
|
|
64
|
+
_callbacks = {};
|
|
65
|
+
_lastSortBy = '';
|
|
66
|
+
_paginator;
|
|
67
|
+
_scriptCodeDialog;
|
|
68
|
+
_moveItemDialog;
|
|
69
|
+
_movingItem;
|
|
70
|
+
constructor(options = {}) {
|
|
71
|
+
this.rootPath = 'https://raw.githubusercontent.com/jxarco/lexgui.js/master/';
|
|
72
|
+
this.layout = options.layout ?? this.layout;
|
|
73
|
+
this.sortMode = options.sortMode ?? this.sortMode;
|
|
74
|
+
if (options.rootPath) {
|
|
75
|
+
if (options.rootPath.constructor !== String) {
|
|
76
|
+
console.warn(`Asset Root Path must be a String (now is a ${options.rootPath.constructor.name})`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
this.rootPath = options.rootPath;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
let div = document.createElement('div');
|
|
83
|
+
div.className = 'lexassetbrowser';
|
|
84
|
+
this.root = div;
|
|
85
|
+
let area = new Area({ width: '100%', height: '100%' });
|
|
86
|
+
div.appendChild(area.root);
|
|
87
|
+
let left, right, contentArea = area;
|
|
88
|
+
this.skipBrowser = options.skipBrowser ?? this.skipBrowser;
|
|
89
|
+
this.skipPreview = options.skipPreview ?? this.skipPreview;
|
|
90
|
+
this.useNativeTitle = options.useNativeTitle ?? this.useNativeTitle;
|
|
91
|
+
this.onlyFolders = options.onlyFolders ?? this.onlyFolders;
|
|
92
|
+
this.allowMultipleSelection = options.allowMultipleSelection ?? this.allowMultipleSelection;
|
|
93
|
+
this.previewActions = options.previewActions ?? [];
|
|
94
|
+
this.itemContextMenuOptions = options.itemContextMenuOptions;
|
|
95
|
+
this.onRefreshContent = options.onRefreshContent;
|
|
96
|
+
this.onItemDragged = options.onItemDragged;
|
|
97
|
+
this.gridScale = options.gridScale ?? this.gridScale;
|
|
98
|
+
if (this.gridScale !== 1.0) {
|
|
99
|
+
const r = document.querySelector(':root');
|
|
100
|
+
r.style.setProperty('--av-grid-scale', this.gridScale);
|
|
101
|
+
}
|
|
102
|
+
// Append temporarily to the dom
|
|
103
|
+
document.body.appendChild(this.root);
|
|
104
|
+
if (!this.skipBrowser) {
|
|
105
|
+
[left, right] = area.split({ type: 'horizontal', sizes: ['15%', '85%'] });
|
|
106
|
+
contentArea = right;
|
|
107
|
+
left.setLimitBox(210, 0);
|
|
108
|
+
right.setLimitBox(512, 0);
|
|
109
|
+
}
|
|
110
|
+
if (!this.skipPreview) {
|
|
111
|
+
[contentArea, right] = contentArea.split({ type: 'horizontal', sizes: ['80%', '20%'] });
|
|
112
|
+
}
|
|
113
|
+
this.allowedTypes = {
|
|
114
|
+
'None': {},
|
|
115
|
+
'Image': { color: 'yellow-500' },
|
|
116
|
+
'JSON': { color: 'sky-200' },
|
|
117
|
+
'Video': { color: 'indigo-400' },
|
|
118
|
+
...(options.allowedTypes ?? {})
|
|
119
|
+
};
|
|
120
|
+
this.path = ['@'];
|
|
121
|
+
this.rootItem = { id: '/', children: this.data, type: 'folder', metadata: { uid: LX.guidGenerator() } };
|
|
122
|
+
this.currentFolder = this.rootItem;
|
|
123
|
+
this._processData(this.data);
|
|
124
|
+
this.currentData = this.data;
|
|
125
|
+
if (!this.skipBrowser) {
|
|
126
|
+
this._createTreePanel(left);
|
|
127
|
+
}
|
|
128
|
+
this._createContentPanel(contentArea);
|
|
129
|
+
// Create resource preview panel
|
|
130
|
+
if (!this.skipPreview) {
|
|
131
|
+
this.previewPanel = right.addPanel({ className: 'lexassetcontentpanel', style: { overflow: 'scroll' } });
|
|
132
|
+
}
|
|
133
|
+
// Clean up
|
|
134
|
+
document.body.removeChild(this.root);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* @method on
|
|
138
|
+
* @description Stores an event callback for the desired action
|
|
139
|
+
*/
|
|
140
|
+
on(eventName, callback) {
|
|
141
|
+
this._callbacks[eventName] = callback;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* @method load
|
|
145
|
+
* @description Loads and processes the input data
|
|
146
|
+
*/
|
|
147
|
+
load(data) {
|
|
148
|
+
this.prevData.length = 0;
|
|
149
|
+
this.nextData.length = 0;
|
|
150
|
+
this.data = data;
|
|
151
|
+
// Update root children
|
|
152
|
+
this.rootItem.children = this.data;
|
|
153
|
+
this._processData(this.data);
|
|
154
|
+
this.currentData = this.data;
|
|
155
|
+
this.path = ['@'];
|
|
156
|
+
if (!this.skipBrowser) {
|
|
157
|
+
this.tree.refresh({ id: '/', children: this.data, type: 'folder',
|
|
158
|
+
metadata: { uid: LX.guidGenerator() } });
|
|
159
|
+
}
|
|
160
|
+
this._refreshContent();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @method addItem
|
|
164
|
+
* @description Creates an item DOM element
|
|
165
|
+
*/
|
|
166
|
+
addItem(item, childIndex, updateTree = true) {
|
|
167
|
+
const isListLayout = this.layout == AssetView.LAYOUT_LIST;
|
|
168
|
+
const isGridLayout = this.layout == AssetView.LAYOUT_GRID; // default
|
|
169
|
+
const type = item.type.charAt(0).toUpperCase() + item.type.slice(1);
|
|
170
|
+
const extension = LX.getExtension(item.id);
|
|
171
|
+
const isFolder = type === 'Folder';
|
|
172
|
+
const that = this;
|
|
173
|
+
let itemEl = document.createElement('li');
|
|
174
|
+
itemEl.className = 'lexassetitem ' + item.type.toLowerCase();
|
|
175
|
+
itemEl.tabIndex = -1;
|
|
176
|
+
LX.insertChildAtIndex(this.content, itemEl, childIndex);
|
|
177
|
+
const typeColor = this.allowedTypes[type]?.color;
|
|
178
|
+
if (typeColor) {
|
|
179
|
+
// Add type tag
|
|
180
|
+
LX.makeElement('span', `rounded-full w-2 h-2 z-100 flex absolute ml-2 mt-2 bg-${typeColor}`, '', itemEl);
|
|
181
|
+
}
|
|
182
|
+
const metadata = item.metadata;
|
|
183
|
+
if (!metadata.uid) {
|
|
184
|
+
metadata.uid = LX.guidGenerator();
|
|
185
|
+
}
|
|
186
|
+
if (metadata.lastModified && !metadata.lastModifiedDate) {
|
|
187
|
+
metadata.lastModifiedDate = this._lastModifiedToStringDate(metadata.lastModified);
|
|
188
|
+
}
|
|
189
|
+
if (!this.useNativeTitle) {
|
|
190
|
+
let desc = document.createElement('span');
|
|
191
|
+
desc.className = 'lexitemdesc';
|
|
192
|
+
desc.id = `floatingTitle_${metadata.uid}`;
|
|
193
|
+
desc.innerHTML = `File: ${item.id}<br>Type: ${type}`;
|
|
194
|
+
LX.insertChildAtIndex(this.content, desc, childIndex ? childIndex + 1 : undefined);
|
|
195
|
+
itemEl.addEventListener('mousemove', (e) => {
|
|
196
|
+
if (!isGridLayout) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const target = e.target;
|
|
200
|
+
const dialog = itemEl.closest('dialog');
|
|
201
|
+
const rect = itemEl.getBoundingClientRect();
|
|
202
|
+
const targetRect = target.getBoundingClientRect();
|
|
203
|
+
let localOffsetX = rect.x + e.offsetX;
|
|
204
|
+
let localOffsetY = rect.y + e.offsetY;
|
|
205
|
+
if (dialog) {
|
|
206
|
+
const dialogRect = dialog.getBoundingClientRect();
|
|
207
|
+
localOffsetX -= dialogRect.x;
|
|
208
|
+
localOffsetY -= dialogRect.y;
|
|
209
|
+
}
|
|
210
|
+
if (target.classList.contains('lexassettitle')) {
|
|
211
|
+
localOffsetY += targetRect.y - rect.y;
|
|
212
|
+
}
|
|
213
|
+
desc.style.left = localOffsetX + 'px';
|
|
214
|
+
desc.style.top = (localOffsetY - 36) + 'px';
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
itemEl.title = type + ': ' + item.id;
|
|
219
|
+
}
|
|
220
|
+
if (this.allowMultipleSelection) {
|
|
221
|
+
let checkbox = document.createElement('input');
|
|
222
|
+
checkbox.type = 'checkbox';
|
|
223
|
+
checkbox.className = 'lexcheckbox';
|
|
224
|
+
checkbox.checked = metadata.selected;
|
|
225
|
+
checkbox.addEventListener('change', (e) => {
|
|
226
|
+
metadata.selected = !metadata.selected;
|
|
227
|
+
const onCheck = that._callbacks['check'];
|
|
228
|
+
if (onCheck !== undefined) {
|
|
229
|
+
const event = {
|
|
230
|
+
type: 'check',
|
|
231
|
+
items: [item],
|
|
232
|
+
userInitiated: true
|
|
233
|
+
};
|
|
234
|
+
onCheck(event);
|
|
235
|
+
// event.multiple = !!e.shiftKey;
|
|
236
|
+
}
|
|
237
|
+
e.stopPropagation();
|
|
238
|
+
e.stopImmediatePropagation();
|
|
239
|
+
});
|
|
240
|
+
itemEl.appendChild(checkbox);
|
|
241
|
+
}
|
|
242
|
+
let title = document.createElement('span');
|
|
243
|
+
title.className = 'lexassettitle';
|
|
244
|
+
title.innerText = item.id;
|
|
245
|
+
itemEl.appendChild(title);
|
|
246
|
+
if (!this.skipPreview) {
|
|
247
|
+
if (item.type === 'video') {
|
|
248
|
+
const itemVideo = LX.makeElement('video', 'absolute left-0 top-0 w-full border-none pointer-events-none', '', itemEl);
|
|
249
|
+
itemVideo.setAttribute('disablePictureInPicture', false);
|
|
250
|
+
itemVideo.setAttribute('disableRemotePlayback', false);
|
|
251
|
+
itemVideo.setAttribute('loop', true);
|
|
252
|
+
itemVideo.setAttribute('async', true);
|
|
253
|
+
itemVideo.style.transition = 'opacity 0.2s ease-out';
|
|
254
|
+
itemVideo.style.opacity = metadata.preview ? '0' : '1';
|
|
255
|
+
itemVideo.src = item.src;
|
|
256
|
+
itemVideo.volume = metadata.volume ?? 0.4;
|
|
257
|
+
}
|
|
258
|
+
let preview = null;
|
|
259
|
+
const previewSrc = metadata.preview ?? item.src;
|
|
260
|
+
const hasImage = previewSrc && ((() => {
|
|
261
|
+
const ext = LX.getExtension(previewSrc.split('?')[0].split('#')[0]); // get final source without url parameters/anchors
|
|
262
|
+
return ext ? ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'avif'].includes(ext.toLowerCase()) : false;
|
|
263
|
+
})()
|
|
264
|
+
|| previewSrc.startsWith('data:image/'));
|
|
265
|
+
if (hasImage || isFolder || !isGridLayout) {
|
|
266
|
+
const defaultPreviewPath = `${this.rootPath}images/file.png`;
|
|
267
|
+
const defaultFolderPath = `${this.rootPath}images/folder.png`;
|
|
268
|
+
preview = document.createElement('img');
|
|
269
|
+
let realSrc = metadata.unknownExtension
|
|
270
|
+
? defaultPreviewPath
|
|
271
|
+
: (isFolder ? defaultFolderPath : previewSrc);
|
|
272
|
+
preview.src = isGridLayout || isFolder ? realSrc : defaultPreviewPath;
|
|
273
|
+
preview.setAttribute('draggable', 'false');
|
|
274
|
+
preview.className = 'pointer-events-none';
|
|
275
|
+
itemEl.appendChild(preview);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
preview = document.createElement('svg');
|
|
279
|
+
preview.className = 'asset-file-preview';
|
|
280
|
+
itemEl.appendChild(preview);
|
|
281
|
+
let textEl = document.createElement('text');
|
|
282
|
+
textEl.innerText = (!extension || extension == item.id)
|
|
283
|
+
? item.type.toUpperCase()
|
|
284
|
+
: (`${extension.toUpperCase()}`); // If no extension, e.g. Clip, use the type...
|
|
285
|
+
preview.appendChild(textEl);
|
|
286
|
+
var newLength = textEl.innerText.length;
|
|
287
|
+
var charsPerLine = 2.5;
|
|
288
|
+
var newEmSize = charsPerLine / newLength;
|
|
289
|
+
var textBaseSize = 64;
|
|
290
|
+
if (newEmSize < 1) {
|
|
291
|
+
var newFontSize = newEmSize * textBaseSize;
|
|
292
|
+
textEl.style.fontSize = newFontSize + 'px';
|
|
293
|
+
preview.style.paddingTop = `calc(50% - ${(textEl.offsetHeight * 0.5 + 10)}px)`;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Add item type info
|
|
298
|
+
let itemInfoHtml = type;
|
|
299
|
+
if (isListLayout) {
|
|
300
|
+
if (metadata.bytesize)
|
|
301
|
+
itemInfoHtml += ` | ${LX.formatBytes(metadata.bytesize)}`;
|
|
302
|
+
if (metadata.lastModifiedDate)
|
|
303
|
+
itemInfoHtml += ` | ${metadata.lastModifiedDate}`;
|
|
304
|
+
}
|
|
305
|
+
LX.makeContainer(['auto', 'auto'], 'lexassetinfo', itemInfoHtml, itemEl);
|
|
306
|
+
itemEl.addEventListener('click', function (e) {
|
|
307
|
+
e.stopImmediatePropagation();
|
|
308
|
+
e.stopPropagation();
|
|
309
|
+
const isDoubleClick = e.detail == LX.MOUSE_DOUBLE_CLICK;
|
|
310
|
+
if (!isDoubleClick) {
|
|
311
|
+
if (!e.shiftKey) {
|
|
312
|
+
that.content.querySelectorAll('.lexassetitem').forEach((i) => i.classList.remove('selected'));
|
|
313
|
+
}
|
|
314
|
+
this.classList.add('selected');
|
|
315
|
+
that.selectedItem = item;
|
|
316
|
+
if (!that.skipPreview) {
|
|
317
|
+
that._previewAsset(item);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else if (isFolder) {
|
|
321
|
+
that._enterFolder(item);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const onSelect = that._callbacks['select'];
|
|
325
|
+
const onDblClick = that._callbacks['dblClick'];
|
|
326
|
+
if (isDoubleClick && onDblClick !== undefined) {
|
|
327
|
+
const event = {
|
|
328
|
+
type: 'dbl_click',
|
|
329
|
+
items: [item],
|
|
330
|
+
userInitiated: true
|
|
331
|
+
};
|
|
332
|
+
onDblClick(event);
|
|
333
|
+
// event.multiple = !!e.shiftKey;
|
|
334
|
+
}
|
|
335
|
+
else if (!isDoubleClick && onSelect !== undefined) {
|
|
336
|
+
const event = {
|
|
337
|
+
type: 'select',
|
|
338
|
+
items: [item],
|
|
339
|
+
userInitiated: true
|
|
340
|
+
};
|
|
341
|
+
onSelect(event);
|
|
342
|
+
// event.multiple = !!e.shiftKey;
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
itemEl.addEventListener('contextmenu', function (e) {
|
|
346
|
+
e.preventDefault();
|
|
347
|
+
e.stopImmediatePropagation();
|
|
348
|
+
e.stopPropagation();
|
|
349
|
+
const multiple = that.content.querySelectorAll('.selected').length;
|
|
350
|
+
const options = [
|
|
351
|
+
{
|
|
352
|
+
name: (multiple > 1) ? (multiple + ' selected') : item.id,
|
|
353
|
+
icon: LX.makeIcon('CircleSmall', { svgClass: `fill-current fg-${typeColor}` }),
|
|
354
|
+
className: 'text-sm',
|
|
355
|
+
disabled: true
|
|
356
|
+
},
|
|
357
|
+
null
|
|
358
|
+
];
|
|
359
|
+
if (multiple <= 1) {
|
|
360
|
+
options.push({ name: 'Rename', icon: 'TextCursor',
|
|
361
|
+
callback: that._renameItemPopover.bind(that, item) });
|
|
362
|
+
}
|
|
363
|
+
if (!isFolder) {
|
|
364
|
+
options.push({ name: 'Clone', icon: 'Copy', callback: that._requestCloneItem.bind(that, item) });
|
|
365
|
+
}
|
|
366
|
+
options.push({ name: 'Move', icon: 'FolderInput', callback: () => that._moveItem(item) });
|
|
367
|
+
if (type == 'Script' && LX.has('CodeEditor')) {
|
|
368
|
+
options.push({ name: 'Open in Editor', icon: 'Code',
|
|
369
|
+
callback: that._openScriptInEditor.bind(that, item) });
|
|
370
|
+
}
|
|
371
|
+
if (that.itemContextMenuOptions) {
|
|
372
|
+
options.push(null);
|
|
373
|
+
for (let o of that.itemContextMenuOptions) {
|
|
374
|
+
if (!o.name || !o.callback)
|
|
375
|
+
continue;
|
|
376
|
+
options.push({ name: o.name, icon: o.icon, callback: o.callback?.bind(that, item) });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
options.push(null, { name: 'Delete', icon: 'Trash2', className: 'fg-error',
|
|
380
|
+
callback: that._requestDeleteItem.bind(that, item) });
|
|
381
|
+
LX.addClass(that.contentPanel.root, 'pointer-events-none');
|
|
382
|
+
LX.addDropdownMenu(e.target, options, { side: 'right', align: 'start', event: e, onBlur: () => {
|
|
383
|
+
LX.removeClass(that.contentPanel.root, 'pointer-events-none');
|
|
384
|
+
} });
|
|
385
|
+
});
|
|
386
|
+
const onDrop = function (src, target) {
|
|
387
|
+
const targetType = target.type.charAt(0).toUpperCase() + target.type.slice(1);
|
|
388
|
+
if (!(targetType === 'Folder') || (src.metadata.uid == target.metadata.uid)) {
|
|
389
|
+
console.error('[AssetView Error] Cannot drop: Target item is not a folder or target is the dragged element!');
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
// Animate dragged element
|
|
393
|
+
const draggedEl = src.domEl;
|
|
394
|
+
if (draggedEl) {
|
|
395
|
+
draggedEl.classList.add('moving-to-folder');
|
|
396
|
+
// When animation ends, finalize move
|
|
397
|
+
draggedEl.addEventListener('animationend', () => {
|
|
398
|
+
draggedEl.classList.remove('moving-to-folder');
|
|
399
|
+
that._requestMoveItemToFolder(src, target);
|
|
400
|
+
}, { once: true });
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
itemEl.addEventListener('dragstart', (e) => {
|
|
404
|
+
window.__av_item_dragged = item;
|
|
405
|
+
var img = new Image();
|
|
406
|
+
img.src = '';
|
|
407
|
+
if (e.dataTransfer) {
|
|
408
|
+
e.dataTransfer.setDragImage(img, 0, 0);
|
|
409
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
410
|
+
}
|
|
411
|
+
const desc = that.content.querySelector(`#floatingTitle_${metadata.uid}`);
|
|
412
|
+
if (desc)
|
|
413
|
+
desc.style.display = 'none';
|
|
414
|
+
}, false);
|
|
415
|
+
itemEl.addEventListener('dragend', (e) => {
|
|
416
|
+
e.preventDefault(); // Prevent default action (open as link for some elements)
|
|
417
|
+
let dragged = window.__av_item_dragged;
|
|
418
|
+
if (dragged && dragged._nodeTarget) { // We dropped into a NodeTree element
|
|
419
|
+
onDrop(dragged, dragged._nodeTarget);
|
|
420
|
+
}
|
|
421
|
+
delete window.__av_item_dragged;
|
|
422
|
+
}, false);
|
|
423
|
+
itemEl.addEventListener('dragenter', (e) => {
|
|
424
|
+
e.preventDefault(); // Prevent default action (open as link for some elements)
|
|
425
|
+
let dragged = window.__av_item_dragged;
|
|
426
|
+
if (!dragged || !isFolder || (dragged.metadata.uid == metadata.uid))
|
|
427
|
+
return;
|
|
428
|
+
LX.addClass(item.domEl, 'animate-pulse');
|
|
429
|
+
});
|
|
430
|
+
itemEl.addEventListener('dragleave', (e) => {
|
|
431
|
+
e.preventDefault(); // Prevent default action (open as link for some elements)
|
|
432
|
+
let dragged = window.__av_item_dragged;
|
|
433
|
+
if (!dragged) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
LX.removeClass(item.domEl, 'animate-pulse');
|
|
437
|
+
});
|
|
438
|
+
itemEl.addEventListener('drop', (e) => {
|
|
439
|
+
e.preventDefault(); // Prevent default action (open as link for some elements)
|
|
440
|
+
let dragged = window.__av_item_dragged;
|
|
441
|
+
if (dragged)
|
|
442
|
+
onDrop(dragged, item);
|
|
443
|
+
});
|
|
444
|
+
itemEl.addEventListener('mouseenter', (e) => {
|
|
445
|
+
if (!that.useNativeTitle && isGridLayout) {
|
|
446
|
+
const desc = that.content.querySelector(`#floatingTitle_${metadata.uid}`);
|
|
447
|
+
if (desc)
|
|
448
|
+
desc.style.display = 'unset';
|
|
449
|
+
}
|
|
450
|
+
if (item.type !== 'video')
|
|
451
|
+
return;
|
|
452
|
+
e.preventDefault();
|
|
453
|
+
const video = itemEl.querySelector('video');
|
|
454
|
+
video.style.opacity = '1';
|
|
455
|
+
video.play();
|
|
456
|
+
});
|
|
457
|
+
itemEl.addEventListener('mouseleave', (e) => {
|
|
458
|
+
if (!that.useNativeTitle && isGridLayout) {
|
|
459
|
+
setTimeout(() => {
|
|
460
|
+
const desc = that.content.querySelector(`#floatingTitle_${metadata.uid}`);
|
|
461
|
+
if (desc)
|
|
462
|
+
desc.style.display = 'none';
|
|
463
|
+
}, 100);
|
|
464
|
+
}
|
|
465
|
+
if (item.type !== 'video')
|
|
466
|
+
return;
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
const video = itemEl.querySelector('video');
|
|
469
|
+
video.pause();
|
|
470
|
+
video.currentTime = 0;
|
|
471
|
+
if (metadata.preview) {
|
|
472
|
+
video.style.opacity = '0';
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
if (!this.skipBrowser && updateTree) {
|
|
476
|
+
this.tree.refresh();
|
|
477
|
+
}
|
|
478
|
+
return itemEl;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* @method clear
|
|
482
|
+
* @description Creates all AssetView container panels
|
|
483
|
+
*/
|
|
484
|
+
clear() {
|
|
485
|
+
if (this.previewPanel) {
|
|
486
|
+
this.previewPanel.clear();
|
|
487
|
+
}
|
|
488
|
+
if (this.leftPanel) {
|
|
489
|
+
this.leftPanel.clear();
|
|
490
|
+
}
|
|
491
|
+
if (this.toolsPanel) {
|
|
492
|
+
this.toolsPanel.clear();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
_processData(data, parent) {
|
|
496
|
+
// Processing an item
|
|
497
|
+
if (data.constructor !== Array) {
|
|
498
|
+
data.parent = parent;
|
|
499
|
+
data.dir = parent?.children;
|
|
500
|
+
data.children = data.children ?? [];
|
|
501
|
+
data.metadata = data.metadata || {};
|
|
502
|
+
}
|
|
503
|
+
// Get the new parent
|
|
504
|
+
const newParent = parent ? data : this.rootItem;
|
|
505
|
+
for (let item of newParent.children) {
|
|
506
|
+
this._processData(item, newParent);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
_updatePath() {
|
|
510
|
+
this.path.length = 0;
|
|
511
|
+
if (this.currentFolder && this.currentFolder.parent) {
|
|
512
|
+
this.path.push(this.currentFolder.id);
|
|
513
|
+
const _pushParentsId = (i) => {
|
|
514
|
+
if (!i)
|
|
515
|
+
return;
|
|
516
|
+
this.path.push(i.parent ? i.id : '@');
|
|
517
|
+
_pushParentsId(i.parent);
|
|
518
|
+
};
|
|
519
|
+
_pushParentsId(this.currentFolder.parent);
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
this.path.push('@');
|
|
523
|
+
}
|
|
524
|
+
LX.emitSignal('@on_folder_change', this.path.reverse().join('/'));
|
|
525
|
+
}
|
|
526
|
+
_createNavigationBar(panel) {
|
|
527
|
+
panel.sameLine(4, 'justify-center');
|
|
528
|
+
panel.addButton(null, 'GoBackButton', () => {
|
|
529
|
+
if (!this.prevData.length || !this.currentFolder)
|
|
530
|
+
return;
|
|
531
|
+
this.nextData.push(this.currentFolder);
|
|
532
|
+
this._enterFolder(this.prevData.pop(), false);
|
|
533
|
+
}, { buttonClass: 'bg-none', title: 'Go Back', tooltip: true, icon: 'ArrowLeft' });
|
|
534
|
+
panel.addButton(null, 'GoForwardButton', () => {
|
|
535
|
+
if (!this.nextData.length || !this.currentFolder)
|
|
536
|
+
return;
|
|
537
|
+
this._enterFolder(this.nextData.pop());
|
|
538
|
+
}, { buttonClass: 'bg-none', title: 'Go Forward', tooltip: true, icon: 'ArrowRight' });
|
|
539
|
+
panel.addButton(null, 'GoUpButton', () => {
|
|
540
|
+
const parentFolder = this.currentFolder?.parent;
|
|
541
|
+
if (parentFolder)
|
|
542
|
+
this._enterFolder(parentFolder);
|
|
543
|
+
}, { buttonClass: 'bg-none', title: 'Go Upper Folder', tooltip: true, icon: 'ArrowUp' });
|
|
544
|
+
panel.addButton(null, 'GoUpButton', () => {
|
|
545
|
+
this._refreshContent();
|
|
546
|
+
}, { buttonClass: 'bg-none', title: 'Refresh', tooltip: true, icon: 'Refresh' });
|
|
547
|
+
}
|
|
548
|
+
_createTreePanel(area) {
|
|
549
|
+
if (this.leftPanel) {
|
|
550
|
+
this.leftPanel.clear();
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
this.leftPanel = area.addPanel({ className: 'lexassetbrowserpanel' });
|
|
554
|
+
}
|
|
555
|
+
this._createNavigationBar(this.leftPanel);
|
|
556
|
+
const treeData = { id: '/', children: this.data };
|
|
557
|
+
const tree = this.leftPanel.addTree('Content Browser', treeData, {
|
|
558
|
+
// icons: tree_icons,
|
|
559
|
+
filter: false,
|
|
560
|
+
onlyFolders: this.onlyFolders,
|
|
561
|
+
onevent: (event) => {
|
|
562
|
+
let node = event.node;
|
|
563
|
+
let value = event.value;
|
|
564
|
+
switch (event.type) {
|
|
565
|
+
case LX.TreeEvent.NODE_SELECTED:
|
|
566
|
+
{
|
|
567
|
+
if (event.multiple) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (!node.parent) {
|
|
571
|
+
if (this.currentFolder) {
|
|
572
|
+
this.prevData.push(this.currentFolder);
|
|
573
|
+
}
|
|
574
|
+
this.currentFolder = undefined;
|
|
575
|
+
this.currentData = this.data;
|
|
576
|
+
this._refreshContent();
|
|
577
|
+
this._updatePath();
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
this._enterFolder(node.type === 'folder' ? node : node.parent);
|
|
581
|
+
this._previewAsset(node);
|
|
582
|
+
if (node.type !== 'folder') {
|
|
583
|
+
this.content.querySelectorAll('.lexassetitem').forEach((i) => i.classList.remove('selected'));
|
|
584
|
+
const dom = node.domEl;
|
|
585
|
+
dom?.classList.add('selected');
|
|
586
|
+
}
|
|
587
|
+
this.selectedItem = node;
|
|
588
|
+
}
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
case LX.TreeEvent.NODE_DRAGGED:
|
|
592
|
+
{
|
|
593
|
+
if (node.parent) {
|
|
594
|
+
const idx = node.parent.children.indexOf(node);
|
|
595
|
+
node.parent.children.splice(idx, 1);
|
|
596
|
+
}
|
|
597
|
+
if (!value.children) {
|
|
598
|
+
value.children = [];
|
|
599
|
+
}
|
|
600
|
+
value.children.push(node);
|
|
601
|
+
node.parent = value;
|
|
602
|
+
node.dir = value.children;
|
|
603
|
+
if (this.onItemDragged) {
|
|
604
|
+
this.onItemDragged(node, value);
|
|
605
|
+
}
|
|
606
|
+
this._refreshContent();
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
this.tree = tree.innerTree;
|
|
613
|
+
}
|
|
614
|
+
_setContentLayout(layoutMode) {
|
|
615
|
+
this.layout = layoutMode;
|
|
616
|
+
this.toolsPanel.refresh();
|
|
617
|
+
this._refreshContent();
|
|
618
|
+
}
|
|
619
|
+
_createContentPanel(area) {
|
|
620
|
+
const that = this;
|
|
621
|
+
area.root.classList.add('flex', 'flex-col');
|
|
622
|
+
if (this.toolsPanel) {
|
|
623
|
+
this.contentPanel.clear();
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
this.toolsPanel = area.addPanel({ className: 'flex-auto', height: 'auto' });
|
|
627
|
+
this.contentPanel = area.addPanel({
|
|
628
|
+
className: 'lexassetcontentpanel flex flex-col flex-auto-fill content-center overflow-hidden'
|
|
629
|
+
});
|
|
630
|
+
this._paginator = new LX.Pagination({
|
|
631
|
+
className: 'ml-auto',
|
|
632
|
+
pages: Math.max(Math.ceil(this.data.length / this.assetsPerPage), 1),
|
|
633
|
+
onChange: () => this._refreshContent()
|
|
634
|
+
});
|
|
635
|
+
this.contentPanel.root.addEventListener('wheel', (e) => {
|
|
636
|
+
if (!e.ctrlKey)
|
|
637
|
+
return;
|
|
638
|
+
e.preventDefault();
|
|
639
|
+
this.gridScale *= e.deltaY < 0 ? 1.05 : 0.95;
|
|
640
|
+
this.gridScale = LX.clamp(this.gridScale, 0.5, 2.0);
|
|
641
|
+
const r = document.querySelector(':root');
|
|
642
|
+
r.style.setProperty('--av-grid-scale', this.gridScale);
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const _onSort = (value, event) => {
|
|
646
|
+
LX.addDropdownMenu(event.target, [
|
|
647
|
+
{ name: 'Name', icon: 'ALargeSmall', callback: () => this._sortData('id') },
|
|
648
|
+
{ name: 'Type', icon: 'Type', callback: () => this._sortData('type') },
|
|
649
|
+
null,
|
|
650
|
+
{ name: 'Ascending', icon: 'SortAsc',
|
|
651
|
+
callback: () => this._sortData(undefined, AssetView.CONTENT_SORT_ASC) },
|
|
652
|
+
{ name: 'Descending', icon: 'SortDesc',
|
|
653
|
+
callback: () => this._sortData(undefined, AssetView.CONTENT_SORT_DESC) }
|
|
654
|
+
], { side: 'bottom', align: 'start' });
|
|
655
|
+
};
|
|
656
|
+
const _onChangeView = (value, event) => {
|
|
657
|
+
LX.addDropdownMenu(event.target, [
|
|
658
|
+
{ name: 'Grid', icon: 'LayoutGrid', callback: () => this._setContentLayout(AssetView.LAYOUT_GRID) },
|
|
659
|
+
{ name: 'Compact', icon: 'LayoutList',
|
|
660
|
+
callback: () => this._setContentLayout(AssetView.LAYOUT_COMPACT) },
|
|
661
|
+
{ name: 'List', icon: 'List', callback: () => this._setContentLayout(AssetView.LAYOUT_LIST) }
|
|
662
|
+
], { side: 'bottom', align: 'start' });
|
|
663
|
+
};
|
|
664
|
+
this.toolsPanel.refresh = () => {
|
|
665
|
+
this.toolsPanel.clear();
|
|
666
|
+
const typeEntries = Object.keys(this.allowedTypes);
|
|
667
|
+
// Put it in the content panel if no browser
|
|
668
|
+
if (this.skipBrowser) {
|
|
669
|
+
this._createNavigationBar(this.toolsPanel);
|
|
670
|
+
}
|
|
671
|
+
this.toolsPanel.sameLine();
|
|
672
|
+
const sortButton = this.toolsPanel.addButton(null, '', _onSort.bind(this), { title: 'Sort',
|
|
673
|
+
tooltip: true, icon: (this.sortMode === AssetView.CONTENT_SORT_ASC) ? 'SortAsc' : 'SortDesc' });
|
|
674
|
+
this.toolsPanel.addButton(null, '', _onChangeView.bind(this), { title: 'View', tooltip: true,
|
|
675
|
+
icon: (this.layout === AssetView.LAYOUT_GRID) ? 'LayoutGrid' : 'LayoutList' });
|
|
676
|
+
this.toolsPanel.addSelect(null, typeEntries, this.filter ?? typeEntries[0], (v) => {
|
|
677
|
+
this._refreshContent(undefined, v);
|
|
678
|
+
}, { overflowContainer: null });
|
|
679
|
+
this.toolsPanel.addText(null, this.searchValue ?? '', (v) => this._refreshContent(v), {
|
|
680
|
+
className: 'flex flex-auto-fill',
|
|
681
|
+
placeholder: 'Search assets..'
|
|
682
|
+
});
|
|
683
|
+
this.toolsPanel.endLine();
|
|
684
|
+
if (this._paginator) {
|
|
685
|
+
const inlineContainer = sortButton.root.parentElement;
|
|
686
|
+
inlineContainer.appendChild(this._paginator.root);
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
// Start content panel
|
|
690
|
+
this.content = document.createElement('ul');
|
|
691
|
+
this.content.className = 'lexassetscontent';
|
|
692
|
+
this.contentPanel.attach(this.content);
|
|
693
|
+
if (!this.skipBrowser) {
|
|
694
|
+
this.contentPanel.addText(null, this.path.join('/'), null, {
|
|
695
|
+
inputClass: 'bg-none fg-quinary text-end',
|
|
696
|
+
disabled: true,
|
|
697
|
+
signal: '@on_folder_change'
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
this.content.addEventListener('dragenter', function (e) {
|
|
701
|
+
e.preventDefault();
|
|
702
|
+
this.classList.add('dragging');
|
|
703
|
+
});
|
|
704
|
+
this.content.addEventListener('dragleave', function (e) {
|
|
705
|
+
e.preventDefault();
|
|
706
|
+
this.classList.remove('dragging');
|
|
707
|
+
});
|
|
708
|
+
this.content.addEventListener('drop', (e) => {
|
|
709
|
+
e.preventDefault();
|
|
710
|
+
this._processDrop(e);
|
|
711
|
+
});
|
|
712
|
+
this.content.addEventListener('click', function () {
|
|
713
|
+
this.querySelectorAll('.lexassetitem').forEach((i) => i.classList.remove('selected'));
|
|
714
|
+
});
|
|
715
|
+
this.content.addEventListener('contextmenu', function (e) {
|
|
716
|
+
e.preventDefault();
|
|
717
|
+
const options = [
|
|
718
|
+
{
|
|
719
|
+
name: 'New Folder',
|
|
720
|
+
icon: LX.makeIcon('FolderPlus'),
|
|
721
|
+
callback: () => {
|
|
722
|
+
that._requestCreateFolder();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
];
|
|
726
|
+
LX.addClass(that.contentPanel.root, 'pointer-events-none');
|
|
727
|
+
LX.addDropdownMenu(e.target, options, { side: 'right', align: 'start', event: e, onBlur: () => {
|
|
728
|
+
LX.removeClass(that.contentPanel.root, 'pointer-events-none');
|
|
729
|
+
} });
|
|
730
|
+
});
|
|
731
|
+
this._refreshContent();
|
|
732
|
+
// After content to update the size of the content based on the toolbar
|
|
733
|
+
LX.doAsync(() => this.toolsPanel.refresh(), 100);
|
|
734
|
+
}
|
|
735
|
+
_makeNameFilterFn(searchValue) {
|
|
736
|
+
const q = searchValue.trim();
|
|
737
|
+
if (q.includes('*') || q.includes('?')) {
|
|
738
|
+
const regex = LX.wildcardToRegExp(q);
|
|
739
|
+
return (name) => regex.test(name);
|
|
740
|
+
}
|
|
741
|
+
// default case, only check include
|
|
742
|
+
return (name) => name.toLowerCase().includes(q.toLowerCase());
|
|
743
|
+
}
|
|
744
|
+
_refreshContent(searchValue, filter) {
|
|
745
|
+
const isCompactLayout = this.layout == AssetView.LAYOUT_COMPACT;
|
|
746
|
+
const isListLayout = this.layout == AssetView.LAYOUT_LIST;
|
|
747
|
+
this.filter = filter ?? (this.filter ?? 'None');
|
|
748
|
+
this.searchValue = searchValue ?? (this.searchValue ?? '');
|
|
749
|
+
this.content.innerHTML = '';
|
|
750
|
+
this.content.className = `lexassetscontent${isCompactLayout ? ' compact' : (isListLayout ? ' list' : '')}`;
|
|
751
|
+
if (!this.currentData.length) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
const fr = new FileReader();
|
|
755
|
+
const nameFilterFn = this._makeNameFilterFn(this.searchValue);
|
|
756
|
+
const filteredData = this.currentData.filter((_i) => {
|
|
757
|
+
const typeMatch = this.filter !== 'None' ? _i.type.toLowerCase() === this.filter.toLowerCase() : true;
|
|
758
|
+
const nameMatch = nameFilterFn(_i.id);
|
|
759
|
+
return typeMatch && nameMatch;
|
|
760
|
+
});
|
|
761
|
+
this._paginator?.setPages(Math.max(Math.ceil(filteredData.length / this.assetsPerPage), 1));
|
|
762
|
+
// Show all data if using filters
|
|
763
|
+
const start = this._paginator ? (this._paginator.page - 1) * this.assetsPerPage : 0;
|
|
764
|
+
const end = this._paginator ? Math.min(start + this.assetsPerPage, filteredData.length) : filteredData.length;
|
|
765
|
+
for (let i = start; i < end; ++i) {
|
|
766
|
+
let item = filteredData[i];
|
|
767
|
+
if (item.path) {
|
|
768
|
+
LX.request({ url: item.path, dataType: 'blob', success: (f) => {
|
|
769
|
+
item.metadata.bytesize = f.size;
|
|
770
|
+
fr.readAsDataURL(f);
|
|
771
|
+
fr.onload = (e) => {
|
|
772
|
+
const target = e.currentTarget;
|
|
773
|
+
item.src = target.result; // This is a base64 string...
|
|
774
|
+
item.metadata.path = item.path;
|
|
775
|
+
delete item.path;
|
|
776
|
+
this._refreshContent(searchValue, filter);
|
|
777
|
+
};
|
|
778
|
+
} });
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
item.domEl = this.addItem(item, undefined, false);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (this.onRefreshContent) {
|
|
785
|
+
this.onRefreshContent(searchValue, filter);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
_previewAsset(file) {
|
|
789
|
+
if (this.skipPreview) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const is_base_64 = file.src && file.src.includes('data:image/');
|
|
793
|
+
file.metadata = file.metadata ?? {};
|
|
794
|
+
this.previewPanel.clear();
|
|
795
|
+
this.previewPanel.branch('Asset');
|
|
796
|
+
if (file.type == 'image' || file.src) {
|
|
797
|
+
const hasImage = ['png', 'jpg'].indexOf(LX.getExtension(file.src)) > -1 || is_base_64;
|
|
798
|
+
if (hasImage) {
|
|
799
|
+
this.previewPanel.addImage(null, file.src, { style: { width: '100%' } });
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (file.metadata.lastModified && !file.metadata.lastModifiedDate) {
|
|
803
|
+
file.metadata.lastModifiedDate = this._lastModifiedToStringDate(file.metadata.lastModified);
|
|
804
|
+
}
|
|
805
|
+
const options = { disabled: true };
|
|
806
|
+
this.previewPanel.addText('Filename', file.id, null, options);
|
|
807
|
+
if (file.metadata.lastModifiedDate) {
|
|
808
|
+
this.previewPanel.addText('Last Modified', file.metadata.lastModifiedDate, null, options);
|
|
809
|
+
}
|
|
810
|
+
if (file.metadata.path || file.src) {
|
|
811
|
+
this.previewPanel.addText('URL', file.metadata.path ? file.metadata.path : file.src, null, options);
|
|
812
|
+
}
|
|
813
|
+
this.previewPanel.addText('Path', this.path.join('/'), null, options);
|
|
814
|
+
this.previewPanel.addText('Type', file.type, null, options);
|
|
815
|
+
if (file.metadata.bytesize) {
|
|
816
|
+
this.previewPanel.addText('Size', LX.formatBytes(file.metadata.bytesize), null, options);
|
|
817
|
+
}
|
|
818
|
+
if (file.type == 'folder') {
|
|
819
|
+
this.previewPanel.addText('Files', file.children ? file.children.length.toString() : '0', null, options);
|
|
820
|
+
}
|
|
821
|
+
this.previewPanel.addSeparator();
|
|
822
|
+
const previewActions = [...this.previewActions];
|
|
823
|
+
if (!previewActions.length && file.type !== 'folder') {
|
|
824
|
+
// By default
|
|
825
|
+
previewActions.push({
|
|
826
|
+
name: 'Download',
|
|
827
|
+
callback: () => LX.downloadURL(file.src, file.id)
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
for (let action of previewActions) {
|
|
831
|
+
if (action.type && action.type !== file.type || action.path && action.path !== this.path.join('/')) {
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
this.previewPanel.addButton(null, action.name, action.callback.bind(this, file));
|
|
835
|
+
}
|
|
836
|
+
this.previewPanel.merge();
|
|
837
|
+
}
|
|
838
|
+
_processDrop(e) {
|
|
839
|
+
if (!e.dataTransfer || !e.dataTransfer.files || e.dataTransfer.files.length == 0) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const fr = new FileReader();
|
|
843
|
+
const num_files = e.dataTransfer.files.length;
|
|
844
|
+
for (let i = 0; i < e.dataTransfer.files.length; ++i) {
|
|
845
|
+
const file = e.dataTransfer.files[i];
|
|
846
|
+
const result = this.currentData.find((e) => e.id === file.name);
|
|
847
|
+
if (result)
|
|
848
|
+
continue;
|
|
849
|
+
fr.readAsDataURL(file);
|
|
850
|
+
fr.onload = (e) => {
|
|
851
|
+
let ext = file.name.substring(file.name.lastIndexOf('.') + 1).toLowerCase();
|
|
852
|
+
let type = null;
|
|
853
|
+
switch (ext) {
|
|
854
|
+
case 'png':
|
|
855
|
+
case 'jpg':
|
|
856
|
+
type = 'image';
|
|
857
|
+
break;
|
|
858
|
+
case 'js':
|
|
859
|
+
case 'css':
|
|
860
|
+
type = 'script';
|
|
861
|
+
break;
|
|
862
|
+
case 'json':
|
|
863
|
+
type = 'json';
|
|
864
|
+
break;
|
|
865
|
+
case 'obj':
|
|
866
|
+
type = 'mesh';
|
|
867
|
+
break;
|
|
868
|
+
default:
|
|
869
|
+
type = ext;
|
|
870
|
+
break;
|
|
871
|
+
}
|
|
872
|
+
let item = {
|
|
873
|
+
id: file.name,
|
|
874
|
+
src: e.currentTarget.result,
|
|
875
|
+
type,
|
|
876
|
+
children: [],
|
|
877
|
+
metadata: {
|
|
878
|
+
extension: ext,
|
|
879
|
+
lastModified: file.lastModified,
|
|
880
|
+
lastModifiedDate: this._lastModifiedToStringDate(file.lastModified),
|
|
881
|
+
unknownExtension: type == ext
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
this.currentData.push(item);
|
|
885
|
+
if (i == (num_files - 1)) {
|
|
886
|
+
this._refreshContent();
|
|
887
|
+
this.tree?.refresh(); // Refresh if tree exists
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
_sortData(sortBy, sortMode) {
|
|
893
|
+
sortBy = sortBy ?? (this._lastSortBy ?? 'id');
|
|
894
|
+
sortMode = sortMode ?? this.sortMode;
|
|
895
|
+
const sortDesc = sortMode === AssetView.CONTENT_SORT_DESC;
|
|
896
|
+
this.currentData = this.currentData.sort((a, b) => {
|
|
897
|
+
var r = sortDesc ? b[sortBy].localeCompare(a[sortBy]) : a[sortBy].localeCompare(b[sortBy]);
|
|
898
|
+
if (r == 0)
|
|
899
|
+
r = sortDesc ? b['id'].localeCompare(a['id']) : a['id'].localeCompare(b['id']);
|
|
900
|
+
return r;
|
|
901
|
+
});
|
|
902
|
+
this._lastSortBy = sortBy;
|
|
903
|
+
this.sortMode = sortMode;
|
|
904
|
+
this.toolsPanel.refresh();
|
|
905
|
+
this._refreshContent();
|
|
906
|
+
}
|
|
907
|
+
_enterFolder(folderItem, storeCurrent = true) {
|
|
908
|
+
if (!folderItem) {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
const child = this.currentData[0];
|
|
912
|
+
const sameFolder = child?.parent?.id === folderItem.id;
|
|
913
|
+
if (storeCurrent) {
|
|
914
|
+
this.prevData.push(this.currentFolder ?? {
|
|
915
|
+
id: '/',
|
|
916
|
+
children: this.data,
|
|
917
|
+
type: 'root',
|
|
918
|
+
metadata: {}
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
let mustRefresh = !sameFolder;
|
|
922
|
+
const onEnterFolder = this._callbacks['enterFolder'];
|
|
923
|
+
if (onEnterFolder !== undefined) {
|
|
924
|
+
const event = {
|
|
925
|
+
type: 'enter_folder',
|
|
926
|
+
to: folderItem,
|
|
927
|
+
userInitiated: true
|
|
928
|
+
};
|
|
929
|
+
const r = onEnterFolder(event);
|
|
930
|
+
mustRefresh = mustRefresh || r;
|
|
931
|
+
}
|
|
932
|
+
// Update this after the event since the user might have added or modified the data
|
|
933
|
+
this.currentFolder = folderItem;
|
|
934
|
+
this.currentData = this.currentFolder?.children ?? [];
|
|
935
|
+
if (mustRefresh) {
|
|
936
|
+
this._processData(this.data);
|
|
937
|
+
this._refreshContent();
|
|
938
|
+
}
|
|
939
|
+
this._updatePath();
|
|
940
|
+
}
|
|
941
|
+
_removeItemFromParent(item) {
|
|
942
|
+
const oldParent = item.parent;
|
|
943
|
+
if (oldParent) {
|
|
944
|
+
const idx = oldParent.children?.indexOf(item) ?? -1;
|
|
945
|
+
if (idx < 0) {
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
948
|
+
oldParent.children?.splice(idx, 1);
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
const oldDir = item.dir;
|
|
952
|
+
if (oldDir) {
|
|
953
|
+
const idx = oldDir.indexOf(item);
|
|
954
|
+
if (idx < 0) {
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
oldDir.splice(idx, 1);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
_requestDeleteItem(item) {
|
|
963
|
+
const onBeforeDelete = this._callbacks['beforeDelete'];
|
|
964
|
+
const onDelete = this._callbacks['delete'];
|
|
965
|
+
const resolve = (...args) => {
|
|
966
|
+
this._deleteItem(item);
|
|
967
|
+
const event = {
|
|
968
|
+
type: 'delete',
|
|
969
|
+
items: [item],
|
|
970
|
+
userInitiated: true
|
|
971
|
+
};
|
|
972
|
+
if (onDelete)
|
|
973
|
+
onDelete(event, ...args);
|
|
974
|
+
};
|
|
975
|
+
if (onBeforeDelete) {
|
|
976
|
+
const event = {
|
|
977
|
+
type: 'delete',
|
|
978
|
+
items: [item],
|
|
979
|
+
userInitiated: true
|
|
980
|
+
};
|
|
981
|
+
onBeforeDelete(event, resolve);
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
resolve();
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
_deleteItem(item) {
|
|
988
|
+
const ok = this._removeItemFromParent(item);
|
|
989
|
+
if (!ok) {
|
|
990
|
+
console.error('[AssetView Error] Cannot delete. Item not found.');
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
this._refreshContent(this.searchValue, this.filter);
|
|
994
|
+
this.tree?.refresh();
|
|
995
|
+
this.previewPanel?.clear();
|
|
996
|
+
}
|
|
997
|
+
_requestMoveItemToFolder(item, folder) {
|
|
998
|
+
const onBeforeMove = this._callbacks['beforeMove'];
|
|
999
|
+
const onMove = this._callbacks['move'];
|
|
1000
|
+
const resolve = (...args) => {
|
|
1001
|
+
this._moveItemToFolder(item, folder);
|
|
1002
|
+
const event = {
|
|
1003
|
+
type: 'move',
|
|
1004
|
+
items: [item],
|
|
1005
|
+
from: item.parent,
|
|
1006
|
+
to: folder,
|
|
1007
|
+
userInitiated: true
|
|
1008
|
+
};
|
|
1009
|
+
if (onMove)
|
|
1010
|
+
onMove(event, ...args);
|
|
1011
|
+
};
|
|
1012
|
+
if (onBeforeMove) {
|
|
1013
|
+
const event = {
|
|
1014
|
+
type: 'move',
|
|
1015
|
+
items: [item],
|
|
1016
|
+
from: item.parent,
|
|
1017
|
+
to: folder,
|
|
1018
|
+
userInitiated: true
|
|
1019
|
+
};
|
|
1020
|
+
onBeforeMove(event, resolve);
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
resolve();
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
_moveItemToFolder(item, folder) {
|
|
1027
|
+
const ok = this._removeItemFromParent(item);
|
|
1028
|
+
if (!ok) {
|
|
1029
|
+
console.error('[AssetView Error] Cannot move. Item not found.');
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
folder.children = folder.children ?? [];
|
|
1033
|
+
folder.children.push(item);
|
|
1034
|
+
item.parent = folder;
|
|
1035
|
+
item.dir = folder.children;
|
|
1036
|
+
this._refreshContent();
|
|
1037
|
+
this.tree?.refresh();
|
|
1038
|
+
this._moveItemDialog?.destroy();
|
|
1039
|
+
this._movingItem = undefined;
|
|
1040
|
+
}
|
|
1041
|
+
_moveItem(item, defaultFolder) {
|
|
1042
|
+
if (this._moveItemDialog) {
|
|
1043
|
+
this._moveItemDialog.destroy();
|
|
1044
|
+
}
|
|
1045
|
+
this._movingItem = item;
|
|
1046
|
+
let targetFolder = null;
|
|
1047
|
+
let bcContainer;
|
|
1048
|
+
const _openFolder = function (p, container, updateBc = true) {
|
|
1049
|
+
container.innerHTML = '';
|
|
1050
|
+
targetFolder = p;
|
|
1051
|
+
for (let pi of (targetFolder.children ?? targetFolder)) {
|
|
1052
|
+
const row = LX.makeContainer(['100%', 'auto'], 'flex flex-row px-1 items-center', '', container);
|
|
1053
|
+
const isFolder = pi.type === 'folder';
|
|
1054
|
+
const rowItem = LX.makeContainer(['100%', 'auto'], `move-item flex flex-row gap-1 py-1 px-3 cursor-pointer ${isFolder ? 'fg-primary font-medium' : 'fg-quinary'} rounded-xxl ${isFolder ? 'hover:bg-secondary' : 'hover:bg-primary'}`, `${isFolder ? LX.makeIcon('FolderOpen', { svgClass: '' }).innerHTML : ''}${pi.id}`, row);
|
|
1055
|
+
if (isFolder) {
|
|
1056
|
+
rowItem.addEventListener('click', () => {
|
|
1057
|
+
container.querySelectorAll('.move-item').forEach((el) => LX.removeClass(el, 'bg-quinary'));
|
|
1058
|
+
LX.addClass(rowItem, 'bg-quinary');
|
|
1059
|
+
targetFolder = pi;
|
|
1060
|
+
});
|
|
1061
|
+
const fPathButton = new LX.Button(null, 'FPathButton', () => {
|
|
1062
|
+
_openFolder(pi, container);
|
|
1063
|
+
}, { icon: 'ChevronRight', className: 'ml-auto h-8', buttonClass: 'bg-none hover:bg-secondary' });
|
|
1064
|
+
row.appendChild(fPathButton.root);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
if (!updateBc) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
const path = [];
|
|
1071
|
+
if (targetFolder && targetFolder.parent) {
|
|
1072
|
+
path.push(targetFolder.id);
|
|
1073
|
+
const _pushParentsId = (i) => {
|
|
1074
|
+
if (!i)
|
|
1075
|
+
return;
|
|
1076
|
+
path.push(i.parent ? i.id : '@');
|
|
1077
|
+
_pushParentsId(i.parent);
|
|
1078
|
+
};
|
|
1079
|
+
_pushParentsId(targetFolder.parent);
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
path.push('@');
|
|
1083
|
+
}
|
|
1084
|
+
bcContainer.innerHTML = '';
|
|
1085
|
+
bcContainer.appendChild(LX.makeBreadcrumb(path.reverse().map((p) => {
|
|
1086
|
+
return { title: p };
|
|
1087
|
+
}), {
|
|
1088
|
+
maxItems: 4,
|
|
1089
|
+
separatorIcon: 'ChevronRight'
|
|
1090
|
+
}));
|
|
1091
|
+
};
|
|
1092
|
+
this._moveItemDialog = new LX.Dialog(`Moving: ${item.id}`, (p) => {
|
|
1093
|
+
const area = new LX.Area({ className: 'flex flex-col rounded-lg' });
|
|
1094
|
+
p.attach(area);
|
|
1095
|
+
const content = LX.makeContainer(['auto', '100%'], 'flex flex-auto-fill flex-col overflow-scroll py-2 gap-1', ``);
|
|
1096
|
+
{
|
|
1097
|
+
const headerPanel = area.addPanel({ className: 'p-2 border-bottom flex flex-auto', height: 'auto' });
|
|
1098
|
+
headerPanel.sameLine(2, 'w-full');
|
|
1099
|
+
headerPanel.addButton(null, 'BackButton', () => {
|
|
1100
|
+
if (targetFolder && targetFolder.parent)
|
|
1101
|
+
_openFolder(targetFolder.parent, content);
|
|
1102
|
+
}, { icon: 'ArrowLeft', title: 'Back', tooltip: true, className: 'flex-auto',
|
|
1103
|
+
buttonClass: 'bg-none hover:bg-secondary' });
|
|
1104
|
+
bcContainer = LX.makeElement('div');
|
|
1105
|
+
headerPanel.addContent('ITEM_MOVE_PATH', bcContainer, { signal: '@item_move_path',
|
|
1106
|
+
className: 'flex-auto-fill' });
|
|
1107
|
+
}
|
|
1108
|
+
area.attach(content);
|
|
1109
|
+
_openFolder(defaultFolder ?? this.data, content);
|
|
1110
|
+
{
|
|
1111
|
+
const footerPanel = area.addPanel({ className: 'p-2 border-top flex flex-auto justify-between',
|
|
1112
|
+
height: 'auto' });
|
|
1113
|
+
footerPanel.addButton(null, 'NewFolderButton', () => {
|
|
1114
|
+
this._requestCreateFolder(targetFolder);
|
|
1115
|
+
}, { width: 'auto', icon: 'FolderPlus', title: 'Create Folder', tooltip: true, className: 'ml-2',
|
|
1116
|
+
buttonClass: 'bg-none hover:bg-secondary' });
|
|
1117
|
+
footerPanel.sameLine(2, 'mr-2');
|
|
1118
|
+
footerPanel.addButton(null, 'Cancel', () => {
|
|
1119
|
+
this._moveItemDialog.close();
|
|
1120
|
+
}, { buttonClass: 'bg-none fg-error' });
|
|
1121
|
+
footerPanel.addButton(null, 'Move', () => {
|
|
1122
|
+
this._requestMoveItemToFolder(item, targetFolder);
|
|
1123
|
+
}, { className: '', buttonClass: 'contrast' });
|
|
1124
|
+
}
|
|
1125
|
+
}, { modal: true, size: ['616px', '500px'], closable: true, onBeforeClose: () => {
|
|
1126
|
+
delete this._moveItemDialog;
|
|
1127
|
+
} });
|
|
1128
|
+
}
|
|
1129
|
+
_requestCloneItem(item) {
|
|
1130
|
+
if (item.type === 'folder') {
|
|
1131
|
+
console.error('[AssetView Error] Cannot clone a folder.');
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
const dir = item.dir ?? [];
|
|
1135
|
+
const idx = dir.indexOf(item);
|
|
1136
|
+
if (idx < 0) {
|
|
1137
|
+
console.error('[AssetView Error] Cannot clone. Item not found.');
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
const onBeforeClone = this._callbacks['beforeClone'];
|
|
1141
|
+
const onClone = this._callbacks['clone'];
|
|
1142
|
+
const resolve = (...args) => {
|
|
1143
|
+
const clonedItem = this._cloneItem(item);
|
|
1144
|
+
const event = {
|
|
1145
|
+
type: 'clone',
|
|
1146
|
+
items: [item],
|
|
1147
|
+
result: [clonedItem],
|
|
1148
|
+
userInitiated: true
|
|
1149
|
+
};
|
|
1150
|
+
if (onClone)
|
|
1151
|
+
onClone(event, ...args);
|
|
1152
|
+
};
|
|
1153
|
+
if (onBeforeClone) {
|
|
1154
|
+
const event = {
|
|
1155
|
+
type: 'clone',
|
|
1156
|
+
items: [item],
|
|
1157
|
+
userInitiated: true
|
|
1158
|
+
};
|
|
1159
|
+
onBeforeClone(event, resolve);
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
resolve();
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
_cloneItem(item) {
|
|
1166
|
+
const parent = item.parent;
|
|
1167
|
+
const dir = item.dir ?? [];
|
|
1168
|
+
const idx = dir.indexOf(item);
|
|
1169
|
+
delete item.domEl;
|
|
1170
|
+
delete item.dir;
|
|
1171
|
+
delete item.parent;
|
|
1172
|
+
const newItem = LX.deepCopy(item);
|
|
1173
|
+
newItem.id = this._getClonedName(item.id, dir);
|
|
1174
|
+
newItem.dir = item.dir = dir;
|
|
1175
|
+
newItem.parent = item.parent = parent;
|
|
1176
|
+
newItem.metadata.uid = LX.guidGenerator(); // generate new uid
|
|
1177
|
+
dir.splice(idx + 1, 0, newItem);
|
|
1178
|
+
this._refreshContent(this.searchValue, this.filter);
|
|
1179
|
+
return newItem;
|
|
1180
|
+
}
|
|
1181
|
+
_getClonedName(originalName, siblings) {
|
|
1182
|
+
const dotIndex = originalName.lastIndexOf('.');
|
|
1183
|
+
let base = originalName;
|
|
1184
|
+
let ext = '';
|
|
1185
|
+
if (dotIndex > 0) {
|
|
1186
|
+
base = originalName.substring(0, dotIndex);
|
|
1187
|
+
ext = originalName.substring(dotIndex); // includes the dot
|
|
1188
|
+
}
|
|
1189
|
+
// core name without (N)
|
|
1190
|
+
const match = base.match(/^(.*)\s\((\d+)\)$/);
|
|
1191
|
+
if (match) {
|
|
1192
|
+
base = match[1];
|
|
1193
|
+
}
|
|
1194
|
+
let maxN = -1;
|
|
1195
|
+
for (const s of siblings) {
|
|
1196
|
+
if (!s.id)
|
|
1197
|
+
continue;
|
|
1198
|
+
let sBase = s.id;
|
|
1199
|
+
let sExt = '';
|
|
1200
|
+
const sDot = sBase.lastIndexOf('.');
|
|
1201
|
+
if (sDot > 0) {
|
|
1202
|
+
sExt = sBase.substring(sDot);
|
|
1203
|
+
sBase = sBase.substring(0, sDot);
|
|
1204
|
+
}
|
|
1205
|
+
// Only compare same extension and same base!
|
|
1206
|
+
if (sExt !== ext)
|
|
1207
|
+
continue;
|
|
1208
|
+
const m = sBase.match(new RegExp('^' + LX.escapeRegExp(base) + '\\s\\((\\d+)\\)$'));
|
|
1209
|
+
if (m) {
|
|
1210
|
+
const num = parseInt(m[1]);
|
|
1211
|
+
if (num > maxN)
|
|
1212
|
+
maxN = num;
|
|
1213
|
+
}
|
|
1214
|
+
else if (sBase === base) {
|
|
1215
|
+
// Base name exists without number
|
|
1216
|
+
maxN = Math.max(maxN, 0);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
return maxN === -1 ? originalName : `${base} (${maxN + 1})${ext}`;
|
|
1220
|
+
}
|
|
1221
|
+
_requestRenameItem(item, newName) {
|
|
1222
|
+
const onBeforeRename = this._callbacks['beforeRename'];
|
|
1223
|
+
const onRename = this._callbacks['rename'];
|
|
1224
|
+
const oldName = item.id;
|
|
1225
|
+
const resolve = (...args) => {
|
|
1226
|
+
this._renameItem(item, newName);
|
|
1227
|
+
const event = {
|
|
1228
|
+
type: 'rename',
|
|
1229
|
+
items: [item],
|
|
1230
|
+
oldName,
|
|
1231
|
+
newName,
|
|
1232
|
+
userInitiated: true
|
|
1233
|
+
};
|
|
1234
|
+
if (onRename)
|
|
1235
|
+
onRename(event, ...args);
|
|
1236
|
+
};
|
|
1237
|
+
if (onBeforeRename) {
|
|
1238
|
+
const event = {
|
|
1239
|
+
type: 'rename',
|
|
1240
|
+
items: [item],
|
|
1241
|
+
oldName,
|
|
1242
|
+
newName,
|
|
1243
|
+
userInitiated: true
|
|
1244
|
+
};
|
|
1245
|
+
onBeforeRename(event, resolve);
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
resolve();
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
_renameItem(item, newName) {
|
|
1252
|
+
const idx = this.currentData.indexOf(item);
|
|
1253
|
+
if (idx < 0) {
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
const wasSelected = LX.hasClass(item.domEl, 'selected');
|
|
1257
|
+
const hoverTitle = this.content.querySelector(`#floatingTitle_${item.id.replace(/\s/g, '_').replaceAll('.', '_')}`);
|
|
1258
|
+
if (hoverTitle)
|
|
1259
|
+
hoverTitle.remove();
|
|
1260
|
+
item.domEl?.remove();
|
|
1261
|
+
item.id = newName;
|
|
1262
|
+
item.domEl = this.addItem(item, idx * 2);
|
|
1263
|
+
if (wasSelected) {
|
|
1264
|
+
this._previewAsset(item);
|
|
1265
|
+
}
|
|
1266
|
+
this.tree?.refresh();
|
|
1267
|
+
this._processData(this.data);
|
|
1268
|
+
}
|
|
1269
|
+
_renameItemPopover(item) {
|
|
1270
|
+
const idx = this.currentData.indexOf(item);
|
|
1271
|
+
if (idx < 0) {
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
const onRename = (value) => {
|
|
1275
|
+
p.destroy();
|
|
1276
|
+
this._requestRenameItem(item, value);
|
|
1277
|
+
};
|
|
1278
|
+
let newName = item.id;
|
|
1279
|
+
const panel = new LX.Panel();
|
|
1280
|
+
panel.addText(null, item.id, (v, e) => {
|
|
1281
|
+
newName = v;
|
|
1282
|
+
if (e.constructor === KeyboardEvent)
|
|
1283
|
+
onRename(v);
|
|
1284
|
+
});
|
|
1285
|
+
panel.addButton(null, 'Save', () => {
|
|
1286
|
+
onRename(newName);
|
|
1287
|
+
}, { buttonClass: 'contrast' });
|
|
1288
|
+
const p = new LX.Popover(item.domEl, [panel], { align: 'center', side: 'bottom', sideOffset: -128 });
|
|
1289
|
+
}
|
|
1290
|
+
_requestCreateFolder(folder) {
|
|
1291
|
+
folder = folder ?? this.currentFolder;
|
|
1292
|
+
if (!folder) {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
const onBeforeCreateFolder = this._callbacks['beforeCreateFolder'];
|
|
1296
|
+
const onCreateFolder = this._callbacks['createFolder'];
|
|
1297
|
+
const resolve = (...args) => {
|
|
1298
|
+
const newFolder = this._createFolder(folder);
|
|
1299
|
+
const event = {
|
|
1300
|
+
type: 'create-folder',
|
|
1301
|
+
result: [newFolder],
|
|
1302
|
+
where: folder,
|
|
1303
|
+
userInitiated: true
|
|
1304
|
+
};
|
|
1305
|
+
if (onCreateFolder)
|
|
1306
|
+
onCreateFolder(event, ...args);
|
|
1307
|
+
};
|
|
1308
|
+
if (onBeforeCreateFolder) {
|
|
1309
|
+
const event = {
|
|
1310
|
+
type: 'create-folder',
|
|
1311
|
+
where: folder,
|
|
1312
|
+
userInitiated: true
|
|
1313
|
+
};
|
|
1314
|
+
onBeforeCreateFolder(event, resolve);
|
|
1315
|
+
}
|
|
1316
|
+
else {
|
|
1317
|
+
resolve();
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
_createFolder(folder) {
|
|
1321
|
+
folder = folder ?? this.currentFolder;
|
|
1322
|
+
if (!folder) {
|
|
1323
|
+
throw ('_createFolder: Something went wrong!');
|
|
1324
|
+
}
|
|
1325
|
+
const newFolder = {
|
|
1326
|
+
id: this._getClonedName('New Folder', folder.children),
|
|
1327
|
+
type: 'folder',
|
|
1328
|
+
children: [],
|
|
1329
|
+
parent: this.currentFolder,
|
|
1330
|
+
metadata: {}
|
|
1331
|
+
};
|
|
1332
|
+
folder.children.push(newFolder);
|
|
1333
|
+
this._refreshContent();
|
|
1334
|
+
this.tree?.refresh();
|
|
1335
|
+
if (this._moveItemDialog && this._movingItem) {
|
|
1336
|
+
this._moveItem(this._movingItem, folder);
|
|
1337
|
+
}
|
|
1338
|
+
return folder;
|
|
1339
|
+
}
|
|
1340
|
+
_openScriptInEditor(script) {
|
|
1341
|
+
if (this._scriptCodeDialog) {
|
|
1342
|
+
this._scriptCodeDialog.destroy();
|
|
1343
|
+
}
|
|
1344
|
+
this._scriptCodeDialog = new LX.Dialog(null, (p) => {
|
|
1345
|
+
const area = new LX.Area({ className: 'rounded-lg' });
|
|
1346
|
+
p.attach(area);
|
|
1347
|
+
new LX.CodeEditor(area, {
|
|
1348
|
+
allowAddScripts: false,
|
|
1349
|
+
files: [script.src]
|
|
1350
|
+
});
|
|
1351
|
+
}, { size: ['50%', '600px'], closable: true, onBeforeClose: () => {
|
|
1352
|
+
delete this._scriptCodeDialog;
|
|
1353
|
+
} });
|
|
1354
|
+
}
|
|
1355
|
+
_setAssetsPerPage(n) {
|
|
1356
|
+
this._assetsPerPage = n;
|
|
1357
|
+
this._refreshContent();
|
|
1358
|
+
}
|
|
1359
|
+
_lastModifiedToStringDate(lm) {
|
|
1360
|
+
const d = new Date(lm).toLocaleString();
|
|
1361
|
+
return d.substring(0, d.indexOf(','));
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
LX.AssetView = AssetView;
|
|
1365
|
+
|
|
1366
|
+
export { AssetView };
|
|
1367
|
+
//# sourceMappingURL=AssetView.js.map
|