quasar-ui-danx 0.4.41 → 0.4.42

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.4.41",
3
+ "version": "0.4.42",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -2,8 +2,9 @@
2
2
  <QBtn
3
3
  :loading="isSaving"
4
4
  class="shadow-none"
5
- :class="colorClass"
6
- @click="onAction"
5
+ :class="disabled ? 'text-slate-800 bg-slate-500 opacity-50' : colorClass"
6
+ :disable="disabled"
7
+ @click="()=> onAction()"
7
8
  >
8
9
  <div class="flex items-center flex-nowrap">
9
10
  <component
@@ -26,11 +27,36 @@
26
27
  >
27
28
  {{ tooltip }}
28
29
  </QTooltip>
30
+ <QMenu
31
+ v-if="isConfirming"
32
+ :model-value="true"
33
+ >
34
+ <div class="p-4 bg-slate-600">
35
+ <div>{{ confirmText }}</div>
36
+ <div class="flex items-center flex-nowrap mt-2">
37
+ <div class="flex-grow">
38
+ <ActionButton
39
+ type="cancel"
40
+ color="gray"
41
+ @click="isConfirming = false"
42
+ />
43
+ </div>
44
+ <ActionButton
45
+ type="confirm"
46
+ color="green"
47
+ @click="()=> onAction(true)"
48
+ />
49
+ </div>
50
+ </div>
51
+ </QMenu>
29
52
  </QBtn>
30
53
  </template>
31
54
  <script setup lang="ts">
