@cloudron/pankow 4.4.12 → 4.4.13

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.
@@ -291,7 +291,7 @@ defineExpose({
291
291
  text-overflow: ellipsis;
292
292
  white-space: nowrap;
293
293
  margin-top: 4px;
294
- font-size: 0.85rem;
294
+ font-size: 1rem;
295
295
  padding: 0 2px;
296
296
  }
297
297
 
@@ -23,8 +23,7 @@ const props = defineProps({
23
23
  align-content: stretch;
24
24
  }
25
25
 
26
- /* spacing between adjacent FormGroups */
27
- .pankow-form-group + .pankow-form-group {
26
+ .pankow-form-group {
28
27
  margin: 6px 0;
29
28
  }
30
29
 
@@ -0,0 +1,118 @@
1
+ <script setup>
2
+
3
+ defineProps({
4
+ orientation: {
5
+ type: String,
6
+ default: 'horizontal'
7
+ },
8
+ opaque: {
9
+ type: Boolean,
10
+ default: false
11
+ },
12
+ dragging: {
13
+ type: Boolean,
14
+ default: false
15
+ },
16
+ contrast: {
17
+ type: String,
18
+ default: 'auto'
19
+ }
20
+ });
21
+
22
+ </script>
23
+
24
+ <template>
25
+ <div
26
+ class="pankow-resize-handle"
27
+ :class="{
28
+ 'pankow-resize-handle-vertical': orientation === 'vertical',
29
+ 'pankow-resize-handle-opaque': opaque,
30
+ 'pankow-resize-handle-dragging': dragging,
31
+ 'pankow-resize-handle-contrast-light': contrast === 'light',
32
+ 'pankow-resize-handle-contrast-dark': contrast === 'dark'
33
+ }"
34
+ >
35
+ <div class="pankow-resize-handle-grip"><span></span><span></span></div>
36
+ </div>
37
+ </template>
38
+
39
+ <style>
40
+
41
+ .pankow-resize-handle {
42
+ background: transparent;
43
+ position: relative;
44
+ flex-shrink: 0;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ }
49
+
50
+ .pankow-resize-handle:not(.pankow-resize-handle-vertical) {
51
+ width: 6px;
52
+ cursor: col-resize;
53
+ }
54
+
55
+ .pankow-resize-handle-vertical {
56
+ height: 6px;
57
+ cursor: row-resize;
58
+ }
59
+
60
+ .pankow-resize-handle-opaque {
61
+ background: var(--pankow-color-background);
62
+ }
63
+
64
+ .pankow-resize-handle:hover,
65
+ .pankow-resize-handle-dragging {
66
+ background: var(--pankow-input-border-color);
67
+ }
68
+
69
+ .pankow-resize-handle-grip {
70
+ position: absolute;
71
+ top: 50%;
72
+ left: 50%;
73
+ transform: translate(-50%, -50%);
74
+ display: flex;
75
+ gap: 2px;
76
+ opacity: 0;
77
+ transition: opacity 0.15s;
78
+ pointer-events: none;
79
+ }
80
+
81
+ .pankow-resize-handle:not(.pankow-resize-handle-vertical) .pankow-resize-handle-grip {
82
+ flex-direction: column;
83
+ }
84
+
85
+ .pankow-resize-handle-vertical .pankow-resize-handle-grip {
86
+ flex-direction: row;
87
+ }
88
+
89
+ .pankow-resize-handle:hover .pankow-resize-handle-grip,
90
+ .pankow-resize-handle-dragging .pankow-resize-handle-grip {
91
+ opacity: 0.5;
92
+ }
93
+
94
+ .pankow-resize-handle-grip span {
95
+ display: block;
96
+ border-radius: 1px;
97
+ background: currentColor;
98
+ }
99
+
100
+ .pankow-resize-handle-contrast-light .pankow-resize-handle-grip span {
101
+ background: var(--pankow-text-color-white);
102
+ }
103
+
104
+ .pankow-resize-handle-contrast-dark .pankow-resize-handle-grip span {
105
+ background: var(--pankow-color-dark);
106
+ }
107
+
108
+ .pankow-resize-handle:not(.pankow-resize-handle-vertical) .pankow-resize-handle-grip span {
109
+ width: 2px;
110
+ height: 16px;
111
+ }
112
+
113
+ .pankow-resize-handle-vertical .pankow-resize-handle-grip span {
114
+ width: 16px;
115
+ height: 2px;
116
+ }
117
+
118
+ </style>
@@ -2,18 +2,34 @@
2
2
 
3
3
  import { ref, useTemplateRef, onMounted, onUnmounted, nextTick } from 'vue';
4
4
  import { onPan } from '../gestures.js';
5
+ import ResizeHandle from './ResizeHandle.vue';
5
6
 
6
- defineProps({
7
+ const props = defineProps({
7
8
  showOpenAction: {
8
9
  type: Boolean,
9
10
  default: true
10
11
  },
12
+ resizable: {
13
+ type: Boolean,
14
+ default: false
15
+ },
16
+ storageKey: {
17
+ type: String,
18
+ default: 'pankow-sidebar-width'
19
+ },
11
20
  });
12
21
 
22
+ const container = useTemplateRef('container');
13
23
  const sideBar = useTemplateRef('sideBar');
14
24
  const backDrop = useTemplateRef('backDrop');
15
25
  const isVisible = ref(false);
16
26
  const dragging = ref(false);
27
+ const resizing = ref(false);
28
+
29
+ const SIDEBAR_MIN_WIDTH = 200;
30
+ const SIDEBAR_MAX_WIDTH = 600;
31
+ let resizeStartX = 0;
32
+ let resizeStartWidth = 0;
17
33
 
18
34
  function open() {
19
35
  isVisible.value = true;
@@ -107,6 +123,62 @@ function dragCancel() {
107
123
  settle(false);
108
124
  }
109
125
 
126
+ function resizeStart(event) {
127
+ if (!props.resizable) return;
128
+ resizing.value = true;
129
+ resizeStartX = event.clientX ?? event.touches?.[0]?.clientX ?? 0;
130
+ resizeStartWidth = container.value ? container.value.offsetWidth : 0;
131
+ document.body.style.cursor = 'col-resize';
132
+ document.body.style.userSelect = 'none';
133
+ event.preventDefault();
134
+ }
135
+
136
+ function resizeMove(event) {
137
+ if (!resizing.value || !container.value) return;
138
+ const x = event.clientX ?? event.touches?.[0]?.clientX ?? 0;
139
+ const width = Math.max(SIDEBAR_MIN_WIDTH, Math.min(SIDEBAR_MAX_WIDTH, resizeStartWidth + (x - resizeStartX)));
140
+ applyWidth(width);
141
+ }
142
+
143
+ function resizeEnd() {
144
+ if (!resizing.value) return;
145
+ resizing.value = false;
146
+ document.body.style.cursor = '';
147
+ document.body.style.userSelect = '';
148
+ saveWidth();
149
+ }
150
+
151
+ function applyWidth(width) {
152
+ if (!container.value) return;
153
+ container.value.style.width = `${width}px`;
154
+ container.value.style.minWidth = `${width}px`;
155
+ }
156
+
157
+ function saveWidth() {
158
+ if (!props.resizable || !container.value || typeof window === 'undefined') return;
159
+ try {
160
+ window.localStorage.setItem(props.storageKey, String(container.value.offsetWidth));
161
+ } catch (e) { /* ignore */ }
162
+ }
163
+
164
+ function restoreWidth() {
165
+ if (!props.resizable || typeof window === 'undefined') return;
166
+ try {
167
+ const stored = window.localStorage.getItem(props.storageKey);
168
+ if (!stored) return;
169
+ const width = parseInt(stored, 10);
170
+ if (Number.isNaN(width)) return;
171
+ applyWidth(Math.max(SIDEBAR_MIN_WIDTH, Math.min(SIDEBAR_MAX_WIDTH, width)));
172
+ } catch (e) { /* ignore */ }
173
+ }
174
+
175
+ function clearResizeWidth() {
176
+ if (container.value) {
177
+ container.value.style.width = '';
178
+ container.value.style.minWidth = '';
179
+ }
180
+ }
181
+
110
182
  let removePans = [];
111
183
  let settleTimer = null;
112
184
  let settleOnEnd = null;
@@ -135,17 +207,30 @@ function teardown() {
135
207
  }
136
208
 
137
209
  function onMediaChange() {
138
- if (media && media.matches) setup();
139
- else teardown();
210
+ if (media && media.matches) {
211
+ setup();
212
+ clearResizeWidth();
213
+ } else {
214
+ teardown();
215
+ }
140
216
  }
141
217
 
142
218
  onMounted(() => {
143
219
  setup();
220
+ restoreWidth();
221
+ document.addEventListener('mousemove', resizeMove);
222
+ document.addEventListener('mouseup', resizeEnd);
223
+ document.addEventListener('touchmove', resizeMove, { passive: false });
224
+ document.addEventListener('touchend', resizeEnd);
144
225
  media && media.addEventListener('change', onMediaChange);
145
226
  });
146
227
 
147
228
  onUnmounted(() => {
148
229
  media && media.removeEventListener('change', onMediaChange);
230
+ document.removeEventListener('mousemove', resizeMove);
231
+ document.removeEventListener('mouseup', resizeEnd);
232
+ document.removeEventListener('touchmove', resizeMove);
233
+ document.removeEventListener('touchend', resizeEnd);
149
234
  teardown();
150
235
  });
151
236
 
@@ -154,7 +239,7 @@ defineExpose({ open, close });
154
239
  </script>
155
240
 
156
241
  <template>
157
- <div class="pankow-sidebar-container" :class="{ 'pankow-sidebar-open': isVisible, 'pankow-sidebar-dragging': dragging }">
242
+ <div class="pankow-sidebar-container" ref="container" :class="{ 'pankow-sidebar-open': isVisible, 'pankow-sidebar-dragging': dragging, 'pankow-sidebar-resizing': resizing }">
158
243
  <div class="pankow-sidebar-backdrop" ref="backDrop" @click="onBackdropClick"></div>
159
244
  <Transition name="pankow-scale">
160
245
  <div class="pankow-sidebar-close-action" v-if="isVisible" tabindex="0" role="button" aria-label="Close sidebar" @click="close()" @keydown.enter="close()" @keydown.space.prevent="close()"><i class="fa-solid fa-xmark"></i></div>
@@ -165,6 +250,15 @@ defineExpose({ open, close });
165
250
  <slot></slot>
166
251
  </div>
167
252
  </div>
253
+ <ResizeHandle
254
+ v-if="resizable"
255
+ class="pankow-sidebar-resize-handle"
256
+ orientation="horizontal"
257
+ contrast="light"
258
+ :dragging="resizing"
259
+ @mousedown="resizeStart"
260
+ @touchstart.prevent="resizeStart"
261
+ />
168
262
  </div>
169
263
  </template>
170
264
 
@@ -173,7 +267,20 @@ defineExpose({ open, close });
173
267
  .pankow-sidebar-container {
174
268
  display: block;
175
269
  height: 100%;
176
- min-width: 220px;
270
+ min-width: 280px;
271
+ position: relative;
272
+ }
273
+
274
+ .pankow-sidebar-resize-handle {
275
+ position: absolute;
276
+ top: 0;
277
+ right: 0;
278
+ bottom: 0;
279
+ z-index: 1;
280
+ }
281
+
282
+ .pankow-sidebar-resizing {
283
+ user-select: none;
177
284
  }
178
285
 
179
286
  .pankow-sidebar {
@@ -278,6 +385,10 @@ defineExpose({ open, close });
278
385
  .pankow-sidebar-close-action {
279
386
  display: block;
280
387
  }
388
+
389
+ .pankow-sidebar-resize-handle {
390
+ display: none;
391
+ }
281
392
  }
282
393
 
283
394
  </style>
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { ref, computed, watch, onMounted, onUnmounted, useTemplateRef } from 'vue';
3
+ import ResizeHandle from './ResizeHandle.vue';
3
4
 
4
5
  const props = defineProps({
5
6
  orientation: {
@@ -142,20 +143,19 @@ const firstStyle = computed(() => {
142
143
  class="pankow-split-layout"
143
144
  :class="{
144
145
  'pankow-split-layout-dragging': isDragging,
145
- 'pankow-split-layout-vertical': !isHorizontal,
146
- 'pankow-split-layout-divider-opaque': opaque
146
+ 'pankow-split-layout-vertical': !isHorizontal
147
147
  }"
148
148
  >
149
149
  <div class="pankow-split-layout-first" :style="firstStyle">
150
150
  <slot name="left" />
151
151
  </div>
152
- <div
153
- class="pankow-split-layout-divider"
152
+ <ResizeHandle
153
+ :orientation="orientation"
154
+ :opaque="opaque"
155
+ :dragging="isDragging"
154
156
  @mousedown="startDrag"
155
157
  @touchstart.prevent="startDrag"
156
- >
157
- <div class="pankow-split-layout-handle"><span></span><span></span></div>
158
- </div>
158
+ />
159
159
  <div class="pankow-split-layout-second">
160
160
  <slot name="right" />
161
161
  </div>
@@ -186,75 +186,6 @@ const firstStyle = computed(() => {
186
186
  min-width: 0;
187
187
  }
188
188
 
189
- .pankow-split-layout-divider {
190
- background: transparent;
191
- position: relative;
192
- flex-shrink: 0;
193
- display: flex;
194
- align-items: center;
195
- justify-content: center;
196
- }
197
-
198
- .pankow-split-layout:not(.pankow-split-layout-vertical) .pankow-split-layout-divider {
199
- width: 6px;
200
- cursor: col-resize;
201
- }
202
-
203
- .pankow-split-layout-vertical .pankow-split-layout-divider {
204
- height: 6px;
205
- cursor: row-resize;
206
- }
207
-
208
- .pankow-split-layout-divider-opaque .pankow-split-layout-divider {
209
- background: var(--pankow-color-background);
210
- }
211
-
212
- .pankow-split-layout-divider:hover,
213
- .pankow-split-layout-dragging .pankow-split-layout-divider {
214
- background: var(--pankow-input-border-color);
215
- }
216
-
217
- .pankow-split-layout-handle {
218
- position: absolute;
219
- top: 50%;
220
- left: 50%;
221
- transform: translate(-50%, -50%);
222
- display: flex;
223
- gap: 2px;
224
- opacity: 0;
225
- transition: opacity 0.15s;
226
- pointer-events: none;
227
- }
228
-
229
- .pankow-split-layout:not(.pankow-split-layout-vertical) .pankow-split-layout-handle {
230
- flex-direction: column;
231
- }
232
-
233
- .pankow-split-layout-vertical .pankow-split-layout-handle {
234
- flex-direction: row;
235
- }
236
-
237
- .pankow-split-layout-divider:hover .pankow-split-layout-handle,
238
- .pankow-split-layout-dragging .pankow-split-layout-handle {
239
- opacity: 0.5;
240
- }
241
-
242
- .pankow-split-layout-handle span {
243
- display: block;
244
- border-radius: 1px;
245
- background: var(--pankow-color-text);
246
- }
247
-
248
- .pankow-split-layout:not(.pankow-split-layout-vertical) .pankow-split-layout-handle span {
249
- width: 2px;
250
- height: 16px;
251
- }
252
-
253
- .pankow-split-layout-vertical .pankow-split-layout-handle span {
254
- width: 16px;
255
- height: 2px;
256
- }
257
-
258
189
  .pankow-split-layout-second {
259
190
  flex: 1;
260
191
  min-width: 0;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudron/pankow",
3
3
  "private": false,
4
- "version": "4.4.12",
4
+ "version": "4.4.13",
5
5
  "description": "Vue 3 UI component library for Cloudron apps",
6
6
  "main": "index.js",
7
7
  "types": "types/index.d.ts",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@fontsource/inter": "^5.3.0",
41
41
  "@fortawesome/fontawesome-free": "^7.3.1",
42
- "filesize": "^11.0.22",
42
+ "filesize": "^11.0.23",
43
43
  "monaco-editor": "^0.56.0",
44
44
  "online-3d-viewer": "^0.18.0"
45
45
  },
@@ -55,7 +55,7 @@
55
55
  "vite": "^8.2.2",
56
56
  "vite-ssg": "^28.3.0",
57
57
  "vue": "^3.5.42",
58
- "vue-router": "^5.2.0"
58
+ "vue-router": "^5.3.1"
59
59
  },
60
60
  "allowScripts": {
61
61
  "core-js": false