quasar-ui-danx 0.4.56 → 0.4.59

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.56",
3
+ "version": "0.4.59",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -10,7 +10,10 @@
10
10
  "types": "types/index.d.ts",
11
11
  "scripts": {
12
12
  "dev": "cd dev && quasar dev && cd ..",
13
- "build": "vite build",
13
+ "clean": "rimraf dist",
14
+ "build:types": "tsc -p tsconfig.build.json",
15
+ "build:bundle": "vite build",
16
+ "build": "yarn clean && yarn build:types && yarn build:bundle",
14
17
  "preview": "vite preview",
15
18
  "postversion": "yarn build && npm publish && cd .. && git add ui && git commit -m \"v$npm_package_version\" && git tag \"v$npm_package_version\" && git push"
16
19
  },
@@ -18,7 +18,7 @@
18
18
  >
19
19
  <div>
20
20
  <div
21
- v-for="option in options"
21
+ v-for="option in optionsPlusSelected"
22
22
  :key="option.id"
23
23
  v-ripple
24
24
  class="cursor-pointer flex items-center relative"
@@ -80,7 +80,6 @@
80
80
  :class="editClass"
81
81
  :size="size"
82
82
  :disabled="editDisabled"
83
- class="opacity-0 group-hover:opacity-100 transition-all"
84
83
  :show-icon="EditIcon"
85
84
  :hide-icon="DoneEditingIcon"
86
85
  :tooltip="editDisabled ? 'You are not allowed to edit' : ''"
@@ -104,7 +103,7 @@ import {
104
103
  FaSolidListCheck as DefaultSelectIcon,
105
104
  FaSolidPencil as EditIcon
106
105
  } from "danx-icon";
107
- import { ref } from "vue";
106
+ import { computed, ref } from "vue";
108
107
  import { ActionTargetItem } from "../../../../types";
109
108
  import { ShowHideButton } from "../../../Utility/Buttons";
110
109
  import { ActionButtonProps, default as ActionButton } from "../../../Utility/Buttons/ActionButton";
@@ -113,7 +112,7 @@ import EditableDiv from "./EditableDiv";
113
112
  defineEmits(["create", "update", "delete"]);
114
113
  const selected = defineModel<ActionTargetItem | null>("selected");
115
114
  const editing = defineModel<boolean>("editing");
