quasar-ui-danx 0.4.12 → 0.4.13

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.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
+ }