quasar-ui-danx 0.4.12 → 0.4.14

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 (32) hide show
  1. package/dist/danx.es.js +10975 -6424
  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/package.json +3 -2
  7. package/src/components/ActionTable/ActionTable.vue +65 -63
  8. package/src/components/ActionTable/Form/ActionForm.vue +18 -12
  9. package/src/components/ActionTable/Form/RenderedForm.vue +86 -60
  10. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +80 -70
  11. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +29 -29
  12. package/src/components/ActionTable/listControls.ts +32 -31
  13. package/src/components/Navigation/NavigationMenu.vue +83 -40
  14. package/src/components/PanelsDrawer/PanelsDrawer.vue +20 -8
  15. package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +3 -1
  16. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +17 -4
  17. package/src/components/Utility/Tools/RenderVnode.vue +5 -1
  18. package/src/components/Utility/Transitions/AutoHeightTransition.vue +21 -0
  19. package/src/components/Utility/Transitions/index.ts +1 -0
  20. package/src/config/index.ts +1 -0
  21. package/src/helpers/actions.ts +31 -20
  22. package/src/helpers/date.ts +2 -2
  23. package/src/helpers/formats.ts +55 -5
  24. package/src/helpers/objectStore.ts +7 -0
  25. package/src/helpers/request.ts +1 -1
  26. package/src/helpers/routes.ts +5 -0
  27. package/src/helpers/utils.ts +3 -3
  28. package/src/types/actions.d.ts +4 -7
  29. package/src/types/config.d.ts +3 -1
  30. package/src/types/controls.d.ts +5 -7
  31. package/src/types/forms.d.ts +20 -1
  32. package/src/types/shared.d.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -57,7 +57,8 @@
57
57
  "danx-icon": "^1.0.2",
58
58
  "exifreader": "^4.21.1",
59
59
  "gsap": "^3.12.5",
60
- "luxon": "^3.4.4"
60
+ "luxon": "^3.4.4",
61
+ "yaml": "^2.4.5"
61
62
  },