116
- withDefaults(defineProps<{
115
+ const props = withDefaults(defineProps<{
117
116
  options: ActionTargetItem[];
118
117
  showEdit?: boolean;
119
118
  loading?: boolean;
@@ -153,4 +152,10 @@ withDefaults(defineProps<{
153
152
  });
154
153
 
155
154
  const isSelecting = ref(false);
155
+
156
+ // If the selected option is not in the options list, it should be added in
157
+ const optionsPlusSelected = computed(() => {
158
+ if (!selected.value || props.options.find((o) => o.id === selected.value?.id)) return props.options;
159
+ return [selected.value, ...props.options];
160
+ });
156
161
  </script>
@@ -1,4 +1,4 @@
1
- import { computed, ref, shallowRef, watch } from "vue";
1
+ import { computed, Ref, 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";
@@ -10,8 +10,7 @@ import {
10
10
  ListControlsFilter,
11
11
  ListControlsOptions,
12
12
  ListControlsPagination,
13
- PagedItems,
14
- Ref
13
+ PagedItems
15
14
  } from "../../types";
16
15
  import { getFilterFromUrl } from "./listHelpers";
17
16
 
@@ -46,6 +46,7 @@
46
46
  class="flex items-stretch flex-nowrap h-full"
47
47
  >
48
48
  <PanelsDrawerTabs
49
+ v-if="!hideTabs"
49
50
  :key="'pd-tabs:' + target.id"
50
51
  v-model="activePanel"
51
52
  :target="target"
@@ -53,13 +54,31 @@
53
54
  :panels="panels"
54
55
  @update:model-value="$emit('update:model-value', $event)"
55
56
  />
56
- <PanelsDrawerPanels
57
+
58
+ <QTabPanels
57
59
  :key="'pd-panels:' + target.id"
58
- :panels="panels"
59
- :active-panel="activePanel"
60
- :active-item="target"
60
+ :model-value="activePanel"
61
61
  :class="activePanelOptions?.class || panelsClass"
62
- />
62
+ class="dx-panels-drawer-panels overflow-y-auto h-full transition-all"
63
+ >
64
+ <slot
65
+ name="panels"
66
+ :active-panel="activePanel"
67
+ >
68
+ <QTabPanel
69
+ v-for="panel in panels"
70
+ :key="panel.name"
71
+ :name="panel.name"
72
+ >
73
+ <RenderVnode
74
+ v-if="panel.vnode"
75
+ :vnode="panel.vnode"
76
+ :props="target"
77
+ />
78
+ </QTabPanel>
79
+ </slot>
80
+ </QTabPanels>
81
+
63
82
  <div
64
83
  v-if="$slots['right-sidebar']"
65
84
  class="border-l overflow-y-auto"
@@ -81,8 +100,7 @@
81
100
  import { computed, onMounted, ref, watch } from "vue";
82
101
  import { XIcon as CloseIcon } from "../../svg";
83
102
  import { ActionPanel, ActionTargetItem } from "../../types";
84
- import { ContentDrawer } from "../Utility";
85
- import PanelsDrawerPanels from "./PanelsDrawerPanels";
103
+ import { ContentDrawer, RenderVnode } from "../Utility";
86
104
  import PanelsDrawerTabs from "./PanelsDrawerTabs";
87
105
 
88
106
  export interface PanelsDrawerProps {
@@ -93,7 +111,8 @@ export interface PanelsDrawerProps {
93
111
  panelsClass?: string | object,
94
112
  drawerClass?: string | object,
95
113
  position?: "standard" | "right" | "left";
96
- panels: ActionPanel[]
114
+ panels: ActionPanel[];
115
+ hideTabs?: boolean;
97
116
  }
98
117
 
99
118
  defineEmits(["update:model-value", "close"]);
@@ -1,3 +1,2 @@
1
1
  export { default as PanelsDrawer } from "./PanelsDrawer.vue";
2
- export { default as PanelsDrawerPanels } from "./PanelsDrawerPanels.vue";
3
2
  export { default as PanelsDrawerTabs } from "./PanelsDrawerTabs.vue";
@@ -1,58 +1,58 @@
1
1
  <template>
2
- <QBtn
3
- :loading="isSaving"
4
- class="shadow-none py-0"
5
- :class="buttonClass"
6
- :disable="disabled"
7
- @click="()=> onAction()"
8
- >
9
- <div class="flex items-center flex-nowrap">
10
- <component
11
- :is="icon || typeOptions.icon"
12
- class="transition-all"
13
- :class="resolvedIconClass"
14
- />
15
- <slot>
16
- <div
17
- v-if="label || label === 0"
18
- class="ml-2"
19
- :class="labelClass"
20
- >
21
- {{ label }}
22
- </div>
23
- </slot>
24
- </div>
25
- <QTooltip
26
- v-if="tooltip"
27
- class="whitespace-nowrap"
28
- >
29
- <slot name="tooltip">
30
- {{ tooltip }}
31
- </slot>
32
- </QTooltip>
33
- <QMenu
34
- v-if="isConfirming"
35
- :model-value="true"
36
- >
37
- <div class="p-4 bg-slate-600">
38
- <div>{{ confirmText }}</div>
39
- <div class="flex items-center flex-nowrap mt-2">
40
- <div class="flex-grow">
41
- <ActionButton
42
- type="cancel"
43
- color="gray"
44
- @click="isConfirming = false"
45
- />
46
- </div>
47
- <ActionButton
48
- type="confirm"
49
- color="green"
50
- @click="()=> onAction(true)"
51
- />
52
- </div>
53
- </div>
54
- </QMenu>
55
- </QBtn>
2
+ <QBtn
3
+ :loading="isSaving"
4
+ class="shadow-none py-0"
5
+ :class="buttonClass"
6
+ :disable="disabled"
7
+ @click="()=> onAction()"
8
+ >
9
+ <div class="flex items-center flex-nowrap">
10
+ <component
11
+ :is="icon || typeOptions.icon"
12
+ class="transition-all"
13
+ :class="resolvedIconClass"
14
+ />
15
+ <div
16
+ v-if="label || label === 0"
17
+ class="ml-2"
18
+ :class="labelClass"
19
+ >
20
+ {{ label }}
21
+ </div>
22
+ <slot />
23
+ </div>
24
+ <QTooltip
25
+ v-if="tooltip"
26
+ class="whitespace-nowrap"
27
+ :class="tooltipClass"
28
+ >
29
+ <slot name="tooltip">
30
+ {{ tooltip }}
31
+ </slot>
32
+ </QTooltip>
33
+ <QMenu
34
+ v-if="isConfirming"
35
+ :model-value="true"
36
+ >
37
+ <div class="p-4 bg-slate-600">
38
+ <div>{{ confirmText }}</div>
39
+ <div class="flex items-center flex-nowrap mt-2">
40
+ <div class="flex-grow">
41
+ <ActionButton
42
+ type="cancel"
43
+ color="gray"
44
+ @click="isConfirming = false"
45
+ />
46
+ </div>
47
+ <ActionButton
48
+ type="confirm"
49
+ color="green"
50
+ @click="()=> onAction(true)"
51
+ />
52
+ </div>
53
+ </div>
54
+ </QMenu>
55
+ </QBtn>
56
56
  </template>
57
57
  <script setup lang="ts">
58
58
  import {
@@ -62,6 +62,7 @@ import {
62
62
  FaSolidCopy as CopyIcon,
63
63
  FaSolidFileExport as ExportIcon,
64
64
  FaSolidFileImport as ImportIcon,
65
+ FaSolidMinus as MinusIcon,
65
66
  FaSolidPause as PauseIcon,
66
67
  FaSolidPencil as EditIcon,
67
68
  FaSolidPlay as PlayIcon,
@@ -73,8 +74,8 @@ import { computed, ref } from "vue";
73
74
  import { ActionTarget, ResourceAction } from "../../../types";
74
75
 
75
76
  export interface ActionButtonProps {
76
- type?: "trash" | "trash-red" | "create" | "edit" | "copy" | "play" | "stop" | "pause" | "refresh" | "confirm" | "cancel" | "export" | "import";
77
- color?: "red" | "blue" | "sky" | "sky-invert" | "green" | "green-invert" | "lime" | "white" | "gray";
77
+ type?: "trash" | "create" | "edit" | "copy" | "play" | "stop" | "pause" | "refresh" | "confirm" | "cancel" | "export" | "import" | "minus";
78
+ color?: "red" | "blue" | "sky" | "sky-invert" | "green" | "green-invert" | "lime" | "white" | "gray" | "yellow" | "orange";
78
79
  size?: "xxs" | "xs" | "sm" | "md" | "lg";
79
80
  icon?: object | string;
80
81
  iconClass?: string;
@@ -89,6 +90,7 @@ export interface ActionButtonProps {
89
90
  disabledClass?: string;
90
91
  confirm?: boolean;
91
92
  confirmText?: string;
93
+ tooltipClass?: string;
92
94
  }
93
95
 
94
96
  const emit = defineEmits(["success", "error", "always"]);
@@ -105,7 +107,8 @@ const props = withDefaults(defineProps<ActionButtonProps>(), {
105
107
  target: null,
106
108
  input: null,
107
109
  confirmText: "Are you sure?",
108
- disabledClass: "text-slate-800 bg-slate-500 opacity-50"
110
+ disabledClass: "text-slate-800 bg-slate-500 opacity-50",
111
+ tooltipClass: ""
109
112
  });
110
113
 
111
114
  const mappedSizeClass = {
@@ -156,6 +159,10 @@ const colorClass = computed(() => {
156
159
  return "text-sky-400 bg-sky-800 hover:bg-sky-900";
157
160
  case "white":
158
161
  return "text-white bg-gray-800 hover:bg-gray-200";
162
+ case "yellow":
163
+ return "text-yellow-300 bg-yellow-800 hover:bg-yellow-700";
164
+ case "orange":
165
+ return "text-orange-400 bg-orange-900 hover:bg-orange-800";
159
166
  case "gray":
160
167
  return "text-slate-200 bg-slate-800 hover:bg-slate-900";
161
168
  default:
@@ -191,6 +198,8 @@ const typeOptions = computed(() => {
191
198
  return { icon: PauseIcon };
192
199
  case "refresh":
193
200
  return { icon: RefreshIcon };
201
+ case "minus":
202
+ return { icon: MinusIcon };
194
203
  default:
195
204
  return { icon: EditIcon };
196
205
  }
@@ -0,0 +1,29 @@
1
+ import { ref, shallowRef } from "vue";
2
+ import { ActionStore, ActionTargetItem, ListControlsRoutes } from "../types";
3
+
4
+ export function useActionStore(routes: ListControlsRoutes): ActionStore {
5
+ const listItems = shallowRef<ActionTargetItem[]>([]);
6
+ const isRefreshing = ref(false);
7
+ const hasLoadedItems = ref(false);
8
+
9
+ async function loadItems() {
10
+ if (hasLoadedItems.value) return;
11
+ await refreshItems();
12
+ hasLoadedItems.value = true;
13
+ }
14
+
15
+ async function refreshItems() {
16
+ if (isRefreshing.value) return;
17
+ isRefreshing.value = true;
18
+ listItems.value = (await routes.list({ sort: [{ column: "name" }] })).data || [];
19
+ isRefreshing.value = false;
20
+ }
21
+
22
+ return {
23
+ listItems,
24
+ isRefreshing,
25
+ hasLoadedItems,
26
+ loadItems,
27
+ refreshItems
28
+ };
29
+ }
@@ -1,64 +1,65 @@
1
1
  import { useGeolocation } from "@vueuse/core";
2
- import { computed } from "vue";
2
+ import { computed, ShallowRef } from "vue";
3
+ import { Ref } from "vue-demi";
3
4
  import { sleep } from "./utils";
4
5
 
5
6
  let isLoaded = false;
6
7
  let hasAlreadyWaited = false;
7
- let geolocationError = null;
8
- let hasLocation = null;
9
- let geolocation = null;
8
+ let geolocationError: ShallowRef<GeolocationPositionError | null> | null = null;
9
+ let hasLocation: Ref<number | null> | null = null;
10
+ let geolocation: Ref<GeolocationCoordinates> | null = null;
10
11
 
11
12
  export function useCompatibility(requestLocation = true) {
12
- if (!isLoaded && requestLocation) {
13
- const { coords, error, locatedAt } = useGeolocation();
14
- geolocationError = error;
15
- hasLocation = locatedAt;
16
- geolocation = coords;
17
- isLoaded = true;
18
- }
13
+ if (!isLoaded && requestLocation) {
14
+ const { coords, error, locatedAt } = useGeolocation();
15
+ geolocationError = error;
16
+ hasLocation = locatedAt;
17
+ geolocation = coords;
18
+ isLoaded = true;
19
+ }
19
20
 
20
- const isLocationSupported = "geolocation" in navigator;
21
+ const isLocationSupported = "geolocation" in navigator;
21
22
 
22
- const location = computed(() => {
23
- if (hasLocation?.value) {
24
- return geolocation?.value;
25
- }
26
- return null;
27
- });
23
+ const location = computed(() => {
24
+ if (hasLocation?.value) {
25
+ return geolocation?.value;
26
+ }
27
+ return null;
28
+ });
28
29
 
29
- const isCompatible = computed(() => !geolocationError?.value && !!hasLocation?.value);
30
+ const isCompatible = computed(() => !geolocationError?.value && !!hasLocation?.value);
30
31
 
31
- /**
32
- * Wait for location to be available and returns the location when it is or null after the wait period times out.
33
- * @param maxWait
34
- */
35
- const waitForLocation = async (maxWait = 3000) => {
36
- // We only should wait once, if we already waited and failed, its unlikely the location will be available at a
37
- // later time
38
- if (hasAlreadyWaited) {
39
- return location;
40
- }
32
+ /**
33
+ * Wait for location to be available and returns the location when it is or null after the wait period times out.
34
+ * @param maxWait
35
+ */
36
+ const waitForLocation = async (maxWait = 3000) => {
37
+ // We only should wait once, if we already waited and failed, its unlikely the location will be available at a
38
+ // later time
39
+ if (hasAlreadyWaited) {
40
+ return location;
41
+ }
41
42
 
42
- hasAlreadyWaited = true;
43
- let waitTime = 0;
44
- while (!location.value) {
45
- await sleep(100);
46
- waitTime += 100;
43
+ hasAlreadyWaited = true;
44
+ let waitTime = 0;
45
+ while (!location.value) {
46
+ await sleep(100);
47
+ waitTime += 100;
47
48
 
48
- if (waitTime > maxWait) {
49
- return null;
50
- }
51
- }
49
+ if (waitTime > maxWait) {
50
+ return null;
51
+ }
52
+ }
52
53
 
53
- return location;
54
- };
54
+ return location;
55
+ };
55
56
 
56
- return {
57
- isLocationSupported,
58
- isCompatible,
59
- geolocationError,
60
- hasLocation,
61
- location,
62
- waitForLocation
63
- };
57
+ return {
58
+ isLocationSupported,
59
+ isCompatible,
60
+ geolocationError,
61
+ hasLocation,
62
+ location,
63
+ waitForLocation
64
+ };
64
65
  }
@@ -1,5 +1,6 @@
1
+ import { DateTime } from "luxon";
1
2
  import { parseDateTime } from "./formats";
2
3
 
3
4
  export function diffInDays(date1: string, date2: string) {
4
- return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
5
+ return parseDateTime(date2)?.diff(parseDateTime(date1) || DateTime.now(), ["days"]).days;
5
6
  }
@@ -1,4 +1,5 @@
1
1
  export * from "./actions";
2
+ export * from "./actionStore";
2
3
  export * from "./app";
3
4
  export * from "./array";
4
5
  export * from "./compatibility";
@@ -87,10 +87,6 @@
87
87
  &.scroll {
88
88
  overflow: auto;
89
89
  }
90
-
91
- .q-tab-panel {
92
- padding: 0;
93
- }
94
90
  }
95
91
 
96
92
  &.overflow-y-auto {
@@ -1,8 +1,8 @@
1
1
  import { FilterGroup, ListController, ListControlsRoutes } from "src/types/controls";
2
2
  import { FormField } from "src/types/forms";
3
3
  import { TableColumn } from "src/types/tables";
4
- import { VNode } from "vue";
5
- import { AnyObject, ComputedRef, TypedObject } from "./shared";
4
+ import { ComputedRef, Ref, ShallowRef, VNode } from "vue";
5
+ import { AnyObject, TypedObject } from "./shared";
6
6
 
7
7
  export interface ActionTargetItem extends TypedObject {
8
8
  isSaving?: boolean;
@@ -58,6 +58,14 @@ export interface ResourceAction<T = ActionTargetItem> extends ActionOptions<T> {
58
58
  __type: string;
59
59
  }
60
60
 
61
+ export interface ActionStore {
62
+ listItems: ShallowRef<ActionTargetItem[]>;
63
+ isRefreshing: Ref<boolean>;
64
+ hasLoadedItems: Ref<boolean>;
65
+ loadItems: () => Promise<void>;
66
+ refreshItems: () => Promise<void>;
67
+ }
68
+
61
69
  export interface ActionController<T = ActionTargetItem> {
62
70
  // Actions
63
71
  action?: (actionName: string, target?: T | null, input?: any) => Promise<any | void>;
@@ -68,8 +76,9 @@ export interface ActionController<T = ActionTargetItem> {
68
76
  batchActions?: ResourceAction[];
69
77
  menuActions?: ResourceAction[];
70
78
  columns?: TableColumn[];
71
- filters?: ComputedRef<FilterGroup[]>;
79
+ filters?: ComputedRef<FilterGroup[]> | FilterGroup[];
72
80
  fields?: FormField[];
73
81
  panels?: ActionPanel[];
74
82
  routes?: ListControlsRoutes;
83
+ store?: ActionStore;
75
84
  }
@@ -1,6 +1,7 @@
1
1
  import { RequestCallOptions } from "src/types/requests";
2
+ import { ComputedRef, Ref, ShallowRef } from "vue";
2
3
  import { ActionOptions, ActionTargetItem, ResourceAction } from "./actions";
3
- import { AnyObject, ComputedRef, LabelValueItem, Ref } from "./shared";
4
+ import { AnyObject, LabelValueItem } from "./shared";
4
5
 
5
6
  export interface ListControlsFilter {
6
7
  [key: string]: object | object[] | null | undefined | string | number | boolean;
@@ -52,6 +53,7 @@ export interface ListControlsOptions {
52
53
 
53
54
  export interface ListControlsPagination {
54
55
  __sort?: object[] | null;
56
+ sort?: object[] | null;
55
57
  sortBy?: string | null;
56
58
  descending?: boolean;
57
59
  page?: number;
@@ -77,7 +79,7 @@ export interface PagedItems<T = ActionTargetItem> {
77
79
  export interface ListController<T = ActionTargetItem> {
78
80
  name: string;
79
81
  label: string;
80
- pagedItems: Ref<PagedItems<T> | null>;
82
+ pagedItems: ShallowRef<PagedItems<T> | null>;
81
83
  activeFilter: Ref<ListControlsFilter>;
82
84
  globalFilter: Ref<ListControlsFilter>;
83
85
  filterActiveCount: ComputedRef<number>;
@@ -14,12 +14,3 @@ export interface LabelValueItem {
14
14
  label: string;
15
15
  value: string | number | boolean;
16
16
  }
17
-
18
- /** Define Vue 3 Types here for better type checking in PHPStorm */
19
- export interface Ref<T = any> {
20
- value: T;
21
- }
22
-
23
- export interface ComputedRef<T = any> {
24
- readonly value: T;
25
- }
@@ -0,0 +1,48 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Compile down to ESNext (or choose your target version)
4
+ "target": "esnext",
5
+ // Use ES modules
6
+ "module": "esnext",
7
+ // Library files to be included in the compilation
8
+ "lib": [
9
+ "dom",
10
+ "esnext"
11
+ ],
12
+ // Generate corresponding '.d.ts' file
13
+ "declaration": true,
14
+ // Output directory for the compiled files
15
+ "outDir": "./dist",
16
+ // Root directory of your source files
17
+ "rootDir": "./src",
18
+ // Enable all strict type-checking options
19
+ "strict": true,
20
+ // Enables compatibility with Babel-style module imports
21
+ "esModuleInterop": true,
22
+ // Skip type checking of all declaration files (*.d.ts)
23
+ "skipLibCheck": true,
24
+ // Disallow inconsistently-cased references to the same file
25
+ "forceConsistentCasingInFileNames": true,
26
+ // Resolve modules using Node.js style
27
+ "moduleResolution": "node",
28
+ "resolveJsonModule": true,
29
+ // Allow default imports from modules with no default export
30
+ "allowSyntheticDefaultImports": true,
31
+ // Enables experimental support for decorators
32
+ "experimentalDecorators": true,
33
+ // Enables source map generation for the compiled files (useful for debugging)
34
+ "sourceMap": true,
35
+ // Base directory to resolve non-relative module names,
36
+ "baseUrl": "./"
37
+ },
38
+ "include": [
39
+ "src/**/*.ts",
40
+ "src/**/*.vue",
41
+ "src/**/*.svg",
42
+ "types/**/*.d.ts"
43
+ ],
44
+ "exclude": [
45
+ "dist",
46
+ "**/*.spec.ts"
47
+ ]
48
+ }