@cloudron/pankow 3.5.18 → 3.6.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/components/Button.vue +11 -1
- 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'] }" />
|
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.0",
|
|
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.0",
|
|
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
|
};
|