quasar-ui-danx 0.4.20 → 0.4.22

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.20",
3
+ "version": "0.4.22",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <QToggle
3
- :data-testid="'boolean-field-' + field.id"
3
+ :data-testid="'boolean-field-' + (name || label)"
4
4
  :model-value="modelValue || (toggleIndeterminate ? modelValue : false)"
5
5
  :disable="disable || readonly"
6
6
  :toggle-indeterminate="toggleIndeterminate"
@@ -8,37 +8,39 @@
8
8
  @update:model-value="$emit('update:model-value', $event)"
9
9
  >
10
10
  <FieldLabel
11
- :field="{...field, label: label || field.label}"
11
+ :label="label || name"
12
+ :name="name"
12
13
  :show-name="showName"
13
14
  :class="labelClass"
14
15
  />
15
16
  </QToggle>
16
17
  </template>
17
18
 
18
- <script setup>
19
+ <script setup lang="ts">
19
20
  import FieldLabel from "./FieldLabel";
20
21
 
21
22
  defineEmits(["update:model-value"]);
22
23
  defineProps({
23
- label: {
24
- type: String,
25
- default: null
26
- },
27
- modelValue: {
28
- type: [Boolean],
29
- default: undefined
30
- },
31
- field: {
32
- type: Object,
33
- required: true
34
- },
35
- labelClass: {
36
- type: String,
37
- default: "text-sm"
38
- },
39
- showName: Boolean,
40
- toggleIndeterminate: Boolean,
41
- disable: Boolean,
42
- readonly: Boolean
24
+ label: {
25
+ type: String,
26
+ default: null
27
+ },
28
+ name: {
29
+ type: String,
30
+ default: null
31
+ },
32
+ required: Boolean,
33
+ modelValue: {
34
+ type: Boolean,
35
+ default: undefined
36
+ },
37
+ labelClass: {
38
+ type: String,
39
+ default: "text-sm"
40
+ },
41
+ showName: Boolean,
42
+ toggleIndeterminate: Boolean,
43
+ disable: Boolean,
44
+ readonly: Boolean
43
45
  });
44
46
  </script>
@@ -1,26 +1,22 @@
1
1
  <template>
2
2
  <NumberField
3
- :field="field"
3
+ v-bind="$props"
4
4
  :precision="0"
5
5
  :model-value="modelValue"
6
- :show-name="showName"
7
6
  @update:model-value="$emit('update:model-value', $event)"
8
7
  />
9
8
  </template>
10
9
 
11
- <script setup>
10
+ <script setup lang="ts">
11
+ import { NumberFieldProps } from "../../../../types";
12
12
  import NumberField from "./NumberField";
13
13
 
14
14
  defineEmits(["update:model-value"]);
