@cloudron/pankow 4.3.5 → 4.4.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.
@@ -56,8 +56,8 @@
56
56
  :show-size="showSize"
57
57
  :show-star="showStar"
58
58
  :show-modified="showModified"
59
- :rename-handler="renameHandler"
60
59
  :share-indicator-property="shareIndicatorProperty"
60
+ :file-drop-indicator-property="fileDropIndicatorProperty"
61
61
  :select-handler="onSelectAndFocus"
62
62
  :drop-handler="onDrop"
63
63
  :can-drop-handler="onCanDropHandler"
@@ -75,8 +75,8 @@
75
75
  :fallbackIcon="fallbackIcon"
76
76
  :item="item"
77
77
  :show-star="showStar"
78
- :rename-handler="renameHandler"
79
78
  :share-indicator-property="shareIndicatorProperty"
79
+ :file-drop-indicator-property="fileDropIndicatorProperty"
80
80
  :select-handler="onSelectAndFocus"
81
81
  :drop-handler="onDrop"
82
82
  :can-drop-handler="onCanDropHandler"
@@ -198,6 +198,10 @@ const props = defineProps({
198
198
  type: String,
199
199
  default: '',
200
200
  },
201
+ fileDropIndicatorProperty: {
202
+ type: String,
203
+ default: '',
204
+ },
201
205
  editable: {
202
206
  type: Boolean,
203
207
  default: true,
@@ -256,12 +260,6 @@ const props = defineProps({
256
260
  console.warn('Missing deleteHandler for DirectoryView');
257
261
  },
258
262
  },
259
- renameHandler: {
260
- type: Function,
261
- default() {
262
- console.warn('Missing renameHandler for DirectoryView');
263
- },
264
- },
265
263
  changeOwnerHandler: {
266
264
  type: Function,
267
265
  default() {
@@ -334,7 +332,7 @@ const props = defineProps({
334
332
  },
335
333
  });
336
334
 
337
- const emit = defineEmits(['selectionChanged', 'item-activated']);
335
+ const emit = defineEmits(['selectionChanged', 'item-activated', 'rename-requested']);
338
336
 
339
337
  const gridBody = useTemplateRef('gridBody');
340
338
  const contextMenu = useTemplateRef('contextMenu');
@@ -604,12 +602,6 @@ function onItemShare() {
604
602
  props.shareHandler(focusItem.value);
605
603
  }
606
604
 
607
- function onItemRenameBegin() {
608
- if (selectedCount.value !== 1) return console.warn('onItemRenameBegin should only be triggered if one item is selected');
609
-
610
- elements[focusItem.value.id]?.onRenameBegin();
611
- }
612
-
613
605
  async function onChangeOwnerDialogSubmit(items, newOwnerUid) {
614
606
  changeOwnerDialog.busy = true;
615
607
  await props.changeOwnerHandler(items, newOwnerUid);
@@ -694,8 +686,9 @@ function onSelectAndFocus(item, event, keepSelected = false) {
694
686
  invalidateSelected();
695
687
  }
696
688
 
697
- const contextMenuBodyModel = [
689
+ const contextMenuBodyModel = ref([
698
690
  {
691
+ id: 'paste-body',
699
692
  label: props.tr('filemanager.list.menu.paste'),
700
693
  icon: 'fa-regular fa-paste',
701
694
  disabled: () => !clipboard.files || !clipboard.files.length,
@@ -704,56 +697,66 @@ const contextMenuBodyModel = [
704
697
  },
705
698
  },
706
699
  {
700
+ id: 'select-all-body',
707
701
  label: props.tr('filemanager.list.menu.selectAll'),
708
702
  icon: 'fa-solid fa-check-double',
709
703
  action: onSelectAll,
710
704
  },
711
705
  {
706
+ id: 'sep-body1',
712
707
  separator: true,
713
708
  visible: () => typeof props.refreshHandler === 'function',
714
709
  },
715
710
  {
711
+ id: 'refresh-body',
716
712
  label: props.tr('filemanager.toolbar.refresh'),
717
713
  icon: 'fa-solid fa-arrow-rotate-right',
718
714
  visible: () => typeof props.refreshHandler === 'function',
719
715
  action: props.refreshHandler,
720
716
  },
721
717
  {
718
+ id: 'sep-body2',
722
719
  separator: true,
723
720
  visible: () => props.showNewFile || props.showNewFolder,
724
721
  },
725
722
  {
723
+ id: 'new-file-body',
726
724
  label: props.tr('filemanager.toolbar.newFile'),
727
725
  icon: 'fa-solid fa-file-circle-plus',
728
726
  action: props.newFileHandler,
729
727
  visible: () => props.showNewFile,
730
728
  },
731
729
  {
730
+ id: 'new-folder-body',
732
731
  label: props.tr('filemanager.toolbar.newFolder'),
733
732
  icon: 'fa-solid fa-folder-plus',
734
733
  action: props.newFolderHandler,
735
734
  visible: () => props.showNewFolder,
736
735
  },
737
736
  {
737
+ id: 'sep-body3',
738
738
  separator: true,
739
739
  visible: () => props.showUploadFile || props.showUploadFolder,
740
740
  },
741
741
  {
742
+ id: 'upload-file-body',
742
743
  label: props.tr('filemanager.toolbar.uploadFile'),
743
744
  icon: 'fa-solid fa-file-arrow-up',
744
745
  action: props.uploadFileHandler,
745
746
  visible: () => props.showUploadFile,
746
747
  },
747
748
  {
749
+ id: 'upload-folder-body',
748
750
  label: props.tr('filemanager.toolbar.uploadFolder'),
749
751
  icon: 'fa-regular fa-folder-open',
750
752
  action: props.uploadFolderHandler,
751
753
  visible: () => props.showUploadFolder,
752
754
  },
753
- ];
755
+ ]);
754
756
 
755
- const contextMenuModel = [
757
+ const contextMenuModel = ref([
756
758
  {
759
+ id: 'open',
757
760
  label: props.tr('filemanager.list.menu.open'),
758
761
  icon: 'fa-regular fa-folder-open',
759
762
  disabled: () => selectedCount.value > 1,
@@ -762,6 +765,7 @@ const contextMenuModel = [
762
765
  },
763
766
  },
764
767
  {
768
+ id: 'sep-actions-top',
765
769
  separator: true,
766
770
  visible: () =>
767
771
  props.showDownload ||
@@ -772,6 +776,7 @@ const contextMenuModel = [
772
776
  props.showSelectAll,
773
777
  },
774
778
  {
779
+ id: 'download',
775
780
  label: props.tr('filemanager.list.menu.download'),
776
781
  icon: 'fa-solid fa-download',
777
782
  action: onItemDownload,
@@ -779,6 +784,7 @@ const contextMenuModel = [
779
784
  disabled: () => (props.multiDownload ? selectedCount.value === 0 : selectedCount.value !== 1),
780
785
  },
781
786
  {
787
+ id: 'share',
782
788
  label: props.tr('filemanager.list.menu.share'),
783
789
  icon: 'fa-solid fa-share-nodes',
784
790
  action: onItemShare,
@@ -786,18 +792,21 @@ const contextMenuModel = [
786
792
  visible: () => props.showShare,
787
793
  },
788
794
  {
795
+ id: 'copy',
789
796
  label: props.tr('filemanager.list.menu.copy'),
790
797
  icon: 'fa-regular fa-copy',
791
798
  action: onItemsCopy,
792
799
  visible: () => props.showCopy,
793
800
  },
794
801
  {
802
+ id: 'cut',
795
803
  label: props.tr('filemanager.list.menu.cut'),
796
804
  icon: 'fa-solid fa-scissors',
797
805
  action: onItemsCut,
798
806
  visible: () => props.showCut,
799
807
  },
800
808
  {
809
+ id: 'paste',
801
810
  label: props.tr('filemanager.list.menu.paste'),
802
811
  icon: 'fa-regular fa-paste',
803
812
  visible: () => props.showPaste,
@@ -811,25 +820,29 @@ const contextMenuModel = [
811
820
  },
812
821
  },
813
822
  {
823
+ id: 'select-all',
814
824
  label: props.tr('filemanager.list.menu.selectAll'),
815
825
  icon: 'fa-solid fa-check-double',
816
826
  visible: () => props.showSelectAll,
817
827
  action: onSelectAll,
818
828
  },
819
829
  {
830
+ id: 'rename',
820
831
  label: props.tr('filemanager.list.menu.rename'),
821
832
  icon: 'fa-regular fa-pen-to-square',
822
- action: onItemRenameBegin,
833
+ action: () => emit('rename-requested', focusItem.value),
823
834
  visible: () => props.showRename,
824
835
  disabled: () => !props.editable || selectedCount.value > 1,
825
836
  },
826
837
  {
838
+ id: 'chown',
827
839
  label: props.tr('filemanager.list.menu.chown'),
828
840
  icon: 'fa-solid fa-user-pen',
829
841
  action: onItemsChangeOwner,
830
842
  visible: () => props.showOwner,
831
843
  },
832
844
  {
845
+ id: 'extract',
833
846
  label: props.tr('filemanager.list.menu.extract'),
834
847
  action: onItemExtract,
835
848
  visible: () => {
@@ -852,10 +865,12 @@ const contextMenuModel = [
852
865
  },
853
866
  },
854
867
  {
868
+ id: 'sep-actions-bottom',
855
869
  separator: true,
856
870
  visible: () => props.showDelete,
857
871
  },
858
872
  {
873
+ id: 'delete',
859
874
  label: props.tr('filemanager.list.menu.delete'),
860
875
  icon: 'fa-regular fa-trash-can',
861
876
  action: () => {
@@ -863,7 +878,7 @@ const contextMenuModel = [
863
878
  },
864
879
  visible: () => props.showDelete,
865
880
  },
866
- ];
881
+ ]);
867
882
 
868
883
  const filteredSortedItems = computed(() => {
869
884
  invalidateSelected();
@@ -902,6 +917,9 @@ const filteredSortedItems = computed(() => {
902
917
  });
903
918
 
904
919
  defineExpose({
920
+ contextMenuModel,
921
+ contextMenuBodyModel,
922
+ focusItem,
905
923
  highlight,
906
924
  highlightByName,
907
925
  });
@@ -1,6 +1,5 @@
1
1
  <template>
2
2
  <div ref="rowWrapper" class="grid-item-wrapper"
3
- :draggable="!rename"
4
3
  @mouseup="onSelect($event)"
5
4
  @drop="onDrop($event)"
6
5
  @dragover="onDragOver($event)"
@@ -19,6 +18,7 @@
19
18
  <template v-if="visible">
20
19
  <img :src="item.previewUrl || item.icon" ref="iconImage" @error="iconError($event)"/>
21
20
  <i v-show="shareIndicatorProperty !== '' && !!item[shareIndicatorProperty]" class="fa-solid fa-share-nodes is-shared"></i>
21
+ <i v-show="fileDropIndicatorProperty !== '' && !!item[fileDropIndicatorProperty]" class="fa-solid fa-cloud-arrow-up is-filedrop"></i>
22
22
  </template>
23
23
  </div>
24
24
 
@@ -26,21 +26,10 @@
26
26
  <div v-if="!visible">
27
27
  {{ item.name }}
28
28
  </div>
29
- <div v-else-if="visible && !rename" class="grid-item-label" :title="item.name + (item.target ? ` → ${item.target}` : '')">
29
+ <div v-else-if="visible" class="grid-item-label" :title="item.name + (item.target ? ` → ${item.target}` : '')">
30
30
  <a v-if="item.href" class="open-action" @dblclick.stop @click="onOpen($event)" :href="item.href">{{ item.name }}</a>
31
31
  <span v-else>{{ item.name }}</span>
32
32
  </div>
33
- <div v-else-if="visible && rename" ref="renameCell" class="grid-item-label grid-item-rename">
34
- <TextInput
35
- v-model="newName"
36
- :disabled="renameBusy"
37
- @blur="onRenameEnd"
38
- @dblclick.stop
39
- @keydown.enter.stop="onRenameSubmit"
40
- @keydown.esc.stop="onRenameEnd"
41
- @keydown.stop
42
- />
43
- </div>
44
33
  </div>
45
34
  </div>
46
35
  </template>
@@ -50,7 +39,6 @@
50
39
  import { ref, onMounted } from 'vue';
51
40
 
52
41
  import Icon from './Icon.vue';
53
- import TextInput from './TextInput.vue';
54
42
 
55
43
  const props = defineProps({
56
44
  item: Object,
@@ -62,13 +50,11 @@ const props = defineProps({
62
50
  type: String,
63
51
  default: '',
64
52
  },
65
- fallbackIcon: String,
66
- renameHandler: {
67
- type: Function,
68
- default() {
69
- console.warn('Missing renameHandler for DirectoryViewGridItem');
70
- },
53
+ fileDropIndicatorProperty: {
54
+ type: String,
55
+ default: '',
71
56
  },
57
+ fallbackIcon: String,
72
58
  starHandler: {
73
59
  type: Function,
74
60
  default() {
@@ -98,12 +84,8 @@ const props = defineProps({
98
84
  const emit = defineEmits(['activated', 'action-menu']);
99
85
 
100
86
  const rowWrapper = ref(null);
101
- const renameCell = ref(null);
102
87
 
103
88
  const visible = ref(false);
104
- const rename = ref(false);
105
- const renameBusy = ref(false);
106
- const newName = ref('');
107
89
  const dropTargetActive = ref(false);
108
90
  const previewRetries = ref(0);
109
91
 
@@ -123,37 +105,6 @@ function onActionMenu(event) {
123
105
  emit('action-menu', props.item, event);
124
106
  }
125
107
 
126
- function onRenameBegin() {
127
- rename.value = true;
128
- newName.value = props.item.name;
129
-
130
- setTimeout(() => {
131
- const elem = renameCell.value?.querySelector('input.pankow-text-input');
132
- if (!elem) return;
133
-
134
- elem.focus();
135
-
136
- if (typeof elem.selectionStart !== 'undefined') {
137
- elem.selectionStart = 0;
138
- elem.selectionEnd = props.item.name.lastIndexOf('.');
139
- }
140
- }, 0);
141
- }
142
-
143
- function onRenameEnd() {
144
- rename.value = false;
145
- renameBusy.value = false;
146
- newName.value = '';
147
- }
148
-
149
- async function onRenameSubmit() {
150
- if (!newName.value) return;
151
-
152
- renameBusy.value = true;
153
- await props.renameHandler(props.item, newName.value);
154
- onRenameEnd();
155
- }
156
-
157
108
  function onOpen(event) {
158
109
  if (event.ctrlKey || event.metaKey) return;
159
110
 
@@ -223,7 +174,6 @@ onMounted(() => {
223
174
 
224
175
  defineExpose({
225
176
  highlight,
226
- onRenameBegin,
227
177
  get $el() {
228
178
  return rowWrapper.value;
229
179
  },
@@ -323,6 +273,13 @@ defineExpose({
323
273
  font-size: 1rem;
324
274
  }
325
275
 
276
+ .is-filedrop {
277
+ position: absolute;
278
+ left: 2px;
279
+ bottom: 2px;
280
+ font-size: 1rem;
281
+ }
282
+
326
283
  .grid-item-label {
327
284
  width: 100%;
328
285
  text-align: center;
@@ -334,18 +291,6 @@ defineExpose({
334
291
  padding: 0 2px;
335
292
  }
336
293
 
337
- .grid-item-rename {
338
- overflow: hidden;
339
- }
340
-
341
- .grid-item-rename :deep(input) {
342
- width: 100%;
343
- box-sizing: border-box;
344
- text-align: center;
345
- font-size: 0.85rem;
346
- padding: 1px 4px;
347
- }
348
-
349
294
  .grid-item-star {
350
295
  position: absolute;
351
296
  top: 4px;
@@ -1,6 +1,5 @@
1
1
  <template>
2
2
  <div ref="rowWrapper" class="row-wrapper"
3
- :draggable="!rename"
4
3
  @mouseup="onSelect($event)"
5
4
  @drop="onDrop($event)"
6
5
  @dragover="onDragOver($event)"
@@ -14,28 +13,17 @@
14
13
  <div v-if="visible" class="col icon">
15
14
  <img :src="item.previewUrl || item.icon" ref="iconImage" @error="iconError($event)"/>
16
15
  <i v-show="shareIndicatorProperty !== '' && !!item[shareIndicatorProperty]" class="fa-solid fa-share-nodes is-shared"></i>
16
+ <i v-show="fileDropIndicatorProperty !== '' && !!item[fileDropIndicatorProperty]" class="fa-solid fa-cloud-arrow-up is-filedrop"></i>
17
17
  </div>
18
18
 
19
19
  <!-- this is just a placeholder label to allow the native browser search to work -->
20
20
  <div v-if="!visible">
21
21
  {{ item.name }}
22
22
  </div>
23
- <div v-else-if="visible && !rename" class="col label">
23
+ <div v-else-if="visible" class="col label">
24
24
  <a v-show="item.href" class="open-action" @dblclick.stop @click="onOpen($event)" :href="item.href">{{ item.name }} {{ item.target ? `-> ${item.target}` : '' }}</a>
25
25
  <span v-show="!item.href">{{ item.name }} {{ item.target ? `→ ${item.target}` : '' }}</span>
26
26
  </div>
27
- <div v-else-if="visible && rename" ref="renameCell" class="col label rename">
28
- <TextInput
29
- ref="renameInput"
30
- v-model="newName"
31
- :disabled="renameBusy"
32
- @blur="onRenameEnd"
33
- @dblclick.stop
34
- @keydown.enter.stop="onRenameSubmit"
35
- @keydown.esc.stop="onRenameEnd"
36
- @keydown.stop
37
- />
38
- </div>
39
27
 
40
28
  <div v-if="visible && showStar" class="col star">
41
29
  <Icon :icon="`${item.star ? 'fa-solid' : 'fa-regular'} fa-star`" class="star-icon" :class="{ 'star-visible': item.star }" @dblclick.stop.prevent @click.stop.prevent="onToggleStar" />
@@ -57,7 +45,6 @@ import { ref, onMounted } from 'vue';
57
45
  import { prettyDate, prettyFileSize } from '../utils.js';
58
46
 
59
47
  import Icon from './Icon.vue';
60
- import TextInput from './TextInput.vue';
61
48
 
62
49
  const props = defineProps({
63
50
  item: Object,
@@ -81,13 +68,11 @@ const props = defineProps({
81
68
  type: String,
82
69
  default: '',
83
70
  },
84
- fallbackIcon: String,
85
- renameHandler: {
86
- type: Function,
87
- default() {
88
- console.warn('Missing renameHandler for DirectoryViewItem');
89
- },
71
+ fileDropIndicatorProperty: {
72
+ type: String,
73
+ default: '',
90
74
  },
75
+ fallbackIcon: String,
91
76
  starHandler: {
92
77
  type: Function,
93
78
  default() {
@@ -117,12 +102,8 @@ const props = defineProps({
117
102
  const emit = defineEmits(['activated', 'action-menu']);
118
103
 
119
104
  const rowWrapper = ref(null);
120
- const renameCell = ref(null);
121
105
 
122
106
  const visible = ref(false);
123
- const rename = ref(false);
124
- const renameBusy = ref(false);
125
- const newName = ref('');
126
107
  const dropTargetActive = ref(false);
127
108
  const previewRetries = ref(0);
128
109
 
@@ -142,37 +123,6 @@ function onActionMenu(event) {
142
123
  emit('action-menu', props.item, event);
143
124
  }
144
125
 
145
- function onRenameBegin() {
146
- rename.value = true;
147
- newName.value = props.item.name;
148
-
149
- setTimeout(() => {
150
- const elem = renameCell.value?.querySelector('input.pankow-text-input');
151
- if (!elem) return;
152
-
153
- elem.focus();
154
-
155
- if (typeof elem.selectionStart !== 'undefined') {
156
- elem.selectionStart = 0;
157
- elem.selectionEnd = props.item.name.lastIndexOf('.');
158
- }
159
- }, 0);
160
- }
161
-
162
- function onRenameEnd() {
163
- rename.value = false;
164
- renameBusy.value = false;
165
- newName.value = '';
166
- }
167
-
168
- async function onRenameSubmit() {
169
- if (!newName.value) return;
170
-
171
- renameBusy.value = true;
172
- await props.renameHandler(props.item, newName.value);
173
- onRenameEnd();
174
- }
175
-
176
126
  function onOpen(event) {
177
127
  if (event.ctrlKey || event.metaKey) return;
178
128
 
@@ -242,7 +192,6 @@ onMounted(() => {
242
192
 
243
193
  defineExpose({
244
194
  highlight,
245
- onRenameBegin,
246
195
  get $el() {
247
196
  return rowWrapper.value;
248
197
  },
@@ -305,6 +254,7 @@ defineExpose({
305
254
  height: 45px;
306
255
  width: 45px;
307
256
  padding: 5px;
257
+ position: relative;
308
258
  }
309
259
 
310
260
  .icon > img {
@@ -341,6 +291,13 @@ defineExpose({
341
291
  font-size: 1.2rem
342
292
  }
343
293
 
294
+ .is-filedrop {
295
+ position: absolute;
296
+ left: 2px;
297
+ bottom: 0;
298
+ font-size: 1.2rem
299
+ }
300
+
344
301
  .label {
345
302
  padding-left: 5px;
346
303
  flex-grow: 1;
@@ -359,10 +316,6 @@ defineExpose({
359
316
  display: block;
360
317
  }
361
318
 
362
- .label.rename > button {
363
- display: block;
364
- }
365
-
366
319
  .size {
367
320
  width: 100px;
368
321
  margin: auto;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudron/pankow",
3
3
  "private": false,
4
- "version": "4.3.5",
4
+ "version": "4.4.0",
5
5
  "description": "Vue 3 UI component library for Cloudron apps",
6
6
  "main": "index.js",
7
7
  "types": "types/index.d.ts",
@@ -37,10 +37,10 @@
37
37
  "author": "",
38
38
  "license": "ISC",
39
39
  "dependencies": {
40
- "@fontsource/inter": "^5.2.8",
40
+ "@fontsource/inter": "^5.3.0",
41
41
  "@fortawesome/fontawesome-free": "^7.3.1",
42
42
  "filesize": "^11.0.22",
43
- "monaco-editor": "^0.55.1",
43
+ "monaco-editor": "^0.56.0",
44
44
  "online-3d-viewer": "^0.18.0"
45
45
  },
46
46
  "peerDependencies": {
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@highlightjs/vue-plugin": "^2.1.0",
51
- "@unhead/vue": "^3.1.8",
51
+ "@unhead/vue": "^3.2.3",
52
52
  "@vitejs/plugin-vue": "^6.0.8",
53
53
  "highlight.js": "^11.11.1",
54
54
  "typescript": "^7.0.2",
@@ -56,5 +56,8 @@
56
56
  "vite-ssg": "^28.3.0",
57
57
  "vue": "^3.5.40",
58
58
  "vue-router": "^5.2.0"
59
+ },
60
+ "allowScripts": {
61
+ "core-js": false
59
62
  }
60
63
  }
package/vite-plugin.js CHANGED
@@ -1,5 +1,6 @@
1
- import { dirname } from 'node:path';
1
+ import { dirname, resolve } from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
+ import { existsSync } from 'node:fs';
3
4
 
4
5
  const __dirname = dirname(fileURLToPath(import.meta.url));
5
6
 
@@ -25,6 +26,15 @@ export default function pankowPlugin() {
25
26
  if (Array.isArray(allow) && !allow.includes(__dirname)) {
26
27
  allow.push(__dirname);
27
28
  }
28
- }
29
+ },
30
+ resolveId(source) {
31
+ if (source.includes('?worker') && source.startsWith('monaco-editor/esm/vs/')) {
32
+ const cleanId = source.replace(/\?worker$/, '');
33
+ const filePath = resolve(__dirname, 'node_modules/', cleanId + '.js');
34
+ if (existsSync(filePath)) {
35
+ return filePath + '?worker';
36
+ }
37
+ }
38
+ },
29
39
  };
30
40
  }