@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.
@@ -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 }" @click="onClick" @keydown.enter="onClick" :href="disabled ? null : href" :target="target" tabindex="0">
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'] }" />
@@ -69,7 +69,7 @@ const props = defineProps({
69
69
  });
70
70
 
71
71
  const openEventTimeStamp = ref(0);
72
- const forElement = ref(null);
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.value = element;
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.value) {
269
- left = forElement.value.getBoundingClientRect().left + (forElement.value.getBoundingClientRect().width - width);
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.5.18",
4
+ "version": "3.6.0",
5
5
  "description": "",
6
6
  "main": "index.js",
7
7
  "types": "types/index.d.ts",
8
- "files": ["*.css", "*.js", "components", "viewers", "types"],
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.2",
32
+ "@vitejs/plugin-vue": "^6.0.3",
26
33
  "typescript": "^5.9.3",
27
- "vite": "^7.2.7",
28
- "vue": "^3.5.25"
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
- delete tooltips[key];
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, value, modifiers, tooltip) {
53
- if (!tooltip) return;
52
+ function update(target, modifiers, key) {
53
+ if (!tooltips[key].element) return;
54
54
 
55
- tooltip.innerText = value;
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 = tooltip.getBoundingClientRect();
61
+ const tooltipRect = tooltips[key].element.getBoundingClientRect();
62
62
 
63
- if (pos === TOP || pos === BOTTOM) tooltip.style.left = (targetRect.left + targetRect.width/2) - (tooltipRect.width/2) + 'px';
64
- else if (pos === LEFT) tooltip.style.left = (targetRect.left - tooltipRect.width - padding) + 'px';
65
- else tooltip.style.left = (targetRect.left + targetRect.width + padding) + 'px';
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) tooltip.style.top = (targetRect.top + targetRect.height/2) - (tooltipRect.height/2) + 'px';
68
- else if (pos === TOP) tooltip.style.top = (targetRect.top - tooltipRect.height - padding) + 'px';
69
- else tooltip.style.top = (targetRect.bottom + padding) + 'px';
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 (!binding.value) return remove(key);
80
+ if (!tooltips[key].value) return;
77
81
 
78
- const tooltip = document.createElement('div');
79
- tooltips[key] = tooltip;
82
+ const element = document.createElement('div');
83
+ tooltips[key].element = element;
80
84
 
81
- tooltip.setAttribute('id', key);
82
- tooltip.setAttribute('role', 'tooltip');
83
- tooltip.setAttribute('aria-hidden', 'false');
84
- tooltip.classList.add('pankow-tooltip');
85
- window.document.body.appendChild(tooltip);
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.value, binding.modifiers, tooltip);
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) return remove(vnode.ctx.uid, el);
105
- update(el, binding.value, binding.modifiers, tooltips[vnode.ctx.uid]);
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
  };