15
- defineProps({
16
- modelValue: {
17
- type: [String, Number],
18
- default: null
19
- },
20
- field: {
21
- type: Object,
22
- required: true
23
- },
24
- showName: Boolean
15
+ withDefaults(defineProps<NumberFieldProps>(), {
16
+ modelValue: "",
17
+ label: undefined,
18
+ delay: 1000,
19
+ min: undefined,
20
+ max: undefined
25
21
  });
26
22
  </script>
@@ -4,14 +4,25 @@
4
4
  {{ label }}
5
5
  </div>
6
6
  <div :class="valueClass">
7
- <a
8
- v-if="url"
9
- target="_blank"
10
- :href="url"
11
- :class="valueClass"
12
- >
13
- <slot>{{ formattedValue }}</slot>
14
- </a>
7
+ <template v-if="url">
8
+ <a
9
+ v-if="!isLargeContent"
10
+ target="_blank"
11
+ :href="url"
12
+ >
13
+ <slot>{{ formattedValue }}</slot>
14
+ </a>
15
+ <template v-else>
16
+ <slot>{{ formattedValue }}</slot>
17
+ <a
18
+ target="_blank"
19
+ :href="url"
20
+ class="inline-block ml-2"
21
+ >
22
+ <LinkIcon class="w-4" />
23
+ </a>
24
+ </template>
25
+ </template>
15
26
  <template v-else>
16
27
  <slot>{{ formattedValue }}</slot>
17
28
  </template>
@@ -19,12 +30,13 @@
19
30
  </div>
20
31
  </template>
21
32
  <script setup lang="ts">
33
+ import { FaSolidLink as LinkIcon } from "danx-icon";
22
34
  import { computed } from "vue";
23
35
  import { fBoolean, fNumber } from "../../../../helpers";
24
36
 
25
37
  export interface LabelValueBlockProps {
26
38
  label: string;
27
- value: string | number | boolean;
39
+ value?: string | number | boolean;
28
40
  url?: string;
29
41
  dense?: boolean;
30
42
  nowrap?: boolean;
@@ -36,6 +48,7 @@ const props = withDefaults(defineProps<LabelValueBlockProps>(), {
36
48
  });
37
49
 
38
50
  const valueClass = computed(() => ({ "mt-2": !props.dense, "mt-1": props.dense, "text-no-wrap": props.nowrap }));
51
+ const isLargeContent = computed(() => typeof props.value === "string" && props.value.length > 30);
39
52
  const formattedValue = computed(() => {
40
53
  switch (typeof props.value) {
41
54
  case "boolean":
@@ -11,18 +11,11 @@
11
11
  import { useDebounceFn } from "@vueuse/core";
12
12
  import { nextTick, ref, watch } from "vue";
13
13
  import { fNumber } from "../../../../helpers";
14
- import { AnyObject, TextFieldProps } from "../../../../types";
14
+ import { AnyObject, NumberFieldProps } from "../../../../types";
15
15
  import TextField from "./TextField";
16
16
 
17
17
  const emit = defineEmits(["update:model-value", "update"]);
18
18
 
19
- export interface NumberFieldProps extends TextFieldProps {
20
- precision?: number;
21
- delay?: number;
22
- currency?: boolean;
23
- min?: number;
24
- max?: number;
25
- }
26
19
 
27
20
  const props = withDefaults(defineProps<NumberFieldProps>(), {
28
21
  modelValue: "",
@@ -144,6 +144,9 @@ export function fElapsedTime(start: string, end?: string) {
144
144
  * Formats an amount into USD currency format
145
145
  */
146
146
  export function fCurrency(amount: number, options?: object) {
147
+ if (amount === null || amount === undefined || isNaN(amount)) {
148
+ return "$-";
149
+ }
147
150
  return new Intl.NumberFormat("en-US", {
148
151
  style: "currency",
149
152
  currency: "USD",
@@ -179,6 +182,9 @@ export function fShortCurrency(value: string | number, options?: { round: boolea
179
182
  * Formats a number into a shorthand human-readable format (ie: 1.2M or 5K)
180
183
  */
181
184
  export function fShortNumber(value: string | number, options?: { round: boolean }) {
185
+ if (value === "" || value === null || value === undefined) {
186
+ return "-";
187
+ }
182
188
  const shorts = [
183
189
  { pow: 3, unit: "K" },
184
190
  { pow: 6, unit: "M" },
@@ -1,9 +1,19 @@
1
1
  import { uid } from "quasar";
2
2
  import { ShallowReactive, shallowReactive } from "vue";
3
3
  import { TypedObject } from "../types";
4
+ import { FlashMessages } from "./FlashMessages";
4
5
 
5
6
  const store = new Map<string, any>();
6
7
 
8
+ export function storeObjects<T extends TypedObject>(newObjects: T[]) {
9
+ for (const index in newObjects) {
10
+ if (newObjects[index] && typeof newObjects[index] === "object") {
11
+ newObjects[index] = storeObject(newObjects[index]);
12
+ }
13
+ }
14
+ return newObjects;
15
+ }
16
+
7
17
  /**
8
18
  * Store an object in the object store via type + id
9
19
  * Returns the stored object that should be used instead of the passed object as the returned object is shared across the system
@@ -56,3 +66,21 @@ export function storeObject<T extends TypedObject>(newObject: T): ShallowReactiv
56
66
  store.set(objectKey, reactiveObject);
57
67
  return reactiveObject;
58
68
  }
69
+
70
+ export async function autoRefreshObject<T extends TypedObject>(object: T, condition: (object: T) => boolean, callback: (object: T) => Promise<T>, interval = 3000) {
71
+ if (!object?.id || !object?.__type) {
72
+ throw new Error("Invalid stored object. Cannot auto-refresh");
73
+ }
74
+
75
+ if (condition(object)) {
76
+ const refreshedObject = await callback(object);
77
+
78
+ if (!refreshedObject.id) {
79
+ return FlashMessages.error(`Failed to refresh ${object.__type} (${object.id}) status: ` + object.name);
80
+ }
81
+
82
+ storeObject(refreshedObject);
83
+ }
84
+
85
+ setTimeout(() => autoRefreshObject(object, condition, callback), interval);
86
+ }
@@ -3,7 +3,7 @@ import { ListControlsRoutes } from "../types";
3
3
  import { downloadFile } from "./downloadPdf";
4
4
  import { request } from "./request";
5
5
 
6
- export function useActionRoutes(baseUrl: string): ListControlsRoutes {
6
+ export function useActionRoutes(baseUrl: string, extend?: object): ListControlsRoutes {
7
7
  return {
8
8
  list(pager?) {
9
9
  return request.post(`${baseUrl}/list`, pager);
@@ -33,6 +33,7 @@ export function useActionRoutes(baseUrl: string): ListControlsRoutes {
33
33
  },
34
34
  export(filter, name) {
35
35
  return downloadFile(`${baseUrl}/export`, name || "export.csv", { filter });
36
- }
36
+ },
37
+ ...extend
37
38
  };
38
39
  }
@@ -19,3 +19,11 @@ export interface TextFieldProps {
19
19
  readonly?: boolean;
20
20
  debounce?: string | number;
21
21
  }
22
+
23
+ export interface NumberFieldProps extends TextFieldProps {
24
+ precision?: number;
25
+ delay?: number;
26
+ currency?: boolean;
27
+ min?: number;
28
+ max?: number;
29
+ }