quasar-ui-danx 0.4.27 → 0.4.29

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 (51) hide show
  1. package/README.md +35 -35
  2. package/dist/danx.es.js +24686 -24119
  3. package/dist/danx.es.js.map +1 -1
  4. package/dist/danx.umd.js +109 -109
  5. package/dist/danx.umd.js.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/package.json +1 -1
  8. package/src/components/ActionTable/ActionTable.vue +29 -7
  9. package/src/components/ActionTable/Filters/FilterableField.vue +14 -2
  10. package/src/components/ActionTable/Form/ActionForm.vue +17 -12
  11. package/src/components/ActionTable/Form/Fields/DateField.vue +24 -20
  12. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +57 -53
  13. package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +9 -2
  14. package/src/components/ActionTable/Form/Fields/EditableDiv.vue +51 -21
  15. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +1 -1
  16. package/src/components/ActionTable/Form/Fields/SelectField.vue +27 -6
  17. package/src/components/ActionTable/Form/Fields/SelectOrCreateField.vue +56 -0
  18. package/src/components/ActionTable/Form/Fields/TextField.vue +2 -0
  19. package/src/components/ActionTable/Form/Fields/index.ts +1 -0
  20. package/src/components/ActionTable/Form/RenderedForm.vue +7 -20
  21. package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +1 -1
  22. package/src/components/ActionTable/Form/Utilities/SaveStateIndicator.vue +37 -0
  23. package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
  24. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +20 -23
  25. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +44 -36
  26. package/src/components/ActionTable/{listControls.ts → controls.ts} +13 -9
  27. package/src/components/ActionTable/index.ts +1 -1
  28. package/src/components/DragAndDrop/ListItemDraggable.vue +45 -31
  29. package/src/components/DragAndDrop/dragAndDrop.ts +221 -220
  30. package/src/components/DragAndDrop/listDragAndDrop.ts +269 -227
  31. package/src/components/PanelsDrawer/PanelsDrawer.vue +7 -7
  32. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +3 -3
  33. package/src/components/Utility/Buttons/ShowHideButton.vue +86 -0
  34. package/src/components/Utility/Buttons/index.ts +1 -0
  35. package/src/components/Utility/Dialogs/ActionFormDialog.vue +30 -0
  36. package/src/components/Utility/Dialogs/CreateNewWithNameDialog.vue +26 -0
  37. package/src/components/Utility/Dialogs/RenderedFormDialog.vue +50 -0
  38. package/src/components/Utility/Dialogs/index.ts +3 -0
  39. package/src/helpers/FileUpload.ts +4 -4
  40. package/src/helpers/actions.ts +84 -20
  41. package/src/helpers/files.ts +56 -43
  42. package/src/helpers/formats.ts +23 -20
  43. package/src/helpers/objectStore.ts +24 -12
  44. package/src/types/actions.d.ts +50 -26
  45. package/src/types/controls.d.ts +23 -25
  46. package/src/types/fields.d.ts +1 -0
  47. package/src/types/files.d.ts +2 -2
  48. package/src/types/index.d.ts +5 -0
  49. package/src/types/shared.d.ts +9 -0
  50. package/src/types/tables.d.ts +3 -3
  51. package/types/vue-shims.d.ts +3 -2
@@ -23,6 +23,7 @@
23
23
  :disable="disabled"
24
24
  :label-slot="!noLabel"
25
25
  :input-class="inputClass"
26
+ :rows="rows"
26
27
  :class="{'dx-input-prepend-label': prependLabel}"
27
28
  stack-label
28
29
  :type="type"
@@ -71,6 +72,7 @@ withDefaults(defineProps<TextFieldProps>(), {
71
72
  labelClass: "",
72
73
  inputClass: "",
73
74
  maxLength: null,
75
+ rows: 3,
74
76
  debounce: 0,
75
77
  placeholder: null
76
78
  });
@@ -19,6 +19,7 @@ export { default as NumberField } from "./NumberField.vue";
19
19
  export { default as NumberRangeField } from "./NumberRangeField.vue";
20
20
  export { default as SelectDrawer } from "./SelectDrawer.vue";
21
21
  export { default as SelectField } from "./SelectField.vue";
22
+ export { default as SelectOrCreateField } from "./SelectOrCreateField.vue";
22
23
  export { default as SelectWithChildrenField } from "./SelectWithChildrenField.vue";