32
55
  import {
33
56
  FaSolidArrowsRotate as RefreshIcon,
57
+ FaSolidCircleCheck as ConfirmIcon,
58
+ FaSolidCircleXmark as CancelIcon,
59
+ FaSolidCopy as CopyIcon,
34
60
  FaSolidPause as PauseIcon,
35
61
  FaSolidPencil as EditIcon,
36
62
  FaSolidPlay as PlayIcon,
@@ -38,11 +64,11 @@ import {
38
64
  FaSolidStop as StopIcon,
39
65
  FaSolidTrash as TrashIcon
40
66
  } from "danx-icon";
41
- import { computed } from "vue";
67
+ import { computed, ref } from "vue";
42
68
  import { ActionTarget, ResourceAction } from "../../../types";
43
69
 
44
70
  export interface ActionButtonProps {
45
- type?: "trash" | "trash-red" | "create" | "edit" | "play" | "stop" | "pause" | "refresh";
71
+ type?: "trash" | "trash-red" | "create" | "edit" | "copy" | "play" | "stop" | "pause" | "refresh" | "confirm" | "cancel";
46
72
  color?: "red" | "blue" | "sky" | "green" | "green-invert" | "lime" | "white" | "gray";
47
73
  icon?: object | string;
48
74
  iconClass?: string;
@@ -52,6 +78,9 @@ export interface ActionButtonProps {
52
78
  action?: ResourceAction;
53
79
  target?: ActionTarget;
54
80
  input?: object;
81
+ disabled?: boolean;
82
+ confirm?: boolean;
83
+ confirmText?: string;
55
84
  }
56
85
 
57
86
  const emit = defineEmits(["success", "error", "always"]);
@@ -64,7 +93,8 @@ const props = withDefaults(defineProps<ActionButtonProps>(), {
64
93
  tooltip: "",
65
94
  action: null,
66
95
  target: null,
67
- input: null
96
+ input: null,
97
+ confirmText: "Are you sure?"
68
98
  });
69
99
 
70
100
  const colorClass = computed(() => {
@@ -101,11 +131,26 @@ const typeOptions = computed(() => {
101
131
  icon: CreateIcon,
102
132
  iconClass: "w-3"
103
133
  };
134
+ case "confirm":
135
+ return {
136
+ icon: ConfirmIcon,
137
+ iconClass: "w-3"
138
+ };
139
+ case "cancel":
140
+ return {
141
+ icon: CancelIcon,
142
+ iconClass: "w-3"
143
+ };
104
144
  case "edit":
105
145
  return {
106
146
  icon: EditIcon,
107
147
  iconClass: "w-3"
108
148
  };
149
+ case "copy":
150
+ return {
151
+ icon: CopyIcon,
152
+ iconClass: "w-3"
153
+ };
109
154
  case "play":
110
155
  return {
111
156
  icon: PlayIcon,
@@ -136,19 +181,27 @@ const typeOptions = computed(() => {
136
181
 
137
182
  const isSaving = computed(() => {
138
183
  if (props.saving) return true;
184
+ if (props.action) {
185
+ return props.action.isApplying;
186
+ }
139
187
  if (props.target) {
140
188
  if (Array.isArray(props.target)) {
141
189
  return props.target.some((t) => t.isSaving);
142
190
  }
143
191
  return props.target.isSaving;
144
192
  }
145
- if (props.action) {
146
- return props.action.isApplying;
147
- }
148
193
  return false;
149
194
  });
150
195
 
151
- function onAction() {
196
+ const isConfirming = ref(false);
197
+ function onAction(isConfirmed = false) {
198
+ // Make sure this action is confirmed if the confirm prop is set
199
+ if (props.confirm && !isConfirmed) {
200
+ isConfirming.value = true;
201
+ return false;
202
+ }
203
+ isConfirming.value = false;
204
+ if (props.disabled) return;
152
205
  if (props.action) {
153
206
  props.action.trigger(props.target, props.input).then(async (response) => {
154
207
  emit("success", typeof response.json === "function" ? await response.json() : response);
@@ -158,6 +211,8 @@ function onAction() {
158
211
  }).finally(() => {
159
212
  emit("always");
160
213
  });
214
+ } else {
215
+ emit("always");
161
216
  }
162
217
  }
163
218
  </script>
@@ -1,3 +1,4 @@
1
+ export { default as ActionButton } from "./ActionButton.vue";
1
2
  export { default as ExportButton } from "./ExportButton.vue";
2
3
  export { default as RefreshButton } from "./RefreshButton.vue";
3
4
  export { default as ShowHideButton } from "./ShowHideButton.vue";
@@ -23,6 +23,7 @@
23
23
  :label="doneText"
24
24
  class="dx-dialog-button dx-dialog-button-done"
25
25
  :class="doneClass"
26
+ :disable="disable"
26
27
  @click="onClose"
27
28
  >
28
29
  <slot name="done-text" />
@@ -39,6 +40,7 @@ import DialogLayout from "./DialogLayout";
39
40
  const emit = defineEmits(["update:model-value", "close"]);
40
41
  defineProps({
41
42
  ...DialogLayout.props,
43
+ disable: Boolean,
42
44
  doneClass: {
43
45
  type: [String, Object],
44
46
  default: ""
@@ -39,18 +39,23 @@
39
39
  >
40
40
  <PdfIcon
41
41
  v-if="isPdf"
42
- class="w-24"
42
+ class="w-3/4"
43
43
  />
44
44
  <TextFileIcon
45
45
  v-else
46
- class="w-24"
46
+ class="w-3/4"
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
+ <template v-if="filename">
49
+ <div
50
+ v-if="showFilename"
51
+ class="text-[.7rem] bg-slate-900 text-slate-300 opacity-80 h-[2.25rem] py-.5 px-1 absolute-bottom"
52
+ >
53
+ {{ filename }}
54
+ </div>
55
+ <QTooltip v-else>
56
+ {{ filename }}
57
+ </QTooltip>
58
+ </template>
54
59
  </div>
55
60
  </div>
56
61
  <div
@@ -152,6 +157,7 @@ export interface FilePreviewProps {
152
157
  file?: UploadedFile;
153
158
  relatedFiles?: UploadedFile[];
154
159
  missingIcon?: any;
160
+ showFilename?: boolean;
155
161
  downloadButtonClass?: string;
156
162
  imageFit?: "cover" | "contain" | "fill" | "none" | "scale-down";
157
163
  downloadable?: boolean;
@@ -89,11 +89,14 @@ export function fDate(dateTime: string | DateTime | null, { empty = "--", format
89
89
  /**
90
90
  * Parses a date string into a Luxon DateTime object
91
91
  */
92
- export function parseDateTime(dateTime: string | DateTime | null): DateTime<boolean> | null {
92
+ export function parseDateTime(dateTime: string | DateTime | number | null): DateTime<boolean> | null {
93
+ if (typeof dateTime === "number") {
94
+ return DateTime.fromMillis(dateTime as number);
95
+ }
93
96
  if (typeof dateTime === "string") {
94
97
  return parseGenericDateTime(dateTime);
95
98
  }
96
- return dateTime || DateTime.fromSQL("0000-00-00 00:00:00");
99
+ return dateTime as DateTime<boolean> || DateTime.fromSQL("0000-00-00 00:00:00");
97
100
  }
98
101
 
99
102
  /**
@@ -193,7 +196,7 @@ export function fSecondsToDuration(seconds: number) {
193
196
  /**
194
197
  * Formats a duration between two date strings in 00h 00m 00s format
195
198
  */
196
- export function fDuration(start: string, end?: string) {
199
+ export function fDuration(start: string | number, end?: string | number) {
197
200
  const endDateTime = end ? parseDateTime(end) : DateTime.now();
198
201
  const diff = endDateTime?.diff(parseDateTime(start) || DateTime.now(), ["hours", "minutes", "seconds"]);
199
202
  if (!diff?.isValid) {
@@ -19,7 +19,7 @@ export const request: RequestApi = {
19
19
 
20
20
  async call(url, options) {
21
21
  options = options || {};
22
- const abortKey = options?.abortOn !== undefined ? options.abortOn : url;
22
+ const abortKey = options?.abortOn !== undefined ? options.abortOn : url + JSON.stringify(options.params || "");
23
23
  const timestamp = new Date().getTime();
24
24
 
25
25
  if (abortKey) {
@@ -42,7 +42,7 @@ export const request: RequestApi = {
42
42
  options.params[key] = JSON.stringify(value);
43
43
  }
44
44
  }
45
-
45
+
46
46
  url += (url.match(/\?/) ? "&" : "?") + new URLSearchParams(options.params).toString();
47
47
  delete options.params;
48
48
  }