quasar-ui-danx 0.4.12 → 0.4.13

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.12",
3
+ "version": "0.4.13",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -57,7 +57,8 @@
57
57
  "danx-icon": "^1.0.2",
58
58
  "exifreader": "^4.21.1",
59
59
  "gsap": "^3.12.5",
60
- "luxon": "^3.4.4"
60
+ "luxon": "^3.4.4",
61
+ "yaml": "^2.4.5"
61
62
  },
62
63
  "browserslist": [
63
64
  "last 4 Chrome versions",
@@ -62,32 +62,36 @@
62
62
  </QTab>
63
63
  </QTabs>
64
64
  </div>
65
- <div
65
+ <template
66
66
  v-for="(field, index) in mappedFields"
67
67
  :key="field.id"
68
- :class="{ 'mt-4': index > 0, [fieldClass]: true }"
69
68
  >
70
- <RenderVnode
71
- v-if="field.vnode"
72
- :vnode="field.vnode"
73
- :field="field"
74
- :props="getVnodeProps(field)"
75
- @update:model-value="onInput(field.name, $event)"
76
- />
77
- <Component
78
- :is="field.component"
79
- :key="field.name + '-' + currentVariation"
80
- :model-value="getFieldValue(field.name)"
81
- :field="field"
82
- :label="field.label || undefined"
83
- :no-label="noLabel"
84
- :show-name="showName"
85
- :clearable="field.clearable || clearable"
86
- :disable="disable"
87
- :readonly="readonly"
88
- @update:model-value="onInput(field.name, $event)"
89
- />
90
- </div>
69
+ <div
70
+ v-show="isFieldEnabled(field)"
71
+ :class="{ 'mt-4': index > 0, [fieldClass]: true }"
72
+ >
73
+ <RenderVnode
74
+ v-if="field.vnode"
75
+ :vnode="field.vnode"
76
+ :props="getVnodeProps(field)"
77
+ :params="fieldInputs"
78
+ @update:model-value="onInput(field.name, $event)"
79
+ />
80
+ <Component
81
+ :is="field.component"
82
+ :key="field.name + '-' + currentVariation"
83
+ :model-value="getFieldValue(field.name)"
84
+ :field="field"
85
+ :label="field.label || undefined"
86
+ :no-label="noLabel"
87
+ :show-name="showName"
88
+ :clearable="field.clearable || clearable"
89
+ :disable="disable"
90
+ :readonly="readonly"
91
+ @update:model-value="onInput(field.name, $event)"
92
+ />
93
+ </div>
94
+ </template>
91
95
  <div
92
96
  v-if="savedAt"
93
97
  :class="savingClass"
@@ -136,7 +140,7 @@ import { ExclamationCircleIcon as MissingIcon, PencilIcon as EditIcon } from "@h
136
140
  import { computed, ref } from "vue";
137
141
  import { fDateTime, FlashMessages, incrementName, replace } from "../../../helpers";
138
142
  import { TrashIcon as RemoveIcon } from "../../../svg";
139
- import { AnyObject, Form, FormFieldValue } from "../../../types";
143
+ import { AnyObject, FormFieldValue, RenderedFormProps } from "../../../types";
140
144
  import { ConfirmDialog, RenderVnode } from "../../Utility";
141
145
  import {
142
146
  BooleanField,
@@ -150,22 +154,6 @@ import {
150
154
  WysiwygField
151
155
  } from "./Fields";
152
156
 
153
- export interface RenderedFormProps {
154
- values?: FormFieldValue[] | object | null;
155
- form: Form;
156
- noLabel?: boolean;
157
- showName?: boolean;
158
- disable?: boolean;
159
- readonly?: boolean;
160
- saving?: boolean;
161
- clearable?: boolean;
162
- emptyValue?: string | number | boolean;
163
- canModifyVariations?: boolean;
164
- fieldClass?: string;
165
- savingClass?: string;
166
- savedAt?: string;
167
- }
168
-
169
157
  const props = withDefaults(defineProps<RenderedFormProps>(), {
170
158
  values: null,
171
159
  emptyValue: undefined,
@@ -190,9 +178,9 @@ const FORM_FIELD_MAP = {
190
178
 
191
179
  const mappedFields = props.form.fields.map((field) => ({
192
180
  placeholder: `Enter ${field.label}`,
181
+ default: field.type === "BOOLEAN" ? false : "",
193
182
  ...field,
194
- component: field.component || FORM_FIELD_MAP[field.type],
195
- default: field.type === "BOOLEAN" ? false : ""
183
+ component: field.component || FORM_FIELD_MAP[field.type]
196
184
  }));
197
185
 
198
186
  const fieldResponses = computed(() => {
@@ -201,6 +189,24 @@ const fieldResponses = computed(() => {
201
189
  return Object.entries(props.values).map(([name, value]) => ({ name, value, variation: "" }));
202
190
  });
203
191
 
192
+ const fieldInputs = computed(() => {
193
+ const inputs: AnyObject = {};
194
+ for (const field of mappedFields) {
195
+ inputs[field.name] = getFieldValue(field.name);
196
+ }
197
+ return inputs;
198
+ });
199
+
200
+ function isFieldEnabled(field) {
201
+ if (field.enabled === undefined) return true;
202
+
203
+ if (typeof field.enabled === "function") {
204
+ return field.enabled(fieldInputs.value);
205
+ }
206
+
207
+ return field.enabled;
208
+ }
209
+
204
210
  function getVnodeProps(field) {
205
211
  return {
206
212
  modelValue: getFieldValue(field.name),
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <ContentDrawer
3
3
  position="right"
4
- :show="true"
4
+ show
5
5
  overlay
6
6
  content-class="h-full"
7
7
  class="dx-panels-drawer"
@@ -13,7 +13,13 @@
13
13
  <div class="dx-panels-drawer-header flex items-center px-6 py-4">
14
14
  <div class="flex-grow">
15
15
  <slot name="header">
16
- <h2>{{ title }}</h2>
16
+ <h2 v-if="title">
17
+ {{ title }}
18
+ </h2>
19
+ <div v-if="!activeItem">
20
+ Loading
21
+ <QSpinnerHourglass />
22
+ </div>
17
23
  </slot>
18
24
  </div>
19
25
  <div
@@ -32,10 +38,14 @@
32
38
  </div>
33
39
  </div>
34
40
  <div class="dx-panels-drawer-body flex-grow overflow-hidden h-full">
35
- <div class="flex items-stretch flex-nowrap h-full">
41
+ <div
42
+ v-if="activeItem.__timestamp > 0"
43
+ class="flex items-stretch flex-nowrap h-full"
44
+ >
36
45
  <PanelsDrawerTabs
37
46
  :key="'pd-tabs:' + activeItem.id"
38
47
  v-model="activePanel"
48
+ :active-item="activeItem"
39
49
  :class="tabsClass"
40
50
  :panels="panels"
41
51
  @update:model-value="$emit('update:model-value', $event)"
@@ -44,6 +54,7 @@
44
54
  :key="'pd-panels:' + activeItem.id"
45
55
  :panels="panels"
46
56
  :active-panel="activePanel"
57
+ :active-item="activeItem"
47
58
  :class="activePanelOptions?.class || panelsClass"
48
59
  />
49
60
  <div
@@ -68,7 +79,7 @@ import PanelsDrawerTabs from "./PanelsDrawerTabs";
68
79
  export interface Props {
69
80
  title?: string,
70
81
  modelValue?: string | number,
71
- activeItem?: ActionTargetItem;
82
+ activeItem: ActionTargetItem;
72
83
  tabsClass?: string | object,
73
84
  panelsClass?: string | object,
74
85
  panels: ActionPanel[]
@@ -78,7 +89,6 @@ defineEmits(["update:model-value", "close"]);
78
89
  const props = withDefaults(defineProps<Props>(), {
79
90
  title: "",
80
91
  modelValue: null,
81
- activeItem: null,
82
92
  tabsClass: "w-[13.5rem]",
83
93
  panelsClass: "w-[35.5rem]"
84
94
  });
@@ -11,17 +11,19 @@
11
11
  <RenderVnode
12
12
  v-if="panel.vnode"
13
13
  :vnode="panel.vnode"
14
+ :props="activeItem"
14
15
  />
15
16
  </QTabPanel>
16
17
  </QTabPanels>
17
18
  </template>
18
19
 
19
20
  <script setup lang="ts">
20
- import { ActionPanel } from "../../types";
21
+ import { ActionPanel, ActionTargetItem } from "../../types";
21
22
  import { RenderVnode } from "../Utility";
22
23
 
23
24
  defineProps<{
24
25
  activePanel?: string | number,
26
+ activeItem: ActionTargetItem,
25
27
  panels: ActionPanel[]
26
28
  }>();
27
29
  </script>
@@ -9,11 +9,11 @@
9
9
  @update:model-value="$emit('update:model-value', $event)"
10
10
  >
11
11
  <template v-for="panel in panels">
12
- <template v-if="panel.enabled === undefined || !!panel.enabled">
12
+ <template v-if="isEnabled(panel)">
13
13
  <RenderVnode
14
14
  v-if="panel.tabVnode"
15
15
  :key="panel.name"
16
- :vnode="panel.tabVnode(modelValue)"
16
+ :vnode="panel.tabVnode(activeItem, modelValue)"
17
17
  :is-active="modelValue === panel.name"
18
18
  :name="panel.name"
19
19
  :label="panel.label"
@@ -30,19 +30,32 @@
30
30
  </template>
31
31
  <script setup lang="ts">
32
32
  import { QTab } from "quasar";
33
- import { ActionPanel } from "../../types";
33
+ import { ActionPanel, ActionTargetItem } from "../../types";
34
34
  import { RenderVnode } from "../Utility";
35
35
 
36
36
  defineEmits(["update:model-value"]);
37
37
 
38
38
  interface Props {
39
39
  modelValue?: string | number;
40
+ activeItem: ActionTargetItem;
40
41
  panels: ActionPanel[];
41
42
  }
42
43
 
43
- withDefaults(defineProps<Props>(), {
44
+ const props = withDefaults(defineProps<Props>(), {
44
45
  modelValue: "general"
45
46
  });
47
+
48
+ function isEnabled(panel) {
49
+ if (panel.enabled === undefined) return true;
50
+
51
+ if (!panel.enabled) return false;
52
+
53
+ if (typeof panel.enabled === "function") {
54
+ return panel.enabled(props.activeItem);
55
+ }
56
+
57
+ return true;
58
+ }
46
59
  </script>
47
60
 
48
61
  <style lang="scss" module="cls">
@@ -11,7 +11,7 @@ const RenderVnode = (props) => {
11
11
  }
12
12
 
13
13
  if (typeof props.vnode === "function") {
14
- return props.vnode(props.props);
14
+ return props.vnode(props.props, props.params);
15
15
  }
16
16
 
17
17
  return null;
@@ -24,6 +24,10 @@ RenderVnode.props = {
24
24
  props: {
25
25
  type: Object,
26
26
  default: () => ({})
27
+ },
28
+ params: {
29
+ type: Object,
30
+ default: null
27
31
  }
28
32
  };
29
33
  export default RenderVnode;
@@ -1,5 +1,5 @@
1
1
  import { parseDateTime } from "./formats";
2
2
 
3
- export function diffInDays(date1, date2) {
4
- return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
3
+ export function diffInDays(date1: string, date2: string) {
4
+ return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
5
5
  }
@@ -1,4 +1,5 @@
1
1
  import { DateTime, IANAZone } from "luxon";
2
+ import { parse as parseYAML, stringify as stringifyYAML } from "yaml";
2
3
  import { ActionTargetItem, fDateOptions } from "../types";
3
4
  import { isJSON } from "./utils";
4
5
 
@@ -335,10 +336,56 @@ export function fJSON(string: string | object) {
335
336
  }
336
337
  }
337
338
 
338
- export function fMarkdownJSON(string: string | object): string {
339
- if (isJSON(string)) {
340
- return `\`\`\`json\n${fJSON(string)}\n\`\`\``;
339
+ /**
340
+ * Convert markdown formatted string into a valid JSON object
341
+ */
342
+ export function parseMarkdownJSON(string: string | object): object | null | undefined {
343
+ if (typeof string === "object") return string as object;
344
+
345
+ try {
346
+ return JSON.parse(parseMarkdownCode(string));
347
+ } catch (e) {
348
+ return undefined;
341
349
  }
342
- // @ts-expect-error Guaranteed to only allow strings here using isJSON check
343
- return string;
350
+ }
351
+
352
+ export function parseMarkdownYAML(string: string): object | null | undefined {
353
+ try {
354
+ return parseYAML(parseMarkdownCode(string)) || (string ? undefined : null);
355
+ } catch (e) {
356
+ return undefined;
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Parse a markdown formatted string and return the code block content
362
+ */
363
+ export function parseMarkdownCode(string: string): string {
364
+ return string.replace(/^```[a-z0-9]{1,6}\s/, "").replace(/```$/, "");
365
+ }
366
+
367
+ /**
368
+ * Convert a JSON object or string of code into a markdown formatted JSON string
369
+ * ie: a valid JSON string with a ```json prefix and ``` postfix
370
+ */
371
+ export function fMarkdownCode(type: string, string: string | object): string {
372
+ if (typeof string === "object" || isJSON(string)) {
373
+ switch (type) {
374
+ case "yaml":
375
+ string = stringifyYAML(string);
376
+ break;
377
+ case "ts":
378
+ string = "";
379
+ break;
380
+ default:
381
+ string = fJSON(string);
382
+ }
383
+ }
384
+
385
+ const regex = new RegExp(`\`\`\`${type}`, "g");
386
+ if (!((string || "") as string).match(regex)) {
387
+ return `\`\`\`${type}\n${string}\n\`\`\``;
388
+ }
389
+
390
+ return string as string;
344
391
  }
@@ -91,12 +91,12 @@ export function incrementName(name: string) {
91
91
  * Check if a string is a valid JSON object. If an object is passed, always return true
92
92
  */
93
93
  export function isJSON(string: string | object) {
94
- if (!string) {
95
- return false;
96
- }
97
94
  if (typeof string === "object") {
98
95
  return true;
99
96
  }
97
+ if (!string) {
98
+ return false;
99
+ }
100
100
  try {
101
101
  JSON.parse(string);
102
102
  return true;
@@ -6,9 +6,9 @@ export interface ActionPanel {
6
6
  label: string;
7
7
  category?: string;
8
8
  class?: string | object;
9
- enabled?: boolean | (() => boolean);
10
- tabVnode?: (activePanel: string | number) => VNode | any;
11
- vnode: (activePanel: string) => VNode | any;
9
+ enabled?: boolean | ((activeItem: ActionTargetItem) => boolean);
10
+ tabVnode?: (activeItem: ActionTargetItem | null | undefined, activePanel: string | number) => VNode | any;
11
+ vnode: (activeItem: ActionTargetItem | null | undefined) => VNode | any;
12
12
  }
13
13
 
14
14
  export interface ActionTargetItem extends TypedObject {
@@ -1,4 +1,5 @@
1
1
  import { VNode } from "vue";
2
+ import { AnyObject } from "./shared";
2
3
 
3
4
  export interface FormFieldOption {
4
5
  value: string;
@@ -11,7 +12,8 @@ export interface FormField {
11
12
  name: string;
12
13
  label: string;
13
14
  placeholder?: string;
14
- vnode?: ((props) => VNode | any);
15
+ enabled?: boolean | ((input: AnyObject) => boolean);
16
+ vnode?: ((field: FormFieldOption, input?: AnyObject) => VNode | any);
15
17
  component?: any;
16
18
  clearable?: boolean;
17
19
  required?: boolean;
@@ -35,3 +37,19 @@ export interface FormFieldValue {
35
37
  value: any,
36
38
  variation?: string
37
39
  }
40
+
41
+ export interface RenderedFormProps {
42
+ values?: FormFieldValue[] | object | null;
43
+ form: Form;
44
+ noLabel?: boolean;
45
+ showName?: boolean;
46
+ disable?: boolean;
47
+ readonly?: boolean;
48
+ saving?: boolean;
49
+ clearable?: boolean;
50
+ emptyValue?: string | number | boolean;
51
+ canModifyVariations?: boolean;
52
+ fieldClass?: string;
53
+ savingClass?: string;
54
+ savedAt?: string;
55
+ }