23
24
  export { default as SingleFileField } from "./SingleFileField.vue";
24
25
  export { default as SliderNumberField } from "./SliderNumberField.vue";
@@ -93,25 +93,11 @@
93
93
  </div>
94
94
  </template>
95
95
  <slot />
96
- <div
97
- v-if="savedAt"
98
- :class="savingClass"
99
- class="dx-saving-indicator flex items-center flex-nowrap"
100
- >
101
- <slot
102
- v-if="saving"
103
- name="saving"
104
- >
105
- Saving...
106
- <QSpinnerPie class="ml-2" />
107
- </slot>
108
- <slot
109
- v-else
110
- name="saved"
111
- >
112
- Saved at {{ fDateTime(savedAt, { format: "M/d/yy h:mm:ssa" }) }}
113
- </slot>
114
- </div>
96
+ <SaveStateIndicator
97
+ :saving="saving"
98
+ :saved-at="savedAt"
99
+ :saving-class="savingClass"
100
+ />
115
101
  <ConfirmDialog
116
102
  v-if="variationToEdit !== false"
117
103
  title="Change variation name"
@@ -139,7 +125,7 @@
139
125
  <script setup lang="ts">
140
126
  import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@heroicons/vue/solid";
141
127
  import { computed, onBeforeUnmount, onMounted, ref } from "vue";
142
- import { fDateTime, FlashMessages, incrementName, replace } from "../../../helpers";
128
+ import { FlashMessages, incrementName, replace } from "../../../helpers";
143
129
  import { TrashIcon as RemoveIcon } from "../../../svg";
144
130
  import { AnyObject, FormFieldValue, RenderedFormProps } from "../../../types";
145
131
  import { ConfirmDialog, RenderVnode } from "../../Utility";
@@ -154,6 +140,7 @@ import {
154
140
  TextField,
155
141
  WysiwygField
156
142
  } from "./Fields";
143
+ import SaveStateIndicator from "./Utilities/SaveStateIndicator";
157
144
 
