quasar-ui-danx 0.4.29 → 0.4.31

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.29",
3
+ "version": "0.4.31",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -2,17 +2,19 @@
2
2
  <div class="inline-block relative">
3
3
  <div
4
4
  contenteditable
5
- class="relative inline-block transition duration-300 outline-none outline-offset-0 border-none focus:outline-4 hover:outline-4 rounded-sm z-10"
5
+ class="relative inline-block transition duration-300 outline-none outline-offset-0 border-none focus:outline-4 hover:outline-4 rounded-sm z-10 min-w-10 min-h-10"
6
6
  :style="{minWidth, minHeight}"
7
7
  :class="contentClass"
8
8
  @input="onInput"
9
+ @focusin="hasFocus = true"
10
+ @focusout="hasFocus = false"
9
11
  >
10
12
  {{ text }}
11
13
  </div>
12
14
  <div
13
- v-if="!text && placeholder"
15
+ v-if="!text && placeholder && !hasFocus"
14
16
  ref="placeholderDiv"
15
- class="text-gray-600 absolute-top-left whitespace-nowrap z-1"
17
+ class="text-gray-600 absolute-top-left whitespace-nowrap z-1 pointer-events-none"
16
18
  >
17
19
  {{ placeholder }}
18
20
  </div>
@@ -21,15 +23,17 @@
21
23
 
22
24
  <script setup lang="ts">
23
25
  import { useDebounceFn } from "@vueuse/core";
24
- import { computed, onMounted, ref } from "vue";
26
+ import { computed, onMounted, ref, watch } from "vue";
25
27
 
26
28
  const emit = defineEmits(["update:model-value", "change"]);
