quasar-ui-danx 0.4.13 → 0.4.14

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.4.13",
3
+ "version": "0.4.14",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -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
@@ -92,6 +92,7 @@
92
92
  />
93
93
  </div>
94
94
  </template>
95
+ <slot />
95
96
  <div
96
97
  v-if="savedAt"
97
98
  :class="savingClass"
@@ -137,7 +138,7 @@
137
138
  </template>
138
139
  <script setup lang="ts">
139
140
  import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@heroicons/vue/solid";
140
- import { computed, ref } from "vue";
141
+ import { computed, onBeforeUnmount, onMounted, ref } from "vue";
141
142
  import { fDateTime, FlashMessages, incrementName, replace } from "../../../helpers";
142
143
  import { TrashIcon as RemoveIcon } from "../../../svg";
143
144
  import { AnyObject, FormFieldValue, RenderedFormProps } from "../../../types";
@@ -178,15 +179,20 @@ const FORM_FIELD_MAP = {
178
179
 
179
180
  const mappedFields = props.form.fields.map((field) => ({
180
181
  placeholder: `Enter ${field.label}`,
181
- default: field.type === "BOOLEAN" ? false : "",
182
182
  ...field,
183
183
  component: field.component || FORM_FIELD_MAP[field.type]
184
184
  }));
185
185
 
186
186
  const fieldResponses = computed(() => {
187
- if (!props.values) return [];
188
- if (Array.isArray(props.values)) return props.values;
189
- 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
+ }));
190
196
  });
191
197
 
192
198
  const fieldInputs = computed(() => {
@@ -252,11 +258,38 @@ function onInput(name: string, value: any) {
252
258
  updateValues(newValues);
253
259
  }
254
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
+
255
288
  function createVariation(variation) {
256
289
  return props.form.fields.map((field) => ({
257
290
  variation,
258
291
  name: field.name,
259
- value: field.type === "BOOLEAN" ? false : null
292
+ value: field.default_value === undefined ? null : field.default_value
260
293
  }));
261
294
  }
262
295
 
@@ -293,19 +326,6 @@ function onChangeVariationName() {
293
326
  newVariationName.value = "";
294
327
  }
295
328
 
296
- function updateValues(values: FormFieldValue[]) {
297
- let updatedValues: FormFieldValue[] | object = values;
298
-
299
- if (!Array.isArray(props.values)) {
300
- updatedValues = values.reduce((acc: AnyObject, v) => {
301
- acc[v.name] = v.value;
302
- return acc;
303
- }, {});
304
- }
305
-
306
- emit("update:values", updatedValues);
307
- }
308
-
309
329
  function onRemoveVariation(name: string) {
310
330
  if (!name) return;
311
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";