quasar-ui-danx 0.4.10 → 0.4.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. package/dist/danx.es.js +12389 -7677
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +137 -7
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/index.d.ts +7 -0
  7. package/index.ts +1 -0
  8. package/package.json +10 -4
  9. package/src/components/ActionTable/ActionMenu.vue +26 -31
  10. package/src/components/ActionTable/ActionTable.vue +4 -1
  11. package/src/components/ActionTable/Columns/ActionTableColumn.vue +14 -6
  12. package/src/components/ActionTable/Columns/ActionTableHeaderColumn.vue +63 -42
  13. package/src/components/ActionTable/Form/ActionForm.vue +55 -0
  14. package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +11 -5
  15. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +1 -0
  16. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +1 -1
  17. package/src/components/ActionTable/Form/Fields/NumberField.vue +0 -1
  18. package/src/components/ActionTable/Form/RenderedForm.vue +57 -50
  19. package/src/components/ActionTable/Form/index.ts +1 -0
  20. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +3 -3
  21. package/src/components/ActionTable/TableSummaryRow.vue +48 -37
  22. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +2 -2
  23. package/src/components/ActionTable/listControls.ts +3 -2
  24. package/src/components/PanelsDrawer/PanelsDrawer.vue +15 -5
  25. package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +3 -1
  26. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +17 -4
  27. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +30 -5
  28. package/src/components/Utility/Files/FilePreview.vue +72 -12
  29. package/src/components/Utility/Popovers/PopoverMenu.vue +34 -29
  30. package/src/components/Utility/Tools/RenderVnode.vue +5 -1
  31. package/src/config/index.ts +2 -1
  32. package/src/helpers/FileUpload.ts +59 -8
  33. package/src/helpers/actions.ts +27 -27
  34. package/src/helpers/date.ts +2 -2
  35. package/src/helpers/download.ts +8 -2
  36. package/src/helpers/formats.ts +52 -5
  37. package/src/helpers/multiFileUpload.ts +6 -4
  38. package/src/helpers/objectStore.ts +14 -17
  39. package/src/helpers/request.ts +12 -0
  40. package/src/helpers/singleFileUpload.ts +63 -55
  41. package/src/helpers/utils.ts +12 -3
  42. package/src/index.ts +1 -0
  43. package/src/styles/danx.scss +5 -0
  44. package/src/styles/index.scss +1 -0
  45. package/src/styles/themes/danx/action-table.scss +24 -13
  46. package/src/types/actions.d.ts +16 -7
  47. package/src/types/controls.d.ts +4 -4
  48. package/src/types/files.d.ts +10 -5
  49. package/src/types/forms.d.ts +19 -1
  50. package/src/types/index.d.ts +0 -1
  51. package/src/types/requests.d.ts +2 -0
  52. package/src/types/tables.d.ts +28 -22
  53. package/src/{vue-plugin.js → vue-plugin.ts} +5 -4
  54. package/tsconfig.json +1 -0
  55. package/types/index.d.ts +2 -0
@@ -68,10 +68,10 @@
68
68
  </template>
69
69
  <script setup lang="ts">
70
70
  import { computed } from "vue";
71
- import { ActionController, ActionOptions, ActionPanel, FilterGroup, TableColumn } from "../../../types";
71
+ import { ActionController, ActionPanel, FilterGroup, ResourceAction, TableColumn } from "../../../types";
72
72
  import { PanelsDrawer } from "../../PanelsDrawer";
73
73
  import { PreviousNextControls } from "../../Utility";
74
- import ActionTable from "../ActionTable";
74
+ import ActionTable from "../ActionTable.vue";
75
75
  import { CollapsableFiltersSidebar } from "../Filters";
76
76
  import { ActionToolbar } from "../Toolbars";
77
77
 