27
29
  const props = withDefaults(defineProps<{
28
- modelValue: string;
30
+ modelValue?: string;
29
31
  color?: string;
32
+ textColor?: string;
30
33
  debounceDelay?: number;
31
34
  placeholder?: string;
32
35
  }>(), {
36
+ modelValue: "",
33
37
  // NOTE: You must safe-list required colors in tailwind.config.js
34
38
  // Add text-blue-900, hover:bg-blue-200, hover:outline-blue-200, focus:outline-blue-200 and focus:bg-blue-200 for the following config
35
39
  color: "blue-200",
@@ -42,6 +46,7 @@ const text = ref(props.modelValue);
42
46
  const placeholderDiv = ref(null);
43
47
  const minWidth = ref(0);
44
48
  const minHeight = ref(0);
49
+ const hasFocus = ref(false);
45
50
 
46
51
  onMounted(() => {
47
52
  // Set the min-width to the width of the placeholder
@@ -51,6 +56,11 @@ onMounted(() => {
51
56
  }
52
57
  });
53
58
 
59
+ watch(() => props.modelValue, (value) => {
60
+ if (!hasFocus.value)
61
+ text.value = value;
62
+ });
63
+
54
64
  const debouncedChange = useDebounceFn(() => {
55
65
  emit("update:model-value", text.value);
56
66
  emit("change", text.value);
@@ -64,6 +74,7 @@ function onInput(e) {
64
74
  const contentClass = computed(() => [
65
75
  `hover:bg-${props.color} focus:bg-${props.color}`,
66
76
  `hover:text-${props.textColor} focus:text-${props.textColor}`,
67
- `hover:outline-${props.color} focus:outline-${props.color}`
77
+ `hover:outline-${props.color} focus:outline-${props.color}`,
78
+ text.value ? "" : "!bg-none"
68
79
  ]);
69
80
  </script>
@@ -24,7 +24,9 @@
24
24
  <FilePreview
25
25
  v-for="file in uploadedFiles"
26
26
  :key="'file-upload-' + file.id"
27
- class="w-32 h-32 m-2 cursor-pointer bg-gray-200"
27
+ class="m-2 cursor-pointer bg-gray-200"
28
+ :class="filePreviewClass"
29
+ :style="styleSize"
28
30
  :file="file"
29
31
  :related-files="file.transcodes || uploadedFiles"
30
32
  downloadable
@@ -33,14 +35,19 @@
33
35
  />
34
36
  <div
35
37
  v-if="!disable && !readonly"
36
- class="dx-add-remove-files w-32 h-32 m-2 rounded-2xl flex flex-col flex-nowrap items-center overflow-hidden cursor-pointer"
38
+ class="dx-add-remove-files m-2 flex flex-col flex-nowrap items-center overflow-hidden cursor-pointer"
39
+ :class="filePreviewClass"
40
+ :style="styleSize"
37
41
  >
38
42
  <div
39
43
  class="dx-add-file flex-grow p-1 pt-3 flex justify-center items-center bg-green-200 text-green-700 w-full hover:bg-green-100"
40
44
  @click="$refs.file.click()"
41
45
  >
42
46
  <div>
43
- <AddFileIcon class="w-10 m-auto" />
47
+ <AddFileIcon
48
+ class="m-auto"
49
+ :class="addIconClass"
50
+ />
44
51
  <div class="mt-1 text-center">
45
52
  Add
46
53
  </div>
@@ -66,7 +73,7 @@
66
73
  </template>
67
74
 
68
75
  <script setup lang="ts">
69
- import { onMounted } from "vue";
76
+ import { computed, onMounted } from "vue";
70
77
  import { useMultiFileUpload } from "../../../../helpers";
71
78
  import { ImageIcon as AddFileIcon, TrashIcon as RemoveFileIcon } from "../../../../svg";
72
79
  import { FormField, UploadedFile } from "../../../../types";
@@ -74,14 +81,28 @@ import { FilePreview } from "../../../Utility";
74
81
  import FieldLabel from "./FieldLabel";
75
82
 
76
83
  const emit = defineEmits(["update:model-value"]);
77
- const props = defineProps<{
84
+ const props = withDefaults(defineProps<{
78
85
  modelValue?: UploadedFile[];
79
86
  field?: FormField;
80
87
  label?: string;
81
88
  showName?: boolean;
82
89
  disable?: boolean;
83
90
  readonly?: boolean;
84
- }>();
91
+ width?: number | string;
92
+ height?: number | string;
93
+ addIconClass?: string;
94
+ filePreviewClass?: string;
95
+ filePreviewBtnSize?: string;
96
+ }>(), {
97
+ modelValue: null,
98
+ field: null,
99
+ label: "",
100
+ width: 128,
101
+ height: 128,
102
+ addIconClass: "w-10",
103
+ filePreviewClass: "rounded-2xl",
104
+ filePreviewBtnSize: "sm"
105
+ });
85
106
 
86
107
  const { onComplete, onDrop, onFilesSelected, uploadedFiles, clearUploadedFiles, onRemove } = useMultiFileUpload();
87
108
  onMounted(() => {
@@ -90,4 +111,11 @@ onMounted(() => {
90
111
  }
91
112
  });
92
113
  onComplete(() => emit("update:model-value", uploadedFiles.value));
114
+
115
+ const styleSize = computed(() => {
116
+ return {
117
+ width: typeof props.width === "number" ? `${props.width}px` : props.width,
118
+ height: typeof props.height === "number" ? `${props.height}px` : props.height
119
+ };
120
+ });
93
121
  </script>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="relative flex justify-center bg-gray-100 overflow-hidden"
3
+ class="group relative flex justify-center bg-gray-100 overflow-hidden"
4
4
  :class="{'rounded-2xl': !square}"
5
5
  >
6
6
  <template v-if="computedImage">
@@ -91,10 +91,10 @@
91
91
  </slot>
92
92
  </template>
93
93
 
94
- <div class="absolute top-1 right-1 flex items-center justify-between space-x-1">
94
+ <div class="absolute top-1 right-1 flex items-center flex-nowrap justify-between space-x-1 transition-all opacity-0 group-hover:opacity-100">
95
95
  <QBtn
96
96
  v-if="downloadable && computedImage?.url"
97
- size="sm"
97
+ :size="btnSize"
98
98
  class="dx-file-preview-download py-1 px-2 opacity-70 hover:opacity-100"
99
99
  :class="downloadButtonClass"
100
100
  @click.stop="download(computedImage.url)"
@@ -104,7 +104,7 @@
104
104
 
105
105
  <QBtn
106
106
  v-if="removable"
107
- size="sm"
107
+ :size="btnSize"
108
108
  class="dx-file-preview-remove bg-red-900 text-white opacity-50 hover:opacity-100 py-1 px-2"
109
109
  @click.stop="onRemove"
110
110
  >
@@ -158,6 +158,7 @@ export interface FilePreviewProps {
158
158
  removable?: boolean;
159
159
  disabled?: boolean;
160
160
  square?: boolean;
161
+ btnSize?: "xs" | "sm" | "md" | "lg";
161
162
  }
162
163
 
163
164
  const emit = defineEmits(["remove"]);
@@ -172,7 +173,8 @@ const props = withDefaults(defineProps<FilePreviewProps>(), {
172
173
  downloadable: false,
173
174
  removable: false,
174
175
  disabled: false,
175
- square: false
176
+ square: false,
177
+ btnSize: "sm"
176
178
  });
177
179
 
178
180
 
@@ -166,6 +166,10 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionGlobal
166
166
  result.item = storeObject(result.item);
167
167
  }
168
168
 
169
+ if (result?.result?.__type) {
170
+ result.result = storeObject(result.result);
171
+ }
172
+
169
173
  return result;
170
174
  }
171
175
 
@@ -79,6 +79,12 @@ export function storeObject<T extends TypedObject>(newObject: T, recentlyStoredO
79
79
  return reactiveObject;
80
80
  }
81
81
 
82
+ /**
83
+ * Auto refresh an object based on a condition and a callback. Returns the timeout ID for the auto-refresh.
84
+ * NOTE: Use the timeout ID to clear the auto-refresh when the object is no longer needed (eg: when the component is unmounted)
85
+ */
86
+ const registeredAutoRefreshes: AnyObject = {};
87
+
82
88
  export async function autoRefreshObject<T extends TypedObject>(object: T, condition: (object: T) => boolean, callback: (object: T) => Promise<T>, interval = 3000) {
83
89
  if (!object?.id || !object?.__type) {
84
90
  throw new Error("Invalid stored object. Cannot auto-refresh");
@@ -88,11 +94,21 @@ export async function autoRefreshObject<T extends TypedObject>(object: T, condit
88
94
  const refreshedObject = await callback(object);
89
95
 
90
96
  if (!refreshedObject.id) {
91
- return FlashMessages.error(`Failed to refresh ${object.__type} (${object.id}) status: ` + object.name);
97
+ FlashMessages.error(`Failed to refresh ${object.__type} (${object.id}) status: ` + object.name);
98
+ return null;
92
99
  }
93
100
 
94
101
  storeObject(refreshedObject);
95
102
  }
96
103
 
97
- setTimeout(() => autoRefreshObject(object, condition, callback), interval);
104
+
105
+ const timeoutId = setTimeout(() => autoRefreshObject(object, condition, callback, interval), interval);
106
+
107
+ registeredAutoRefreshes[object.__type + ":" + object.id] = timeoutId;
108
+ }
109
+
110
+ export async function stopAutoRefreshObject<T extends TypedObject>(object: T) {
111
+ const timeoutId = registeredAutoRefreshes[object.__type + ":" + object.id];
112
+
113
+ timeoutId && clearTimeout(timeoutId);
98
114
  }
@@ -12,6 +12,13 @@ export function sleep(delay: number) {
12
12
  return new Promise((resolve) => setTimeout(resolve, delay));
13
13
  }
14
14
 
15
+ /**
16
+ * Deep clone an object
17
+ */
18
+ export function cloneDeep(obj: any) {
19
+ return JSON.parse(JSON.stringify(obj));
20
+ }
21
+
15
22
  /**
16
23
  * Poll a callback function until the result is true
17
24
  */
@@ -20,7 +20,7 @@ export interface FilterGroup {
20
20
  }
21
21
 
22
22
  export interface ListControlsRoutes<T = ActionTargetItem> {
23
- list(pager?: ListControlsPagination): Promise<T[]>;
23
+ list(pager?: ListControlsPagination): Promise<PagedItems>;
24
24
 
25
25
  summary?(filter?: ListControlsFilter): Promise<AnyObject>;
26
26