158
145
  const props = withDefaults(defineProps<RenderedFormProps>(), {
159
146
  values: null,
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="maxLength"
4
- class="danx-input-chars mt-1"
4
+ class="danx-input-chars"
5
5
  :class="{'danx-input-chars--error': length > maxLength}"
6
6
  >
7
7
  {{ fNumber(length) }} / {{ fNumber(maxLength) }} characters
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <div
3
+ :class="savingClass"
4
+ class="dx-saving-indicator flex items-center flex-nowrap"
5
+ >
6
+ <slot
7
+ v-if="saving"
8
+ name="saving"
9
+ >
10
+ Saving...
11
+ <QSpinnerPie class="ml-2" />
12
+ </slot>
13
+ <slot
14
+ v-else
15
+ name="saved"
16
+ >
17
+ <template v-if="savedAt">
18
+ Saved at {{ fDateTime(savedAt, { format: "M/d/yy h:mm:ssa" }) }}
19
+ </template>
20
+ <template v-else>
21
+ Not saved
22
+ </template>
23
+ </slot>
24
+ </div>
25
+ </template>
26
+ <script setup lang="ts">
27
+ import { fDateTime } from "../../../../helpers";
28
+
29
+ withDefaults(defineProps<{
30
+ saving?: boolean;
31
+ savedAt?: string;
32
+ savingClass?: string;
33
+ }>(), {
34
+ savingClass: "text-sm text-slate-500",
35
+ savedAt: ""
36
+ });
37
+ </script>
@@ -1 +1,2 @@
1
1
  export { default as MaxLengthCounter } from "./MaxLengthCounter.vue";
2
+ export { default as SaveStateIndicator } from "./SaveStateIndicator.vue";
@@ -6,11 +6,13 @@
6
6
  v-if="!hideToolbar"
7
7
  :title="title"
8
8
  :refresh-button="refreshButton"
9
- :actions="actions?.filter(a => a.batch)"
9
+ :create-button="createButton"
10
+ :actions="controller.batchActions"
10
11
  :action-target="controller.selectedRows.value"
11
12
  :exporter="controller.exportList"
12
13
  :loading="controller.isLoadingList.value || controller.isLoadingSummary.value"
13
14
  @refresh="controller.refreshAll"
15
+ @create="controller.action && controller.action('create')"
14
16
  >
15
17
  <template #default>
16
18
  <slot name="action-toolbar" />
@@ -23,7 +25,7 @@
23
25
  v-if="activeFilter"
24
26
  :name="controller.name"
25
27
  :show-filters="showFilters"
26
- :filters="filters"
28
+ :filters="controller.filters?.value"
27
29
  :active-filter="activeFilter"
28
30
  class="dx-action-table-filters"
29
31
  @update:active-filter="controller.setActiveFilter"
@@ -37,11 +39,12 @@
37
39
  :label="controller.label"
38
40
  :name="controller.name"
39
41
  :class="tableClass"
42
+ :menu-actions="controller.menuActions"
40
43
  :summary="controller.summary.value"
41
44
  :loading-list="controller.isLoadingList.value"
42
45
  :loading-summary="controller.isLoadingSummary.value"
43
46
  :paged-items="controller.pagedItems.value"
44
- :columns="columns"
47
+ :columns="controller.columns || []"
45
48
  :selection="selection"
46
49
  @update:selected-rows="controller.setSelectedRows"
47
50
  @update:pagination="controller.setPagination"
@@ -49,11 +52,11 @@
49
52
  </slot>
50
53
  <slot name="panels">
51
54
  <PanelsDrawer
52
- v-if="activeItem && panels"
55
+ v-if="activeItem && controller.panels"
53
56
  :title="panelTitle"
54
57
  :model-value="activePanel"
55
- :active-item="activeItem"
56
- :panels="panels"
58
+ :target="activeItem"
59
+ :panels="controller.panels"
57
60
  @update:model-value="panel => controller.activatePanel(activeItem, panel)"
58
61
  @close="controller.setActiveItem(null)"
59
62
  >
@@ -70,7 +73,7 @@
70
73
  </template>
71
74
  <script setup lang="ts">
72
75
  import { computed } from "vue";
73
- import { ActionController, ActionPanel, FilterGroup, ResourceAction, TableColumn } from "../../../types";
76
+ import { DanxController } from "../../../types";
74
77
  import { PanelsDrawer } from "../../PanelsDrawer";
75
78
  import { PreviousNextControls } from "../../Utility";
76
79
  import ActionTable from "../ActionTable.vue";
@@ -78,30 +81,24 @@ import { CollapsableFiltersSidebar } from "../Filters";
78
81
  import { ActionToolbar } from "../Toolbars";
79
82
 
80
83
  export interface ActionTableLayoutProps {
81
- title?: string;
82
- controller: ActionController;
83
- columns: TableColumn[];
84
- filters?: FilterGroup[];
85
- panels?: ActionPanel[];
86
- actions?: ResourceAction[];
84
+ controller: DanxController;
87
85
  exporter?: () => Promise<void>;
86
+ hideToolbar?: boolean;
88
87
  panelTitleField?: string;
89
- tableClass?: string;
90
88
  refreshButton?: boolean;
91
- showFilters?: boolean;
92
- hideToolbar?: boolean;
89
+ createButton?: boolean;
93
90
  selection?: "multiple" | "single";
91
+ showFilters?: boolean;
92
+ tableClass?: string;
93
+ title?: string;
94
94
  }
95
95
 
96
96
  const props = withDefaults(defineProps<ActionTableLayoutProps>(), {
97
- title: "",
98
- tableClass: "",
99
- selection: "multiple",
100
- filters: undefined,
101
- panels: undefined,
102
- actions: undefined,
103
97
  exporter: undefined,
104
- panelTitleField: ""
98
+ panelTitleField: "",
99
+ selection: "multiple",
100
+ tableClass: "",
101
+ title: ""
105
102
  });
106
103
 
107
104
  const activeFilter = computed(() => props.controller.activeFilter.value);
@@ -1,46 +1,54 @@
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 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
+ <QBtn
13
+ v-if="createButton"
14
+ class="bg-green-900 mr-4 px-4"
15
+ @click="$emit('create')"
16
+ >
17
+ Create
18
+ </QBtn>
19
+ <RefreshButton
20
+ v-if="refreshButton"
21
+ :loading="loading"
22
+ @click="$emit('refresh')"
23
+ />
24
+ <ExportButton
25
+ v-if="exporter"
26
+ :exporter="exporter"
27
+ class="ml-4"
28
+ />
29
+ <ActionMenu
30
+ v-if="actions && actions.length > 0"
31
+ class="ml-4 dx-batch-actions"
32
+ :target="actionTarget"
33
+ :actions="actions"
34
+ />
35
+ <slot name="after" />
36
+ </div>
37
+ </div>
31
38
  </template>
32
39
  <script setup lang="ts">
33
40
  import { ActionTargetItem, AnyObject, ResourceAction } from "../../../types";
34
41
  import { ExportButton, RefreshButton } from "../../Utility";
35
42
  import ActionMenu from "../ActionMenu";
36
43
 
37
- defineEmits(["refresh"]);
44
+ defineEmits(["refresh", "create"]);
38
45
  defineProps<{
39
- title?: string,
40
- actions?: ResourceAction[],
41
- actionTarget?: ActionTargetItem[],
42
- refreshButton?: boolean,
43
- loading?: boolean,
44
- exporter?: (filter?: AnyObject) => void | Promise<void>
46
+ title?: string;
47
+ actions?: ResourceAction[];
48
+ actionTarget?: ActionTargetItem[];
49
+ createButton?: boolean;
50
+ refreshButton?: boolean;
51
+ loading?: boolean;
52
+ exporter?: (filter?: AnyObject) => void | Promise<void>;
45
53
  }>();
46
54
  </script>
@@ -1,20 +1,21 @@
1
- import { computed, Ref, ref, shallowRef, watch } from "vue";
1
+ import { computed, ref, shallowRef, watch } from "vue";
2
2
  import { RouteParams, Router } from "vue-router";
3
3
  import { danxOptions } from "../../config";
4
4
  import { getItem, latestCallOnly, setItem, storeObject, waitForRef } from "../../helpers";
5
5
  import {
6
- ActionController,
7
6
  ActionTargetItem,
8
7
  AnyObject,
9
8
  FilterGroup,
9
+ ListController,
10
10
  ListControlsFilter,
11
11
  ListControlsOptions,
12
12
  ListControlsPagination,
13
- PagedItems
13
+ PagedItems,
14
+ Ref
14
15
  } from "../../types";
15
16
  import { getFilterFromUrl } from "./listHelpers";
16
17
 
17
- export function useListControls(name: string, options: ListControlsOptions): ActionController {
18
+ export function useControls(name: string, options: ListControlsOptions): ListController {
18
19
  let isInitialized = false;
19
20
  const PAGE_SETTINGS_KEY = `dx-${name}-pager`;
20
21
  const pagedItems = shallowRef<PagedItems | null>(null);
@@ -72,12 +73,13 @@ export function useListControls(name: string, options: ListControlsOptions): Act
72
73
 
73
74
  async function loadList() {
74
75
  if (!isInitialized) return;
75
- isLoadingList.value = true;
76
+ // isLoadingList.value = true;
76
77
  try {
77
78
  setPagedItems(await options.routes.list(pager.value));
78
- isLoadingList.value = false;
79
79
  } catch (e) {
80
80
  // Fail silently
81
+ } finally {
82
+ isLoadingList.value = false;
81
83
  }
82
84
  }
83
85
 
@@ -91,9 +93,10 @@ export function useListControls(name: string, options: ListControlsOptions): Act
91
93
  }
92
94
  try {
93
95
  summary.value = await options.routes.summary(summaryFilter);
94
- isLoadingSummary.value = false;
95
96
  } catch (e) {
96
97
  // Fail silently
98
+ } finally {
99
+ isLoadingSummary.value = false;
97
100
  }
98
101
  }
99
102
 
@@ -116,9 +119,10 @@ export function useListControls(name: string, options: ListControlsOptions): Act
116
119
  isLoadingFilters.value = true;
117
120
  try {
118
121
  fieldOptions.value = await options.routes.fieldOptions(activeFilter.value) || {};
119
- isLoadingFilters.value = false;
120
122
  } catch (e) {
121
123
  // Fail silently
124
+ } finally {
125
+ isLoadingFilters.value = false;
122
126
  }
123
127
  }
124
128
 
@@ -490,7 +494,7 @@ export function useListControls(name: string, options: ListControlsOptions): Act
490
494
  activeItem,
491
495
  activePanel,
492
496
 
493
- // Actions
497
+ // List controls
494
498
  initialize,
495
499
  resetPaging,
496
500
  setPagination,
@@ -1,8 +1,8 @@
1
1
  export * from "./Columns";
2
+ export * from "./controls";
2
3
  export * from "./Filters";
3
4
  export * from "./Form";
4
5
  export * from "./Layouts";
5
- export * from "./listControls";
6
6
  export * from "./listHelpers";
7
7
  export * from "./tableColumns";
8
8
  export * from "./Toolbars";
@@ -1,12 +1,16 @@
1
1
  <template>
2
2
  <div
3
- class="cursor-move"
3
+ :class="{'cursor-move': !showHandle}"
4
4
  draggable="true"
5
- @dragstart="dragAndDrop.dragStart"
5
+ @dragstart.stop="dragAndDrop.dragStart"
6
6
  @dragend="dragAndDrop.dragEnd"
7
7
  >
8
- <div class="flex items-center">
9
- <div v-if="showHandle">
8
+ <div :class="contentClass">
9
+ <div
10
+ v-if="showHandle"
11
+ class="cursor-move"
12
+ :class="handleClass"
13
+ >
10
14
  <SvgImg
11
15
  :svg="DragHandleIcon"
12
16
  class="w-4 h-4"
@@ -19,39 +23,49 @@
19
23
  </div>
20
24
  </div>
21
25
  </template>
22
- <script setup>
26
+ <script setup lang="ts">
23
27
  import { DragHandleDotsIcon as DragHandleIcon } from "../../svg";
24
28
  import { SvgImg } from "../Utility";
25
29
  import { ListDragAndDrop } from "./listDragAndDrop";
26
30
 
27
- const emit = defineEmits(["position", "update:list-items"]);
28
- const props = defineProps({
29
- dropZone: {
30
- type: [Function, String],
31
- required: true
32
- },
33
- direction: {
34
- type: String,
35
- default: "vertical",
36
- validator: (value) => ["vertical", "horizontal"].includes(value)
37
- },
38
- showHandle: Boolean,
39
- listItems: {
40
- type: Array,
41
- default: null
42
- }
31
+ const emit = defineEmits(["position", "update:list-items", "drop-zone"]);
32
+ const dragging = defineModel<boolean>();
33
+ const props = withDefaults(defineProps<{
34
+ dropZone: string | (() => string);
35
+ direction?: "vertical" | "horizontal";
36
+ showHandle?: boolean;
37
+ changeDropZone?: boolean;
38
+ contentClass?: string | object;
39
+ handleClass?: string | object;
40
+ listItems?: any[];
41
+ }>(), {
42
+ direction: "vertical",
43
+ handleClass: "",
44
+ contentClass: "flex flex-nowrap items-center",
45
+ listItems: () => []
43
46
  });
44
47
 
45
48
  const dragAndDrop = new ListDragAndDrop()
46
- .setDropZone(props.dropZone)
47
- .setOptions({ showPlaceholder: true, direction: props.direction })
48
- .onPositionChange((newPosition, oldPosition) => {
49
- emit("position", newPosition);
49
+ .setDropZone(props.dropZone)
50
+ .setOptions({ showPlaceholder: true, allowDropZoneChange: props.changeDropZone, direction: props.direction })
51
+ .onStart(() => dragging.value = true)
52
+ .onEnd(() => dragging.value = false)
53
+ .onDropZoneChange((target, dropZone, newPosition, oldPosition, data) => {
54
+ let item = null;
55
+ let items = [];
56
+ if (props.listItems) {
57
+ items = [...props.listItems];
58
+ item = items.splice(oldPosition, 1)[0];
59
+ }
50
60
 
51
- if (props.listItems) {
52
- const items = [...props.listItems];
53
- items.splice(newPosition, 0, items.splice(oldPosition, 1)[0]);
54
- emit("update:list-items", items);
55
- }
56
- });
61
+ emit("drop-zone", { target, item, items, dropZone, oldPosition, newPosition, data });
62
+ })
63
+ .onPositionChange((newPosition, oldPosition) => {
64
+ emit("position", newPosition);
65
+ if (props.listItems) {
66
+ const items = [...props.listItems];
67
+ items.splice(newPosition, 0, items.splice(oldPosition, 1)[0]);
68
+ emit("update:list-items", items);
69
+ }
70
+ });
57
71
  </script>