@@ -83,7 +83,7 @@ const props = defineProps<{
83
83
  columns: TableColumn[];
84
84
  filters?: FilterGroup[];
85
85
  panels?: ActionPanel[];
86
- actions?: ActionOptions[];
86
+ actions?: ResourceAction[];
87
87
  exporter?: () => Promise<void>;
88
88
  panelTitleField?: string;
89
89
  tableClass?: string;
@@ -4,8 +4,8 @@
4
4
  :class="{'has-selection': selectedCount, 'is-loading': loading}"
5
5
  >
6
6
  <QTd
7
- :colspan="stickyColspan"
8
- class="dx-table-summary-td transition-all"
7
+ :colspan="colspan"
8
+ class="dx-table-summary-td dx-table-summary-count transition-all"
9
9
  :class="{'has-selection': selectedCount}"
10
10
  >
11
11
  <div class="flex flex-nowrap items-center">
@@ -36,60 +36,71 @@
36
36
  <QTd
37
37
  v-for="column in summaryColumns"
38
38
  :key="column.name"
39
- :align="column.align || 'left'"
39
+ :align="column.align || 'right'"
40
+ :class="column.summaryClass"
41
+ class="dx-table-summary-fd"
40
42
  >
41
- <template v-if="summary">
43
+ <div
44
+ v-if="summary"
45
+ :class="{'dx-summary-column-link': column.onClick}"
46
+ >
42
47
  {{ formatValue(column) }}
43
- </template>
48
+ </div>
44
49
  </QTd>
45
50
  </QTr>
46
51
  </template>
47
- <script setup>
52
+ <script setup lang="ts">
48
53
  import { XCircleIcon as ClearIcon } from "@heroicons/vue/solid";
49
54
  import { QSpinner, QTd, QTr } from "quasar";
50
55
  import { computed } from "vue";
51
56
  import { fNumber } from "../../helpers";
57
+ import { TableColumn } from "../../types";
58
+
59
+ interface TableSummaryRowProps {
60
+ loading: boolean;
61
+ label?: string;
62
+ selectedLabel?: string;
63
+ selectedCount?: number;
64
+ itemCount?: number;
65
+ summary?: Record<string, any> | null;
66
+ columns: TableColumn[];
67
+ stickyColspan?: number;
68
+ }
52
69
 
53
70
  defineEmits(["clear"]);
54
- const props = defineProps({
55
- loading: Boolean,
56
- label: {
57
- type: String,
58
- default: "Rows"
59
- },
60
- selectedLabel: {
61
- type: String,
62
- default: "Selected"
63
- },
64
- selectedCount: {
65
- type: Number,
66
- default: 0
67
- },
68
- itemCount: {
69
- type: Number,
70
- default: 0
71
- },
72
- summary: {
73
- type: Object,
74
- default: null
75
- },
76
- columns: {
77
- type: Array,
78
- required: true
79
- },
80
- stickyColspan: {
81
- type: Number,
82
- default: 2
71
+ const props = withDefaults(defineProps<TableSummaryRowProps>(), {
72
+ label: "Rows",
73
+ selectedLabel: "Selected",
74
+ selectedCount: 0,
75
+ itemCount: 0,
76
+ summary: null,
77
+ stickyColspan: null
78
+ });
79
+
80
+ // Allow the colspan for the first summary column w/ count + label to extend out to the first column with summary data
81
+ // (ie: take up as much room as possible without affecting the summary columns)
82
+ const colspan = computed(() => {
83
+ if (props.stickyColspan) return props.stickyColspan;
84
+
85
+ if (props.summary) {
86
+ for (let i = 0; i < props.columns.length; i++) {
87
+ const fieldName = props.columns[i].field || props.columns[i].name;
88
+ if (props.summary[fieldName]) {
89
+ return i + 1;
90
+ }
91
+ }
83
92
  }
93
+
94
+ return props.columns.length + 1;
84
95
  });
85
96
 
86
97
  const summaryColumns = computed(() => {
87
98
  // The sticky columns are where we display the selection count and should not be included in the summary columns
88
- return props.columns.slice(props.stickyColspan - 1);
99
+ return props.columns.slice(colspan.value - 1);
89
100
  });
90
101
 
91
102
  function formatValue(column) {
92
- const value = props.summary[column.name];
103
+ const value = props.summary && props.summary[column.name];
93
104
  if (value === undefined) return "";
94
105
 
95
106
  if (column.format) {
@@ -30,14 +30,14 @@
30
30
  </div>
31
31
  </template>
32
32
  <script setup lang="ts">
33
- import { ActionOptions, ActionTargetItem, AnyObject } from "../../../types";
33
+ import { ActionTargetItem, AnyObject, ResourceAction } from "../../../types";
34
34
  import { ExportButton, RefreshButton } from "../../Utility";
35
35
  import ActionMenu from "../ActionMenu";
36
36
 
37
37
  defineEmits(["refresh"]);
38
38
  defineProps<{
39
39
  title?: string,
40
- actions?: ActionOptions[],
40
+ actions?: ResourceAction[],
41
41
  actionTarget?: ActionTargetItem[],
42
42
  refreshButton?: boolean,
43
43
  loading?: boolean,
@@ -317,7 +317,8 @@ export function useListControls(name: string, options: ListControlsOptions): Act
317
317
  // (ie: tasks, verifications, creatives, etc.)
318
318
  if (options.routes.details) {
319
319
  watch(() => activeItem.value, async (newItem, oldItem) => {
320
- if (newItem && oldItem?.id !== newItem.id) {
320
+ // Note we want a loose comparison in case it's a string vs int for the ID
321
+ if (newItem?.id && oldItem?.id != newItem.id) {
321
322
  await getActiveItemDetails();
322
323
  }
323
324
  });
@@ -328,7 +329,7 @@ export function useListControls(name: string, options: ListControlsOptions): Act
328
329
  */
329
330
  function activatePanel(item: ActionTargetItem | null, panel: string = "") {
330
331
  // If we're already on the correct item and panel, don't do anything
331
- if (item?.id === activeItem.value?.id && panel === activePanel.value) return;
332
+ if (item?.id == activeItem.value?.id && panel === activePanel.value) return;
332
333
 
333
334
  setActiveItem(item);
334
335
  activePanel.value = panel;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <ContentDrawer
3
3
  position="right"
4
- :show="true"
4
+ show
5
5
  overlay
6
6
  content-class="h-full"
7
7
  class="dx-panels-drawer"
@@ -13,7 +13,13 @@
13
13
  <div class="dx-panels-drawer-header flex items-center px-6 py-4">
14
14
  <div class="flex-grow">
15
15
  <slot name="header">
16
- <h2>{{ title }}</h2>
16
+ <h2 v-if="title">
17
+ {{ title }}
18
+ </h2>
19
+ <div v-if="!activeItem">
20
+ Loading
21
+ <QSpinnerHourglass />
22
+ </div>
17
23
  </slot>
18
24
  </div>
19
25
  <div
@@ -32,10 +38,14 @@
32
38
  </div>
33
39
  </div>
34
40
  <div class="dx-panels-drawer-body flex-grow overflow-hidden h-full">
35
- <div class="flex items-stretch flex-nowrap h-full">
41
+ <div
42
+ v-if="activeItem.__timestamp > 0"
43
+ class="flex items-stretch flex-nowrap h-full"
44
+ >
36
45
  <PanelsDrawerTabs
37
46
  :key="'pd-tabs:' + activeItem.id"
38
47
  v-model="activePanel"
48
+ :active-item="activeItem"
39
49
  :class="tabsClass"
40
50
  :panels="panels"
41
51
  @update:model-value="$emit('update:model-value', $event)"
@@ -44,6 +54,7 @@
44
54
  :key="'pd-panels:' + activeItem.id"
45
55
  :panels="panels"
46
56
  :active-panel="activePanel"
57
+ :active-item="activeItem"
47
58
  :class="activePanelOptions?.class || panelsClass"
48
59
  />
49
60
  <div
@@ -68,7 +79,7 @@ import PanelsDrawerTabs from "./PanelsDrawerTabs";
68
79
  export interface Props {
69
80
  title?: string,
70
81
  modelValue?: string | number,
71
- activeItem?: ActionTargetItem;
82
+ activeItem: ActionTargetItem;
72
83
  tabsClass?: string | object,
73
84
  panelsClass?: string | object,
74
85
  panels: ActionPanel[]
@@ -78,7 +89,6 @@ defineEmits(["update:model-value", "close"]);
78
89
  const props = withDefaults(defineProps<Props>(), {
79
90
  title: "",
80
91
  modelValue: null,
81
- activeItem: null,
82
92
  tabsClass: "w-[13.5rem]",
83
93
  panelsClass: "w-[35.5rem]"
84
94
  });
@@ -11,17 +11,19 @@
11
11
  <RenderVnode
12
12
  v-if="panel.vnode"
13
13
  :vnode="panel.vnode"
14
+ :props="activeItem"
14
15
  />
15
16
  </QTabPanel>
16
17
  </QTabPanels>
17
18
  </template>
18
19
 
19
20
  <script setup lang="ts">
20
- import { ActionPanel } from "../../types";
21
+ import { ActionPanel, ActionTargetItem } from "../../types";
21
22
  import { RenderVnode } from "../Utility";
22
23
 
23
24
  defineProps<{
24
25
  activePanel?: string | number,
26
+ activeItem: ActionTargetItem,
25
27
  panels: ActionPanel[]
26
28
  }>();
27
29
  </script>
@@ -9,11 +9,11 @@
9
9
  @update:model-value="$emit('update:model-value', $event)"
10
10
  >
11
11
  <template v-for="panel in panels">
12
- <template v-if="panel.enabled === undefined || !!panel.enabled">
12
+ <template v-if="isEnabled(panel)">
13
13
  <RenderVnode
14
14
  v-if="panel.tabVnode"
15
15
  :key="panel.name"
16
- :vnode="panel.tabVnode(modelValue)"
16
+ :vnode="panel.tabVnode(activeItem, modelValue)"
17
17
  :is-active="modelValue === panel.name"
18
18
  :name="panel.name"
19
19
  :label="panel.label"
@@ -30,19 +30,32 @@
30
30
  </template>
31
31
  <script setup lang="ts">
32
32
  import { QTab } from "quasar";
33
- import { ActionPanel } from "../../types";
33
+ import { ActionPanel, ActionTargetItem } from "../../types";
34
34
  import { RenderVnode } from "../Utility";
35
35
 
36
36
  defineEmits(["update:model-value"]);
37
37
 
38
38
  interface Props {
39
39
  modelValue?: string | number;
40
+ activeItem: ActionTargetItem;
40
41
  panels: ActionPanel[];
41
42
  }
42
43
 
43
- withDefaults(defineProps<Props>(), {
44
+ const props = withDefaults(defineProps<Props>(), {
44
45
  modelValue: "general"
45
46
  });
47
+
48
+ function isEnabled(panel) {
49
+ if (panel.enabled === undefined) return true;
50
+
51
+ if (!panel.enabled) return false;
52
+
53
+ if (typeof panel.enabled === "function") {
54
+ return panel.enabled(props.activeItem);
55
+ }
56
+
57
+ return true;
58
+ }
46
59
  </script>
47
60
 
48
61
  <style lang="scss" module="cls">
@@ -37,17 +37,37 @@
37
37
  </video>
38
38
  </template>
39
39
  <img
40
- v-else
40
+ v-else-if="getPreviewUrl(file)"
41
41
  :alt="file.filename"
42
42
  :src="getPreviewUrl(file)"
43
43
  >
44
+ <div v-else>
45
+ <h3 class="text-center mb-4">
46
+ No Preview Available
47
+ </h3>
48
+ <a
49
+ :href="file.url"
50
+ target="_blank"
51
+ class="text-base"
52
+ >
53
+ {{ file.url }}
54
+ </a>
55
+ </div>
56
+ </div>
57
+
58
+ <div class="text-base text-center py-5 bg-slate-800 opacity-70 text-slate-300 absolute-top hover:opacity-20 transition-all">
59
+ {{ file.filename || file.name }}
44
60
  </div>
45
61
  </QCarouselSlide>
46
62
  </QCarousel>
47
- <CloseIcon
48
- class="absolute top-4 right-4 cursor-pointer text-white w-8 h-8"
63
+ <a
64
+ class="absolute top-0 right-0 text-white flex items-center justify-center w-16 h-16 hover:bg-slate-600 transition-all"
49
65
  @click="$emit('close')"
50
- />
66
+ >
67
+ <CloseIcon
68
+ class="w-8 h-8"
69
+ />
70
+ </a>
51
71
  </div>
52
72
  </QDialog>
53
73
  </template>
@@ -73,8 +93,13 @@ function isVideo(file) {
73
93
  return file.mime?.startsWith("video");
74
94
  }
75
95
 
96
+ function isImage(file) {
97
+ return file.mime?.startsWith("image");
98
+ }
99
+
76
100
  function getPreviewUrl(file) {
77
- return file.optimized?.url || file.blobUrl || file.url;
101
+ // Use the optimized URL first if available. If not, use the URL directly if its an image, otherwise use the thumb URL
102
+ return file.optimized?.url || (isImage(file) ? (file.blobUrl || file.url) : file.thumb?.url);
78
103
  }
79
104
 
80
105
  function getThumbUrl(file) {
@@ -45,6 +45,12 @@
45
45
  v-else
46
46
  class="w-24"
47
47
  />
48
+ <div
49
+ v-if="filename"
50
+ class="text-[.7rem] bg-slate-900 text-slate-300 opacity-80 h-[2.25rem] py-.5 px-1 absolute-bottom"
51
+ >
52
+ {{ filename }}
53
+ </div>
48
54
  </div>
49
55
  </div>
50
56
  <div
@@ -54,15 +60,26 @@
54
60
  <slot name="action-button" />
55
61
  </div>
56
62
  <div
57
- v-if="file && file.progress !== undefined"
58
- class="absolute-bottom w-full"
63
+ v-if="isUploading || transcodingStatus"
64
+ class="absolute-bottom w-full bg-slate-800"
59
65
  >
60
66
  <QLinearProgress
61
- :value="file.progress"
62
- size="15px"
63
- color="green-600"
67
+ :value="isUploading ? file.progress : (transcodingStatus.progress / 100)"
68
+ size="36px"
69
+ :color="isUploading ? 'green-800' : 'blue-800'"
70
+ :animation-speed="transcodingStatus?.estimate_ms || 3000"
64
71
  stripe
65
- />
72
+ >
73
+ <div class="absolute-full flex items-center flex-nowrap text-[.7rem] text-slate-200 justify-start px-1">
74
+ <QSpinnerPie
75
+ class="mr-2 text-slate-50 ml-1"
76
+ size="20"
77
+ />
78
+ <div>
79
+ {{ isUploading ? "Uploading..." : transcodingStatus.message }}
80
+ </div>
81
+ </div>
82
+ </QLinearProgress>
66
83
  </div>
67
84
  </template>
68
85
  <template v-else>
@@ -105,9 +122,9 @@
105
122
  </div>
106
123
 
107
124
  <FullScreenCarouselDialog
108
- v-if="showPreview && !disabled"
109
- :files="relatedFiles || [computedImage]"
110
- :default-slide="relatedFiles ? relatedFiles[0].id : (computedImage?.id || '')"
125
+ v-if="showPreview && !disabled && previewableFiles"
126
+ :files="previewableFiles"
127
+ :default-slide="previewableFiles[0]?.id || ''"
111
128
  @close="showPreview = false"
112
129
  />
113
130
  </div>
@@ -115,12 +132,21 @@
115
132
 
116
133
  <script setup lang="ts">
117
134
  import { DocumentTextIcon as TextFileIcon, DownloadIcon, PlayIcon } from "@heroicons/vue/outline";
118
- import { computed, ComputedRef, ref } from "vue";
119
- import { download } from "../../../helpers";
135
+ import { computed, ComputedRef, onMounted, ref } from "vue";
136
+ import { download, FileUpload } from "../../../helpers";
120
137
  import { ImageIcon, PdfIcon, TrashIcon as RemoveIcon } from "../../../svg";
121
138
  import { UploadedFile } from "../../../types";
122
139
  import { FullScreenCarouselDialog } from "../Dialogs";
123
140
 
141
+ export interface FileTranscode {
142
+ status: "Complete" | "Pending" | "In Progress";
143
+ progress: number;
144
+ estimate_ms: number;
145
+ started_at: string;
146
+ completed_at: string;
147
+ message?: string;
148
+ }
149
+
124
150
  export interface FilePreviewProps {
125
151
  src?: string;
126
152
  file?: UploadedFile;
@@ -149,6 +175,7 @@ const props = withDefaults(defineProps<FilePreviewProps>(), {
149
175
  square: false
150
176
  });
151
177
 
178
+
152
179
  const showPreview = ref(false);
153
180
  const computedImage: ComputedRef<UploadedFile | null> = computed(() => {
154
181
  if (props.file) {
@@ -159,11 +186,19 @@ const computedImage: ComputedRef<UploadedFile | null> = computed(() => {
159
186
  url: props.src,
160
187
  type: "image/" + props.src.split(".").pop()?.toLowerCase(),
161
188
  name: "",
162
- size: 0
189
+ size: 0,
190
+ __type: "BrowserFile"
163
191
  };
164
192
  }
165
193
  return null;
166
194
  });
195
+
196
+ const isUploading = computed(() => !props.file || props.file?.progress !== undefined);
197
+ const previewableFiles: ComputedRef<[UploadedFile | null]> = computed(() => {
198
+ return props.relatedFiles?.length > 0 ? props.relatedFiles : [computedImage.value];
199
+ });
200
+
201
+ const filename = computed(() => computedImage.value?.name || computedImage.value?.filename || "");
167
202
  const mimeType = computed(
168
203
  () => computedImage.value?.type || computedImage.value?.mime || ""
169
204
  );
@@ -179,6 +214,31 @@ const thumbUrl = computed(() => {
179
214
  const isPreviewable = computed(() => {
180
215
  return !!thumbUrl.value || isVideo.value || isImage.value;
181
216
  });
217
+
218
+ /**
219
+ * Resolve the active transcoding operation if there is one, otherwise return null
220
+ */
221
+ const transcodingStatus = computed(() => {
222
+ let status = null;
223
+ const metaTranscodes: FileTranscode[] = props.file?.meta?.transcodes || [];
224
+
225
+ for (let transcodeName of Object.keys(metaTranscodes)) {
226
+ const transcode = metaTranscodes[transcodeName];
227
+ if (!transcode?.completed_at) {
228
+ return { ...transcode, message: `${transcodeName} ${transcode.status}` };
229
+ }
230
+ }
231
+
232
+ return status;
233
+ });
234
+
235
+ // Check for an active transcode and make sure the file is being polled for updates
236
+ onMounted(() => {
237
+ if (transcodingStatus.value) {
238
+ (new FileUpload([])).waitForTranscode(props.file);
239
+ }
240
+ });
241
+
182
242
  const isConfirmingRemove = ref(false);
183
243
  function onRemove() {
184
244
  if (!isConfirmingRemove.value) {
@@ -24,62 +24,67 @@
24
24
  auto-close
25
25
  >
26
26
  <QList>
27
- <template v-for="item in items">
27
+ <template
28
+ v-for="item in items"
29
+ :key="item.name"
30
+ >
28
31
  <a
29
32
  v-if="item.url"
30
- :key="item.url"
31
33
  class="q-item"
32
34
  target="_blank"
33
35
  :href="item.url"
34
36
  :class="item.class"
35
37
  >
36
- {{ item.label }}
38
+ <Component
39
+ :is="item.icon"
40
+ v-if="item.icon"
41
+ :class="item.iconClass"
42
+ class="mr-3 w-4"
43
+ /> {{ item.label }}
37
44
  </a>
38
45
  <QItem
39
46
  v-else
40
- :key="item.name || item.action"
41
47
  clickable
42
48
  :class="item.class"
43
49
  @click="onAction(item)"
44
50
  >
45
- {{ item.label }}
51
+ <Component
52
+ :is="item.icon"
53
+ v-if="item.icon"
54
+ :class="item.iconClass"
55
+ class="mr-3 w-4"
56
+ /> {{ item.label }}
46
57
  </QItem>
47
58
  </template>
48
59
  </QList>
49
60
  </QMenu>
50
61
  </a>
51
62
  </template>
52
- <script setup>
63
+ <script setup lang="ts">
53
64
  import { DotsVerticalIcon as MenuIcon } from "@heroicons/vue/outline";
54
65
  import { QSpinner } from "quasar";
66
+ import { ResourceAction } from "../../../types";
55
67
  import { RenderComponent } from "../Tools";
56
68
 
69
+ export interface PopoverMenuProps {
70
+ items: ResourceAction;
71
+ tooltip?: string;
72
+ disabled?: boolean;
73
+ loading?: boolean;
74
+ loadingComponent?: any;
75
+ }
76
+
57
77
  const emit = defineEmits(["action", "action-item"]);
58
- defineProps({
59
- items: {
60
- type: Array,
61
- required: true,
62
- validator(items) {
63
- return items.every((item) => item.url || item.action || item.name);
64
- }
65
- },
66
- tooltip: {
67
- type: String,
68
- default: null
69
- },
70
- disabled: Boolean,
71
- loading: Boolean,
72
- loadingComponent: {
73
- type: [Function, Object],
74
- default: () => ({
75
- is: QSpinner,
76
- props: { class: "w-4 h-4" }
77
- })
78
- }
78
+ withDefaults(defineProps<PopoverMenuProps>(), {
79
+ tooltip: null,
80
+ loadingComponent: () => ({
81
+ is: QSpinner,
82
+ props: { class: "w-4 h-4" }
83
+ })
79
84
  });
80
85
 
81
86
  function onAction(item) {
82
- emit("action", item.name || item.action);
83
- emit("action-item", item);
87
+ emit("action", item.name || item.action);
88
+ emit("action-item", item);
84
89
  }
85
90
  </script>
@@ -11,7 +11,7 @@ const RenderVnode = (props) => {
11
11
  }
12
12
 
13
13
  if (typeof props.vnode === "function") {
14
- return props.vnode(props.props);
14
+ return props.vnode(props.props, props.params);
15
15
  }
16
16
 
17
17
  return null;
@@ -24,6 +24,10 @@ RenderVnode.props = {
24
24
  props: {
25
25
  type: Object,
26
26
  default: () => ({})
27
+ },
28
+ params: {
29
+ type: Object,
30
+ default: null
27
31
  }
28
32
  };
29
33
  export default RenderVnode;
@@ -11,7 +11,8 @@ export const danxOptions = shallowRef<DanxOptions>({
11
11
  fileUpload: {
12
12
  directory: "file-upload",
13
13
  createPresignedUpload: null,
14
- completePresignedUpload: null
14
+ completePresignedUpload: null,
15
+ refreshFile: null
15
16
  },
16
17
  flashMessages: {
17
18
  default: {},