62
63
  "browserslist": [
63
64
  "last 4 Chrome versions",
@@ -1,66 +1,66 @@
1
1
  <template>
2
- <div
3
- class="dx-action-table overflow-hidden"
4
- :class="{'dx-no-data': !hasData, 'dx-is-loading': loadingList || loadingSummary, 'dx-is-loading-list': loadingList}"
5
- >
6
- <ActionVnode />
7
- <QTable
8
- ref="actionTable"
9
- :selected="selectedRows"
10
- :pagination="pagination"
11
- :columns="tableColumns"
12
- :loading="loadingList || loadingSummary"
13
- :rows="pagedItems?.data || []"
14
- :binary-state-sort="false"
15
- selection="multiple"
16
- :rows-per-page-options="rowsPerPageOptions"
17
- class="sticky-column sticky-header w-full h-full !border-0"
18
- :color="color"
19
- @update:selected="$emit('update:selected-rows', $event)"
20
- @update:pagination="() => {}"
21
- @request="(e) => $emit('update:pagination', {...e.pagination, __sort: mapSortBy(e.pagination, tableColumns)})"
22
- >
23
- <template #no-data>
24
- <slot name="empty">
25
- <EmptyTableState :text="`There are no ${label.toLowerCase()} matching the applied filter`" />
26
- </slot>
27
- </template>
28
- <template #top-row>
29
- <TableSummaryRow
30
- v-if="hasData"
31
- :label="label"
32
- :item-count="summary?.count || 0"
33
- :selected-count="selectedRows.length"
34
- :sticky-colspan="summaryColSpan"
35
- :loading="loadingSummary"
36
- :summary="summary"
37
- :columns="tableColumns"
38
- @clear="$emit('update:selected-rows', [])"
39
- />
40
- </template>
41
- <template #header-cell="rowProps">
42
- <ActionTableHeaderColumn
43
- v-model="columnSettings"
44
- :row-props="rowProps"
45
- :name="name"
46
- @update:model-value="onUpdateColumnSettings"
47
- />
48
- </template>
49
- <template #body-cell="rowProps">
50
- <ActionTableColumn
51
- :key="rowProps.key"
52
- :row-props="rowProps"
53
- :settings="columnSettings[rowProps.col.name]"
54
- >
55
- <slot
56
- :column-name="rowProps.col.name"
57
- :row="rowProps.row"
58
- :value="rowProps.value"
59
- />
60
- </ActionTableColumn>
61
- </template>
62
- </QTable>
63
- </div>
2
+ <div
3
+ class="dx-action-table overflow-hidden"
4
+ :class="{'dx-no-data': !hasData, 'dx-is-loading': loadingList || loadingSummary, 'dx-is-loading-list': loadingList}"
5
+ >
6
+ <ActionVnode />
7
+ <QTable
8
+ ref="actionTable"
9
+ :selected="selectedRows"
10
+ :pagination="pagination"
11
+ :columns="tableColumns"
12
+ :loading="loadingList || loadingSummary"
13
+ :rows="pagedItems?.data || []"
14
+ :binary-state-sort="false"
15
+ :selection="selection"
16
+ :rows-per-page-options="rowsPerPageOptions"
17
+ class="sticky-column sticky-header w-full h-full !border-0"
18
+ :color="color"
19
+ @update:selected="$emit('update:selected-rows', $event)"
20
+ @update:pagination="() => {}"
21
+ @request="(e) => $emit('update:pagination', {...e.pagination, __sort: mapSortBy(e.pagination, tableColumns)})"
22
+ >
23
+ <template #no-data>
24
+ <slot name="empty">
25
+ <EmptyTableState :text="`There are no ${label.toLowerCase()} matching the applied filter`" />
26
+ </slot>
27
+ </template>
28
+ <template #top-row>
29
+ <TableSummaryRow
30
+ v-if="hasData"
31
+ :label="label"
32
+ :item-count="summary?.count || 0"
33
+ :selected-count="selectedRows.length"
34
+ :sticky-colspan="summaryColSpan"
35
+ :loading="loadingSummary"
36
+ :summary="summary"
37
+ :columns="tableColumns"
38
+ @clear="$emit('update:selected-rows', [])"
39
+ />
40
+ </template>
41
+ <template #header-cell="rowProps">
42
+ <ActionTableHeaderColumn
43
+ v-model="columnSettings"
44
+ :row-props="rowProps"
45
+ :name="name"
46
+ @update:model-value="onUpdateColumnSettings"
47
+ />
48
+ </template>
49
+ <template #body-cell="rowProps">
50
+ <ActionTableColumn
51
+ :key="rowProps.key"
52
+ :row-props="rowProps"
53
+ :settings="columnSettings[rowProps.col.name]"
54
+ >
55
+ <slot
56
+ :column-name="rowProps.col.name"
57
+ :row="rowProps.row"
58
+ :value="rowProps.value"
59
+ />
60
+ </ActionTableColumn>
61
+ </template>
62
+ </QTable>
63
+ </div>
64
64
  </template>
65
65
 
66
66
  <script setup lang="ts">
@@ -89,6 +89,7 @@ export interface Props {
89
89
  columns: TableColumn[];
90
90
  rowsPerPageOptions?: number[];
91
91
  summaryColSpan?: number;
92
+ selection: "multiple" | "single";
92
93
  }
93
94
 
94
95
  const props = withDefaults(defineProps<Props>(), {
@@ -97,7 +98,8 @@ const props = withDefaults(defineProps<Props>(), {
97
98
  summary: null,
98
99
  loadingSummary: false,
99
100
  rowsPerPageOptions: () => [10, 25, 50, 100],
100
- summaryColSpan: null
101
+ summaryColSpan: null,
102
+ selection: "multiple"
101
103
  });
102
104
 
103
105
  const actionTable = ref(null);
@@ -1,16 +1,19 @@
1
1
  <template>
2
- <div>
3
- <RenderedForm
4
- v-bind="renderedFormProps"
5
- v-model:values="input"
6
- empty-value=""
7
- :saving="action.isApplying"
8
- @update:values="action.trigger(target, input)"
9
- />
10
- </div>
2
+ <div>
3
+ <RenderedForm
4
+ v-bind="renderedFormProps"
5
+ v-model:values="input"
6
+ empty-value=""
7
+ :saved-at="target.updated_at"
8
+ :saving="action.isApplying"
9
+ @update:values="action.trigger(target, input)"
10
+ >
11
+ <slot />
12
+ </RenderedForm>
13
+ </div>
11
14
  </template>
12
15
  <script setup lang="ts">
13
- import { ref, watch } from "vue";
16
+ import { Ref, ref, watch } from "vue";
14
17
  import { ActionTargetItem, AnyObject, Form, ResourceAction } from "../../../types";
15
18
  import RenderedForm from "./RenderedForm.vue";
16
19
 
@@ -27,7 +30,10 @@ interface ActionFormProps {
27
30
  savingClass?: string;
28
31
  }
29
32
 
30
- const props = defineProps<ActionFormProps>();
33
+ const props = withDefaults(defineProps<ActionFormProps>(), {
34
+ fieldClass: "",
35
+ savingClass: undefined
36
+ });
31
37
  const renderedFormProps = {
32
38
  form: props.form,
33
39
  noLabel: props.noLabel,
@@ -39,7 +45,7 @@ const renderedFormProps = {
39
45
  savingClass: props.savingClass
40
46
  };
41
47
 
42
- const input: AnyObject = ref({ ...props.target });
48
+ const input: Ref<AnyObject> = ref({ ...props.target });
43
49
  const fieldStatus: AnyObject = {};
44
50
 
45
51
  // Only update field values from target changes when the field is not already being saved
@@ -62,32 +62,37 @@
62
62
  </QTab>
63
63
  </QTabs>
64
64
  </div>
65
- <div
65
+ <template
66
66
  v-for="(field, index) in mappedFields"
67
67
  :key="field.id"
68
- :class="{ 'mt-4': index > 0, [fieldClass]: true }"
69
68
  >
70
- <RenderVnode
71
- v-if="field.vnode"
72
- :vnode="field.vnode"
73
- :field="field"
74
- :props="getVnodeProps(field)"
75
- @update:model-value="onInput(field.name, $event)"
76
- />
77
- <Component
78
- :is="field.component"
79
- :key="field.name + '-' + currentVariation"
80
- :model-value="getFieldValue(field.name)"
81
- :field="field"
82
- :label="field.label || undefined"
83
- :no-label="noLabel"
84
- :show-name="showName"
85
- :clearable="field.clearable || clearable"
86
- :disable="disable"
87
- :readonly="readonly"
88
- @update:model-value="onInput(field.name, $event)"
89
- />
90
- </div>
69
+ <div
70
+ v-show="isFieldEnabled(field)"
71
+ :class="{ 'mt-4': index > 0, [fieldClass]: true }"
72
+ >
73
+ <RenderVnode
74
+ v-if="field.vnode"
75
+ :vnode="field.vnode"
76
+ :props="getVnodeProps(field)"
77
+ :params="fieldInputs"
78
+ @update:model-value="onInput(field.name, $event)"
79
+ />
80
+ <Component
81
+ :is="field.component"
82
+ :key="field.name + '-' + currentVariation"
83
+ :model-value="getFieldValue(field.name)"
84
+ :field="field"
85
+ :label="field.label || undefined"
86
+ :no-label="noLabel"
87
+ :show-name="showName"
88
+ :clearable="field.clearable || clearable"
89
+ :disable="disable"
90
+ :readonly="readonly"
91
+ @update:model-value="onInput(field.name, $event)"
92
+ />
93
+ </div>
94
+ </template>
95
+ <slot />
91
96
  <div
92
97
  v-if="savedAt"
93
98
  :class="savingClass"
@@ -133,10 +138,10 @@
133
138
  </template>
134
139
  <script setup lang="ts">
135
140
  import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@heroicons/vue/solid";
136
- import { computed, ref } from "vue";
141
+ import { computed, onBeforeUnmount, onMounted, ref } from "vue";
137
142
  import { fDateTime, FlashMessages, incrementName, replace } from "../../../helpers";
138
143
  import { TrashIcon as RemoveIcon } from "../../../svg";
139
- import { AnyObject, Form, FormFieldValue } from "../../../types";
144
+ import { AnyObject, FormFieldValue, RenderedFormProps } from "../../../types";
140
145
  import { ConfirmDialog, RenderVnode } from "../../Utility";
141
146
  import {
142
147
  BooleanField,
@@ -150,22 +155,6 @@ import {
150
155
  WysiwygField
151
156
  } from "./Fields";
152
157
 
153
- export interface RenderedFormProps {
154
- values?: FormFieldValue[] | object | null;
155
- form: Form;
156
- noLabel?: boolean;
157
- showName?: boolean;
158
- disable?: boolean;
159
- readonly?: boolean;
160
- saving?: boolean;
161
- clearable?: boolean;
162
- emptyValue?: string | number | boolean;
163
- canModifyVariations?: boolean;
164
- fieldClass?: string;
165
- savingClass?: string;
166
- savedAt?: string;
167
- }
168
-
169
158
  const props = withDefaults(defineProps<RenderedFormProps>(), {
170
159
  values: null,
171
160
  emptyValue: undefined,
@@ -191,16 +180,39 @@ const FORM_FIELD_MAP = {
191
180
  const mappedFields = props.form.fields.map((field) => ({
192
181
  placeholder: `Enter ${field.label}`,
193
182
  ...field,
194
- component: field.component || FORM_FIELD_MAP[field.type],
195
- default: field.type === "BOOLEAN" ? false : ""
183
+ component: field.component || FORM_FIELD_MAP[field.type]
196
184
  }));
197
185
 
198
186
  const fieldResponses = computed(() => {
199
- if (!props.values) return [];
200
- if (Array.isArray(props.values)) return props.values;
201
- return Object.entries(props.values).map(([name, value]) => ({ name, value, variation: "" }));
187
+ const values = props.values;
188
+ if (!values) return [];
189
+ if (Array.isArray(values)) return values;
190
+
191
+ return Object.entries(values).filter((entry) => mappedFields.find(mf => mf.name === entry[0])).map(([name, value]) => ({
192
+ name,
193
+ value,
194
+ variation: ""
195
+ }));
196
+ });
197
+
198
+ const fieldInputs = computed(() => {
199
+ const inputs: AnyObject = {};
200
+ for (const field of mappedFields) {
201
+ inputs[field.name] = getFieldValue(field.name);
202
+ }
203
+ return inputs;
202
204
  });
203
205
 
206
+ function isFieldEnabled(field) {
207
+ if (field.enabled === undefined) return true;
208
+
209
+ if (typeof field.enabled === "function") {
210
+ return field.enabled(fieldInputs.value);
211
+ }
212
+
213
+ return field.enabled;
214
+ }
215
+
204
216
  function getVnodeProps(field) {
205
217
  return {
206
218
  modelValue: getFieldValue(field.name),
@@ -246,11 +258,38 @@ function onInput(name: string, value: any) {
246
258
  updateValues(newValues);
247
259
  }
248
260
 
261
+ function updateValues(values: FormFieldValue[]) {
262
+ let updatedValues: FormFieldValue[] | object = values;
263
+
264
+ if (!Array.isArray(props.values)) {
265
+ updatedValues = values.reduce((acc: AnyObject, v) => {
266
+ acc[v.name] = v.value;
267
+ return acc;
268
+ }, {});
269
+ }
270
+
271
+ emit("update:values", updatedValues);
272
+ }
273
+
274
+ onMounted(() => {
275
+ window.addEventListener("beforeunload", handleBeforeUnload);
276
+ });
277
+ onBeforeUnmount(() => {
278
+ window.removeEventListener("beforeunload", handleBeforeUnload);
279
+ });
280
+
281
+
282
+ function handleBeforeUnload(event: BeforeUnloadEvent) {
283
+ if (props.saving) {
284
+ return event.returnValue = "Changes are currently being saved. If you leave now, you might lose unsaved changes.";
285
+ }
286
+ }
287
+
249
288
  function createVariation(variation) {
250
289
  return props.form.fields.map((field) => ({
251
290
  variation,
252
291
  name: field.name,
253
- value: field.type === "BOOLEAN" ? false : null
292
+ value: field.default_value === undefined ? null : field.default_value
254
293
  }));
255
294
  }
256
295
 
@@ -287,19 +326,6 @@ function onChangeVariationName() {
287
326
  newVariationName.value = "";
288
327
  }
289
328
 
290
- function updateValues(values: FormFieldValue[]) {
291
- let updatedValues: FormFieldValue[] | object = values;
292
-
293
- if (!Array.isArray(props.values)) {
294
- updatedValues = values.reduce((acc: AnyObject, v) => {
295
- acc[v.name] = v.value;
296
- return acc;
297
- }, {});
298
- }
299
-
300
- emit("update:values", updatedValues);
301
- }
302
-
303
329
  function onRemoveVariation(name: string) {
304
330
  if (!name) return;
305
331
 
@@ -1,70 +1,72 @@
1
1
  <template>
2
- <div class="flex flex-grow flex-col flex-nowrap overflow-hidden h-full">
3
- <slot name="top" />
4
- <slot name="toolbar">
5
- <ActionToolbar
6
- :title="title"
7
- :refresh-button="refreshButton"
8
- :actions="actions?.filter(a => a.batch)"
9
- :action-target="controller.selectedRows.value"
10
- :exporter="controller.exportList"
11
- :loading="controller.isLoadingList.value || controller.isLoadingSummary.value"
12
- @refresh="controller.refreshAll"
13
- >
14
- <template #default>
15
- <slot name="action-toolbar" />
16
- </template>
17
- </ActionToolbar>
18
- </slot>
19
- <div class="flex flex-nowrap flex-grow overflow-hidden w-full">
20
- <slot name="filters">
21
- <CollapsableFiltersSidebar
22
- v-if="activeFilter"
23
- :name="controller.name"
24
- :show-filters="showFilters"
25
- :filters="filters"
26
- :active-filter="activeFilter"
27
- class="dx-action-table-filters"
28
- @update:active-filter="controller.setActiveFilter"
29
- />
30
- </slot>
31
- <slot>
32
- <ActionTable
33
- class="flex-grow"
34
- :pagination="controller.pagination.value"
35
- :selected-rows="controller.selectedRows.value"
36
- :label="controller.label"
37
- :name="controller.name"
38
- :class="tableClass"
39
- :summary="controller.summary.value"
40
- :loading-list="controller.isLoadingList.value"
41
- :loading-summary="controller.isLoadingSummary.value"
42
- :paged-items="controller.pagedItems.value"
43
- :columns="columns"
44
- @update:selected-rows="controller.setSelectedRows"
45
- @update:pagination="controller.setPagination"
46
- />
47
- </slot>
48
- <slot name="panels">
49
- <PanelsDrawer
50
- v-if="activeItem && panels"
51
- :title="panelTitle"
52
- :model-value="activePanel"
53
- :active-item="activeItem"
54
- :panels="panels"
55
- @update:model-value="panel => controller.activatePanel(activeItem, panel)"
56
- @close="controller.setActiveItem(null)"
57
- >
58
- <template #controls>
59
- <PreviousNextControls
60
- :is-loading="controller.isLoadingList.value"
61
- @next="controller.getNextItem"
62
- />
63
- </template>
64
- </PanelsDrawer>
65
- </slot>
66
- </div>
67
- </div>
2
+ <div class="flex flex-grow flex-col flex-nowrap overflow-hidden h-full">
3
+ <slot name="top" />
4
+ <slot name="toolbar">
5
+ <ActionToolbar
6
+ v-if="!hideToolbar"
7
+ :title="title"
8
+ :refresh-button="refreshButton"
9
+ :actions="actions?.filter(a => a.batch)"
10
+ :action-target="controller.selectedRows.value"
11
+ :exporter="controller.exportList"
12
+ :loading="controller.isLoadingList.value || controller.isLoadingSummary.value"
13
+ @refresh="controller.refreshAll"
14
+ >
15
+ <template #default>
16
+ <slot name="action-toolbar" />
17
+ </template>
18
+ </ActionToolbar>
19
+ </slot>
20
+ <div class="flex flex-nowrap flex-grow overflow-hidden w-full">
21
+ <slot name="filters">
22
+ <CollapsableFiltersSidebar
23
+ v-if="activeFilter"
24
+ :name="controller.name"
25
+ :show-filters="showFilters"
26
+ :filters="filters"
27
+ :active-filter="activeFilter"
28
+ class="dx-action-table-filters"
29
+ @update:active-filter="controller.setActiveFilter"
30
+ />
31
+ </slot>
32
+ <slot>
33
+ <ActionTable
34
+ class="flex-grow"
35
+ :pagination="controller.pagination.value"
36
+ :selected-rows="controller.selectedRows.value"
37
+ :label="controller.label"
38
+ :name="controller.name"
39
+ :class="tableClass"
40
+ :summary="controller.summary.value"
41
+ :loading-list="controller.isLoadingList.value"
42
+ :loading-summary="controller.isLoadingSummary.value"
43
+ :paged-items="controller.pagedItems.value"
44
+ :columns="columns"
45
+ :selection="selection"
46
+ @update:selected-rows="controller.setSelectedRows"
47
+ @update:pagination="controller.setPagination"
48
+ />
49
+ </slot>
50
+ <slot name="panels">
51
+ <PanelsDrawer
52
+ v-if="activeItem && panels"
53
+ :title="panelTitle"
54
+ :model-value="activePanel"
55
+ :active-item="activeItem"
56
+ :panels="panels"
57
+ @update:model-value="panel => controller.activatePanel(activeItem, panel)"
58
+ @close="controller.setActiveItem(null)"
59
+ >
60
+ <template #controls>
61
+ <PreviousNextControls
62
+ :is-loading="controller.isLoadingList.value"
63
+ @next="controller.getNextItem"
64
+ />
65
+ </template>
66
+ </PanelsDrawer>
67
+ </slot>
68
+ </div>
69
+ </div>
68
70
  </template>
69
71
  <script setup lang="ts">
70
72
  import { computed } from "vue";
@@ -75,10 +77,8 @@ import ActionTable from "../ActionTable.vue";
75
77
  import { CollapsableFiltersSidebar } from "../Filters";
76
78
  import { ActionToolbar } from "../Toolbars";
77
79
 
78
- const props = defineProps<{
80
+ export interface ActionTableLayoutProps {
79
81
  title?: string;
80
- refreshButton: boolean;
81
- showFilters: boolean;
82
82
  controller: ActionController;
83
83
  columns: TableColumn[];
84
84
  filters?: FilterGroup[];
@@ -87,7 +87,17 @@ const props = defineProps<{
87
87
  exporter?: () => Promise<void>;
88
88
  panelTitleField?: string;
89
89
  tableClass?: string;
90
- }>();
90
+ refreshButton?: boolean;
91
+ showFilters?: boolean;
92
+ hideToolbar?: boolean;
93
+ selection?: "multiple" | "single";
94
+ }
95
+
96
+ const props = withDefaults(defineProps<ActionTableLayoutProps>(), {
97
+ title: "",
98
+ tableClass: "",
99
+ selection: "multiple"
100
+ });
91
101
 
92
102
  const activeFilter = computed(() => props.controller.activeFilter.value);
93
103
  const activeItem = computed(() => props.controller.activeItem.value);
@@ -1,33 +1,33 @@
1
1
  <template>
2
- <div class="dx-action-toolbar flex items-center">
3
- <div class="flex-grow px-6">
4
- <slot name="title">
5
- <h4 v-if="title">
6
- {{ title }}
7
- </h4>
8
- </slot>
9
- </div>
10
- <div class="py-3 px-6 flex items-center flex-nowrap">
11
- <slot />
12
- <RefreshButton
13
- v-if="refreshButton"
14
- :loading="loading"
15
- @click="$emit('refresh')"
16
- />
17
- <ExportButton
18
- v-if="exporter"
19
- :exporter="exporter"
20
- class="ml-4"
21
- />
22
- <ActionMenu
23
- v-if="actions && actions.length > 0"
24
- class="ml-4 dx-batch-actions"
25
- :target="actionTarget"
26
- :actions="actions"
27
- />
28
- <slot name="after" />
29
- </div>
30
- </div>
2
+ <div class="dx-action-toolbar flex items-center">
3
+ <div class="flex-grow px-6">
4
+ <slot name="title">
5
+ <h4 v-if="title">
6
+ {{ title }}
7
+ </h4>
8
+ </slot>
9
+ </div>
10
+ <div class="py-3 flex items-center flex-nowrap">
11
+ <slot />
12
+ <RefreshButton
13
+ v-if="refreshButton"
14
+ :loading="loading"
15
+ @click="$emit('refresh')"
16
+ />
17
+ <ExportButton
18
+ v-if="exporter"
19
+ :exporter="exporter"
20
+ class="ml-4"
21
+ />
22
+ <ActionMenu
23
+ v-if="actions && actions.length > 0"
24
+ class="ml-4 dx-batch-actions"
25
+ :target="actionTarget"
26
+ :actions="actions"
27
+ />
28
+ <slot name="after" />
29
+ </div>
30
+ </div>
31
31
  </template>
32
32
  <script setup lang="ts">
33
33
  import { ActionTargetItem, AnyObject, ResourceAction } from "../../../types";