quasar-ui-danx 0.4.27 → 0.4.29

Sign up to get free protection for your applications and to get access to all the features.
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>