@cloudron/pankow 3.5.18 → 3.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Button.vue +11 -1
- package/components/DirectoryView.vue +42 -12
- package/components/Menu.vue +5 -6
- package/fetcher.js +2 -0
- package/package.json +13 -6
- package/tooltip.js +33 -23
- package/viewers/ThreeDViewer.vue +102 -0
- package/viewers.js +2 -0
package/components/Button.vue
CHANGED
|
@@ -12,6 +12,14 @@ const props = defineProps({
|
|
|
12
12
|
target: String,
|
|
13
13
|
iconRight: String,
|
|
14
14
|
menu: Array,
|
|
15
|
+
danger: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: false
|
|
18
|
+
},
|
|
19
|
+
success: {
|
|
20
|
+
type: Boolean,
|
|
21
|
+
default: false
|
|
22
|
+
},
|
|
15
23
|
disabled: {
|
|
16
24
|
type: Boolean,
|
|
17
25
|
default: false
|
|
@@ -43,7 +51,9 @@ function onClick(event) {
|
|
|
43
51
|
</script>
|
|
44
52
|
|
|
45
53
|
<template>
|
|
46
|
-
<component ref="elem" :is="href ? 'a' : 'div'" class="pankow-button" role="button" type="button" :class="{ 'pankow-button-disabled': disabled }"
|
|
54
|
+
<component ref="elem" :is="href ? 'a' : 'div'" class="pankow-button" role="button" type="button" :class="{ 'pankow-button-disabled': disabled }"
|
|
55
|
+
@click="onClick" @keydown.enter="onClick" :href="disabled ? null : href" :target="target" tabindex="0"
|
|
56
|
+
:success="success || undefined" :danger="danger || undefined">
|
|
47
57
|
<Menu ref="menuItem" :model="menu" v-if="menu"></Menu>
|
|
48
58
|
<Spinner v-show="loading" style="stroke: white;" :class="{ 'pankow-button-icon-with-text': $slots['default'] }"/>
|
|
49
59
|
<Icon v-show="!loading && icon" :icon="icon" :class="{ 'pankow-button-icon-with-text': $slots['default'] }" />
|
|
@@ -124,6 +124,26 @@ export default {
|
|
|
124
124
|
type: Boolean,
|
|
125
125
|
default: true
|
|
126
126
|
},
|
|
127
|
+
showNewFile: {
|
|
128
|
+
type: Boolean,
|
|
129
|
+
default: true
|
|
130
|
+
},
|
|
131
|
+
showNewFolder: {
|
|
132
|
+
type: Boolean,
|
|
133
|
+
default: true
|
|
134
|
+
},
|
|
135
|
+
showCut: {
|
|
136
|
+
type: Boolean,
|
|
137
|
+
default: true
|
|
138
|
+
},
|
|
139
|
+
showCopy: {
|
|
140
|
+
type: Boolean,
|
|
141
|
+
default: true
|
|
142
|
+
},
|
|
143
|
+
showDelete: {
|
|
144
|
+
type: Boolean,
|
|
145
|
+
default: true
|
|
146
|
+
},
|
|
127
147
|
showShare: {
|
|
128
148
|
// if String its the name of the property for existing share indicator
|
|
129
149
|
type: [ Boolean, String ],
|
|
@@ -299,25 +319,31 @@ export default {
|
|
|
299
319
|
icon:'fa-solid fa-check-double',
|
|
300
320
|
action: this.onSelectAll
|
|
301
321
|
}, {
|
|
302
|
-
separator:true
|
|
322
|
+
separator:true,
|
|
323
|
+
visible: () => { return this.showNewFile || this.showNewFolder; },
|
|
303
324
|
}, {
|
|
304
325
|
label: this.tr('filemanager.toolbar.newFile'),
|
|
305
326
|
icon:'fa-solid fa-file-circle-plus',
|
|
306
|
-
action: this.newFileHandler
|
|
327
|
+
action: this.newFileHandler,
|
|
328
|
+
visible: () => { return this.showNewFile; },
|
|
307
329
|
}, {
|
|
308
330
|
label: this.tr('filemanager.toolbar.newFolder'),
|
|
309
331
|
icon:'fa-solid fa-folder-plus',
|
|
310
|
-
action: this.newFolderHandler
|
|
332
|
+
action: this.newFolderHandler,
|
|
333
|
+
visible: () => { return this.showNewFolder; },
|
|
311
334
|
}, {
|
|
312
|
-
separator:true
|
|
335
|
+
separator:true,
|
|
336
|
+
visible: () => { return this.showUploadFile || this.showUploadFolder; },
|
|
313
337
|
}, {
|
|
314
338
|
label: this.tr('filemanager.toolbar.uploadFile'),
|
|
315
339
|
icon:'fa-solid fa-file-arrow-up',
|
|
316
|
-
action: this.uploadFileHandler
|
|
340
|
+
action: this.uploadFileHandler,
|
|
341
|
+
visible: () => { return this.showUploadFile; },
|
|
317
342
|
}, {
|
|
318
343
|
label: this.tr('filemanager.toolbar.uploadFolder'),
|
|
319
344
|
icon:'fa-regular fa-folder-open',
|
|
320
|
-
action: this.uploadFolderHandler
|
|
345
|
+
action: this.uploadFolderHandler,
|
|
346
|
+
visible: () => { return this.showUploadFolder; },
|
|
321
347
|
}],
|
|
322
348
|
contextMenuModel: [{
|
|
323
349
|
label: this.tr('filemanager.list.menu.open'),
|
|
@@ -336,15 +362,17 @@ export default {
|
|
|
336
362
|
icon:'fa-solid fa-share-nodes',
|
|
337
363
|
action: this.onItemShare,
|
|
338
364
|
disabled: () => { return this.selectedCount > 1; },
|
|
339
|
-
visible: this.showShare
|
|
365
|
+
visible: () => { return this.showShare; },
|
|
340
366
|
}, {
|
|
341
367
|
label: this.tr('filemanager.list.menu.copy'),
|
|
342
368
|
icon:'fa-regular fa-copy',
|
|
343
|
-
action: this.onItemsCopy
|
|
369
|
+
action: this.onItemsCopy,
|
|
370
|
+
visible: () => { return this.showCopy; },
|
|
344
371
|
}, {
|
|
345
372
|
label: this.tr('filemanager.list.menu.cut'),
|
|
346
373
|
icon:'fa-solid fa-scissors',
|
|
347
|
-
action: this.onItemsCut
|
|
374
|
+
action: this.onItemsCut,
|
|
375
|
+
visible: () => { return this.showCut; },
|
|
348
376
|
}, {
|
|
349
377
|
label: this.tr('filemanager.list.menu.paste'),
|
|
350
378
|
icon:'fa-regular fa-paste',
|
|
@@ -363,7 +391,7 @@ export default {
|
|
|
363
391
|
label: this.tr('filemanager.list.menu.chown'),
|
|
364
392
|
icon:'fa-solid fa-user-pen',
|
|
365
393
|
action: this.onItemsChangeOwner,
|
|
366
|
-
visible: this.showOwner
|
|
394
|
+
visible: () => { return this.showOwner; },
|
|
367
395
|
}, {
|
|
368
396
|
label: this.tr('filemanager.list.menu.extract'),
|
|
369
397
|
action: this.onItemExtract,
|
|
@@ -381,11 +409,13 @@ export default {
|
|
|
381
409
|
file.name.match(/\.tar\.bz2$/)));
|
|
382
410
|
}
|
|
383
411
|
}, {
|
|
384
|
-
separator:true
|
|
412
|
+
separator:true,
|
|
413
|
+
visible: () => { return this.showDelete; },
|
|
385
414
|
}, {
|
|
386
415
|
label: this.tr('filemanager.list.menu.delete'),
|
|
387
416
|
icon:'fa-regular fa-trash-can',
|
|
388
|
-
action: () => { this.deleteHandler(this.getSelected()); }
|
|
417
|
+
action: () => { this.deleteHandler(this.getSelected()); },
|
|
418
|
+
visible: () => { return this.showDelete; },
|
|
389
419
|
}],
|
|
390
420
|
};
|
|
391
421
|
},
|
package/components/Menu.vue
CHANGED
|
@@ -69,7 +69,7 @@ const props = defineProps({
|
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
const openEventTimeStamp = ref(0);
|
|
72
|
-
|
|
72
|
+
let forElement = null;
|
|
73
73
|
let prevFocusElement = null;
|
|
74
74
|
const isOpen = ref(false);
|
|
75
75
|
let pageX = 0;
|
|
@@ -172,7 +172,7 @@ async function open(event, element = null) {
|
|
|
172
172
|
targetBottom = element ? element.getBoundingClientRect().bottom : event.pageY;
|
|
173
173
|
targetTop = element ? element.getBoundingClientRect().top : event.pageY;
|
|
174
174
|
offsetY.value = element ? (element.getBoundingClientRect().height + 2) : 0; // offset in case we roll up
|
|
175
|
-
forElement
|
|
175
|
+
forElement = element;
|
|
176
176
|
prevFocusElement = document.activeElement;
|
|
177
177
|
|
|
178
178
|
if (!container.value) return;
|
|
@@ -253,20 +253,19 @@ function position() {
|
|
|
253
253
|
if (!container.value) return;
|
|
254
254
|
|
|
255
255
|
const size = getHiddenElementSize(container.value);
|
|
256
|
+
const viewport = getViewport();
|
|
256
257
|
|
|
257
258
|
let left = pageX;
|
|
258
259
|
let top = targetBottom + 1;
|
|
259
260
|
let width = container.value.offsetParent ? container.value.offsetWidth : size.width;
|
|
260
261
|
let height = container.value.offsetParent ? container.value.offsetHeight : size.height;
|
|
261
|
-
let viewport = getViewport();
|
|
262
|
-
|
|
263
262
|
let bottom = viewport.height - targetTop + 1;
|
|
264
263
|
|
|
265
264
|
//flip
|
|
266
265
|
if (left + width - document.body.scrollLeft > viewport.width) {
|
|
267
266
|
// if this is like a dropdown right align instead of flip
|
|
268
|
-
if (forElement
|
|
269
|
-
left = forElement.
|
|
267
|
+
if (forElement) {
|
|
268
|
+
left = forElement.getBoundingClientRect().left + (forElement.getBoundingClientRect().width - width);
|
|
270
269
|
} else {
|
|
271
270
|
left -= width;
|
|
272
271
|
}
|
package/fetcher.js
CHANGED
|
@@ -38,6 +38,8 @@ async function request(uri, method, headers, query, body, options) {
|
|
|
38
38
|
} catch (e) {
|
|
39
39
|
throw new Error(`Failed to parse response as json for content type ${contentType}.`, e);
|
|
40
40
|
}
|
|
41
|
+
} else if (contentType.indexOf('application/octet-stream') !== -1) {
|
|
42
|
+
responseBody = await response.blob();
|
|
41
43
|
} else {
|
|
42
44
|
responseBody = await response.text();
|
|
43
45
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudron/pankow",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.6.1",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "types/index.d.ts",
|
|
8
|
-
"files": [
|
|
8
|
+
"files": [
|
|
9
|
+
"*.css",
|
|
10
|
+
"*.js",
|
|
11
|
+
"components",
|
|
12
|
+
"viewers",
|
|
13
|
+
"types"
|
|
14
|
+
],
|
|
9
15
|
"scripts": {
|
|
10
16
|
"gallery": "cd gallery && vite",
|
|
11
17
|
"build": "cd gallery && vite build",
|
|
@@ -19,12 +25,13 @@
|
|
|
19
25
|
"@fontsource/inter": "^5.2.8",
|
|
20
26
|
"@fortawesome/fontawesome-free": "^7.1.0",
|
|
21
27
|
"filesize": "^11.0.13",
|
|
22
|
-
"monaco-editor": "^0.55.1"
|
|
28
|
+
"monaco-editor": "^0.55.1",
|
|
29
|
+
"online-3d-viewer": "^0.18.0"
|
|
23
30
|
},
|
|
24
31
|
"devDependencies": {
|
|
25
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
32
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
26
33
|
"typescript": "^5.9.3",
|
|
27
|
-
"vite": "^7.
|
|
28
|
-
"vue": "^3.5.
|
|
34
|
+
"vite": "^7.3.1",
|
|
35
|
+
"vue": "^3.5.26"
|
|
29
36
|
}
|
|
30
37
|
}
|
package/tooltip.js
CHANGED
|
@@ -38,9 +38,9 @@ function isElementHidden(element) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function remove(key, target) {
|
|
41
|
-
if (tooltips[key]) tooltips[key].remove();
|
|
41
|
+
if (tooltips[key].element) tooltips[key].element.remove();
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
tooltips[key].element = null;
|
|
44
44
|
clearInterval(intervals[key]);
|
|
45
45
|
|
|
46
46
|
if (target) {
|
|
@@ -49,45 +49,49 @@ function remove(key, target) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function update(target,
|
|
53
|
-
if (!
|
|
52
|
+
function update(target, modifiers, key) {
|
|
53
|
+
if (!tooltips[key].element) return;
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
tooltips[key].element.innerText = tooltips[key].value;
|
|
56
56
|
|
|
57
57
|
// BOTTOM is default
|
|
58
58
|
const pos = modifiers.top ? TOP : (modifiers.left ? LEFT : (modifiers.right ? RIGHT : BOTTOM));
|
|
59
59
|
|
|
60
60
|
const targetRect = target.getBoundingClientRect();
|
|
61
|
-
const tooltipRect =
|
|
61
|
+
const tooltipRect = tooltips[key].element.getBoundingClientRect();
|
|
62
62
|
|
|
63
|
-
if (pos === TOP || pos === BOTTOM)
|
|
64
|
-
else if (pos === LEFT)
|
|
65
|
-
else
|
|
63
|
+
if (pos === TOP || pos === BOTTOM) tooltips[key].element.style.left = (targetRect.left + targetRect.width/2) - (tooltipRect.width/2) + 'px';
|
|
64
|
+
else if (pos === LEFT) tooltips[key].element.style.left = (targetRect.left - tooltipRect.width - padding) + 'px';
|
|
65
|
+
else tooltips[key].element.style.left = (targetRect.left + targetRect.width + padding) + 'px';
|
|
66
66
|
|
|
67
|
-
if (pos === LEFT || pos === RIGHT)
|
|
68
|
-
else if (pos === TOP)
|
|
69
|
-
else
|
|
67
|
+
if (pos === LEFT || pos === RIGHT) tooltips[key].element.style.top = (targetRect.top + targetRect.height/2) - (tooltipRect.height/2) + 'px';
|
|
68
|
+
else if (pos === TOP) tooltips[key].element.style.top = (targetRect.top - tooltipRect.height - padding) + 'px';
|
|
69
|
+
else tooltips[key].element.style.top = (targetRect.bottom + padding) + 'px';
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function mounted(el, binding, vnode) {
|
|
73
73
|
const key = vnode.ctx.uid;
|
|
74
|
+
tooltips[key] = {
|
|
75
|
+
element: null,
|
|
76
|
+
value: binding.value
|
|
77
|
+
};
|
|
74
78
|
|
|
75
79
|
el.addEventListener('mouseenter', () => {
|
|
76
|
-
if (!
|
|
80
|
+
if (!tooltips[key].value) return;
|
|
77
81
|
|
|
78
|
-
const
|
|
79
|
-
tooltips[key] =
|
|
82
|
+
const element = document.createElement('div');
|
|
83
|
+
tooltips[key].element = element;
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
window.document.body.appendChild(
|
|
85
|
+
element.setAttribute('id', key);
|
|
86
|
+
element.setAttribute('role', 'tooltip');
|
|
87
|
+
element.setAttribute('aria-hidden', 'false');
|
|
88
|
+
element.classList.add('pankow-tooltip');
|
|
89
|
+
window.document.body.appendChild(element);
|
|
86
90
|
|
|
87
91
|
el.setAttribute('aria-expanded', 'true');
|
|
88
92
|
el.setAttribute('aria-describedby', key);
|
|
89
93
|
|
|
90
|
-
update(el, binding.
|
|
94
|
+
update(el, binding.modifiers, key);
|
|
91
95
|
|
|
92
96
|
intervals[key] = setInterval(() => {
|
|
93
97
|
if (isElementHidden(el)) remove(key, el)
|
|
@@ -101,12 +105,18 @@ function mounted(el, binding, vnode) {
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
function updated(el, binding, vnode) {
|
|
104
|
-
if (!binding.value)
|
|
105
|
-
|
|
108
|
+
if (!binding.value) remove(vnode.ctx.uid, el);
|
|
109
|
+
|
|
110
|
+
tooltips[vnode.ctx.uid].value = binding.value;
|
|
111
|
+
|
|
112
|
+
update(el, binding.modifiers, vnode.ctx.uid);
|
|
106
113
|
}
|
|
107
114
|
|
|
108
115
|
function beforeUnmount(el, binding, vnode) {
|
|
116
|
+
if (!tooltips[vnode.ctx.uid]) return;
|
|
117
|
+
|
|
109
118
|
remove(vnode.ctx.uid, el);
|
|
119
|
+
delete tooltips[vnode.ctx.uid];
|
|
110
120
|
}
|
|
111
121
|
|
|
112
122
|
const tooltip = {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
|
|
3
|
+
import { ref, useTemplateRef, onMounted } from 'vue';
|
|
4
|
+
import Button from '../components/Button.vue';
|
|
5
|
+
import MainLayout from '../components/MainLayout.vue';
|
|
6
|
+
import TopBar from '../components/TopBar.vue';
|
|
7
|
+
import utils from '../utils.js';
|
|
8
|
+
import { EmbeddedViewer, RGBAColor, RGBColor, EdgeSettings } from 'online-3d-viewer';
|
|
9
|
+
|
|
10
|
+
const emits = defineEmits([ 'close' ]);
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
tr: {
|
|
13
|
+
type: Function,
|
|
14
|
+
default(id) { console.warn('Missing tr for ThreeDViewer'); return utils.translation(id); }
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const entry = ref(null);
|
|
19
|
+
const container = useTemplateRef('container');
|
|
20
|
+
|
|
21
|
+
const SUPPORTED_EXTENSIONS = [
|
|
22
|
+
'3dm', '3ds', '3mf', 'amf', 'bim', 'brep',
|
|
23
|
+
'dae', 'fbx', 'fcstd', 'glb', 'gltf',
|
|
24
|
+
'ifc', 'igs', 'iges', 'stp', 'step',
|
|
25
|
+
'stl', 'obj', 'off', 'ply', 'wrl',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function canHandle(e) {
|
|
29
|
+
return SUPPORTED_EXTENSIONS.includes(e.extension.toLowerCase());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let viewer;
|
|
33
|
+
|
|
34
|
+
async function open(e, content) {
|
|
35
|
+
if (!e || e.isDirectory || !canHandle(e)) return;
|
|
36
|
+
|
|
37
|
+
entry.value = e;
|
|
38
|
+
|
|
39
|
+
viewer.LoadModelFromFileList([ new File([content], e.fileName) ], {
|
|
40
|
+
onModelLoaded: () => viewer.Resize()
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// setTimeout(() => viewer.Resize(), 1000);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function onClose() {
|
|
47
|
+
emits('close');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
onMounted(() => {
|
|
51
|
+
viewer = new EmbeddedViewer(container.value, {
|
|
52
|
+
backgroundColor: new RGBAColor(59, 68, 76, 0),
|
|
53
|
+
defaultColor: new RGBColor(65, 131, 196),
|
|
54
|
+
edgeSettings: new EdgeSettings(false, new RGBColor(0, 0, 0), 1),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
58
|
+
viewer.Resize();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
resizeObserver.observe(container.value);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
defineExpose({
|
|
65
|
+
canHandle,
|
|
66
|
+
open,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<MainLayout :gap="false" class="main-layout">
|
|
73
|
+
<template #header>
|
|
74
|
+
<TopBar class="navbar" :gap="false">
|
|
75
|
+
<template #left>
|
|
76
|
+
</template>
|
|
77
|
+
<template #center>
|
|
78
|
+
<div class="file-name">{{ entry ? entry.fileName : '' }}</div>
|
|
79
|
+
</template>
|
|
80
|
+
<template #right>
|
|
81
|
+
<Button icon="fa-solid fa-xmark" @click="onClose">{{ tr('main.dialog.close') }}</Button>
|
|
82
|
+
</template>
|
|
83
|
+
</TopBar>
|
|
84
|
+
</template>
|
|
85
|
+
<template #body>
|
|
86
|
+
<div ref="container" class="threed-object-container"></div>
|
|
87
|
+
</template>
|
|
88
|
+
</MainLayout>
|
|
89
|
+
</template>
|
|
90
|
+
|
|
91
|
+
<style scoped>
|
|
92
|
+
|
|
93
|
+
.threed-object-container {
|
|
94
|
+
width: 100%;
|
|
95
|
+
height: 100%;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.main-layout {
|
|
99
|
+
background-color: white;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
</style>
|
package/viewers.js
CHANGED
|
@@ -2,10 +2,12 @@ import GenericViewer from './viewers/GenericViewer.vue';
|
|
|
2
2
|
import ImageViewer from './viewers/ImageViewer.vue';
|
|
3
3
|
import PdfViewer from './viewers/PdfViewer.vue';
|
|
4
4
|
import TextViewer from './viewers/TextViewer.vue';
|
|
5
|
+
import ThreeDViewer from './viewers/ThreeDViewer.vue';
|
|
5
6
|
|
|
6
7
|
export {
|
|
7
8
|
GenericViewer,
|
|
8
9
|
ImageViewer,
|
|
9
10
|
PdfViewer,
|
|
10
11
|
TextViewer,
|
|
12
|
+
ThreeDViewer,
|
|
11
13
|
};
|