quasar-ui-danx 0.4.10 → 0.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.
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: {},