@d-mok/quasar-app-extension-quasar-axe 3.1.45 → 3.1.47

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": "@d-mok/quasar-app-extension-quasar-axe",
3
- "version": "3.1.45",
3
+ "version": "3.1.47",
4
4
  "description": "A Quasar App Extension",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1,33 +1,28 @@
1
- import { Dialog, QDialogOptions } from 'quasar'
2
1
  import dialogTextarea from './custom/dialogTextarea.vue'
3
2
  import dialogTable from './custom/dialogTable.vue'
4
- import dialogForm from './custom/dialogForm.vue'
5
3
  import dialogBtn from './custom/dialogBtn.vue'
6
4
  import dialogFile from './custom/dialogFile.vue'
5
+ import dialogRadioText from './custom/dialogRadioText.vue'
6
+ import dialogCheckboxText from './custom/dialogCheckboxText.vue'
7
7
  import { throwError } from './basic'
8
+ import type { Component } from 'vue'
9
+ import { base } from './custom_base'
8
10
 
9
11
  type Predicate<T> = (_: T) => boolean
10
12
  type Mapper<T, S> = (_: T) => S
11
13
 
12
- async function base(component: QDialogOptions['component'], props: any) {
13
- return new Promise<any>((resolve, reject) =>
14
- Dialog.create({
15
- component,
16
- componentProps: {
17
- persistent: true,
18
- ...props,
19
- },
20
- })
21
- .onOk((data: any) => resolve(data))
22
- .onCancel(() => reject('dialog cancelled by user.'))
23
- )
24
- }
14
+ type VueComponent = Component & (new (...args: any) => any)
15
+ type PropType<C extends VueComponent> = Omit<
16
+ InstanceType<C>['$props'],
17
+ 'onOk' | 'onHide' | `onVnode${string}` | `ref_${string}`
18
+ >
19
+ type OkType<C extends VueComponent> = Parameters<InstanceType<C>['$emit']>[1]
25
20
 
26
- export async function custom(
27
- component: QDialogOptions['component'],
28
- props: any = {}
29
- ) {
30
- return await base(component, props)
21
+ export async function custom<C extends VueComponent>(
22
+ component: C,
23
+ props?: PropType<C>
24
+ ): Promise<OkType<C>> {
25
+ return await base(component, props ?? {})
31
26
  }
32
27
 
33
28
  /**
@@ -75,7 +70,7 @@ export async function askFn(
75
70
  funcs: Record<string, () => any>
76
71
  ): Promise<string> {
77
72
  let key = await askBtn(title, message, Object.keys(funcs))
78
- await funcs[key]()
73
+ await funcs[key]?.()
79
74
  return key
80
75
  }
81
76
 
@@ -148,43 +143,6 @@ export async function askArray(
148
143
  return result.pluck('value').unmatch([''])
149
144
  }
150
145
 
151
- type FormPrefill = Record<
152
- string,
153
- | string
154
- | number
155
- | boolean
156
- | { type: 'select'; value: string; options: string[] }
157
- | { type: 'autocomplete'; value: string; options: string[] }
158
- >
159
-
160
- type FormFlat<T extends FormPrefill> = {
161
- [P in keyof T]: T[P] extends object ? T[P]['value'] : T[P]
162
- }
163
-
164
- /**
165
- * Ask for an objects.
166
- */
167
- export async function askForm<T extends FormPrefill>(
168
- title: string,
169
- message: string,
170
- prefill: T,
171
- validators: [(_: FormFlat<T>) => boolean, string][] = [[$ => true, '']]
172
- ): Promise<FormFlat<T>> {
173
- const standardPrefill = Object.mapValues(prefill, $ => {
174
- if (typeof $ === 'string') return { type: 'string', value: $ }
175
- if (typeof $ === 'number') return { type: 'number', value: $ }
176
- if (typeof $ === 'boolean') return { type: 'boolean', value: $ }
177
- return $
178
- })
179
-
180
- return await base(dialogForm, {
181
- title,
182
- message,
183
- prefill: standardPrefill,
184
- validators,
185
- })
186
- }
187
-
188
146
  /**
189
147
  * Show a dialog with text area display.
190
148
  */
@@ -269,3 +227,47 @@ export async function askFiles(
269
227
  cancel: true,
270
228
  })
271
229
  }
230
+
231
+ /**
232
+ * Ask to select one text by a radio button.
233
+ */
234
+ export async function askRadioText(
235
+ title: string,
236
+ message: string = '',
237
+ items: string[],
238
+ prefill: string = items[0]!,
239
+ newLabel: string = '',
240
+ isValid: Predicate<string> = $ => true
241
+ ): Promise<string> {
242
+ return await base(dialogRadioText, {
243
+ title,
244
+ message,
245
+ items,
246
+ prefill,
247
+ newLabel,
248
+ isValid,
249
+ cancel: true,
250
+ })
251
+ }
252
+
253
+ /**
254
+ * Ask to select multiple text by checkboxs.
255
+ */
256
+ export async function askCheckboxText(
257
+ title: string,
258
+ message: string = '',
259
+ items: string[],
260
+ prefill: string[] = [],
261
+ newLabel: string = '',
262
+ isValid: Predicate<string[]> = $ => true
263
+ ): Promise<string[]> {
264
+ return await base(dialogCheckboxText, {
265
+ title,
266
+ message,
267
+ items,
268
+ prefill,
269
+ newLabel,
270
+ isValid,
271
+ cancel: true,
272
+ })
273
+ }
@@ -143,7 +143,7 @@ export async function askRadio<T>(
143
143
  title: string,
144
144
  message: string = '',
145
145
  items: T[],
146
- prefill: T = items[0],
146
+ prefill: T = items[0]!,
147
147
  label?: (string & keyof T) | Mapper<T, string>
148
148
  ): Promise<T> {
149
149
  let labelFunc = ($: T) => Object.print($, label)
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <q-dialog
3
+ ref="dialogRef"
4
+ @hide="onDialogHide"
5
+ >
6
+ <q-card style="width: 400px">
7
+ <q-card-section>
8
+ <p class="text-h6">{{ title }}</p>
9
+ <p v-html="message" />
10
+ <div style="max-height: 340px; overflow-y: auto">
11
+ <div v-for="item in items">
12
+ <q-checkbox
13
+ v-model="select"
14
+ :val="item"
15
+ :label="item"
16
+ />
17
+ </div>
18
+ <div v-for="item in newItems">
19
+ <div class="row q-pr-md">
20
+ <q-checkbox
21
+ class="col-auto"
22
+ v-model="item.checked"
23
+ :val="item.val"
24
+ />
25
+ <q-input
26
+ class="col"
27
+ v-model="item.val"
28
+ :label="newLabel"
29
+ dense
30
+ />
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </q-card-section>
35
+
36
+ <q-card-actions align="right">
37
+ <q-btn
38
+ label="Cancel"
39
+ flat
40
+ @click="onDialogCancel"
41
+ />
42
+ <q-btn
43
+ label="OK"
44
+ flat
45
+ @click="onDialogOK(final)"
46
+ :disable="
47
+ ![...items, ...newItems.pluck('val')].includesEvery(
48
+ final
49
+ ) || !isValid(final)
50
+ "
51
+ />
52
+ </q-card-actions>
53
+ </q-card>
54
+ </q-dialog>
55
+ </template>
56
+
57
+ <script lang="ts" setup>
58
+ import { useDialogPluginComponent } from 'quasar'
59
+ import { computed, ref, watch } from 'vue'
60
+
61
+ defineEmits([...useDialogPluginComponent.emits])
62
+
63
+ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
64
+ useDialogPluginComponent()
65
+
66
+ const props = defineProps<{
67
+ title: string
68
+ message: string
69
+ items: string[]
70
+ prefill: string[]
71
+ newLabel: string
72
+ isValid: (_: string[]) => boolean
73
+ }>()
74
+
75
+ let select = ref([...props.prefill.match(props.items)])
76
+
77
+ let newItems = ref<{ val: string; checked: boolean }[]>([
78
+ { val: '', checked: false },
79
+ ])
80
+
81
+ let final = computed(() => {
82
+ return [
83
+ ...select.value.sortedBy(props.items),
84
+ ...newItems.value.match('checked').pluck('val'),
85
+ ]
86
+ })
87
+
88
+ watch(
89
+ newItems,
90
+ () => {
91
+ if (newItems.value.at(-1)?.val !== '') {
92
+ newItems.value.push({ val: '', checked: false })
93
+ }
94
+ },
95
+ { deep: true }
96
+ )
97
+ </script>
@@ -3,7 +3,7 @@
3
3
  ref="dialogRef"
4
4
  @hide="onDialogHide"
5
5
  >
6
- <q-card style="min-width: 300px; max-width: 600px">
6
+ <q-card style="width: 400px; max-width: 600px">
7
7
  <q-card-section class="q-pb-xs">
8
8
  <p class="text-h6">{{ title }}</p>
9
9
  <p v-html="message" />
@@ -14,30 +14,50 @@
14
14
  <q-input
15
15
  v-if="spec.type === 'string'"
16
16
  v-model="spec.value"
17
- :label="field"
17
+ :label="spec.label ?? field"
18
+ :hint="spec.hint"
18
19
  />
19
20
  <q-input
20
21
  v-if="spec.type === 'number'"
21
22
  v-model.number="spec.value"
22
- :label="field"
23
+ :label="spec.label ?? field"
23
24
  type="number"
25
+ :hint="spec.hint"
24
26
  />
25
27
  <q-checkbox
26
28
  v-if="spec.type === 'boolean'"
27
29
  v-model="spec.value"
28
- :label="field"
30
+ :label="spec.label ?? field"
31
+ />
32
+ <q-input
33
+ v-if="spec.type === 'date'"
34
+ v-model="spec.value"
35
+ :label="spec.label ?? field"
36
+ type="date"
37
+ :hint="spec.hint"
38
+ />
39
+ <q-input
40
+ v-if="spec.type === 'time'"
41
+ v-model="spec.value"
42
+ :label="spec.label ?? field"
43
+ type="time"
44
+ :hint="spec.hint"
29
45
  />
30
46
  <q-select
31
47
  v-if="spec.type === 'select'"
32
48
  v-model="spec.value"
33
49
  :options="spec.options"
34
- :label="field"
50
+ :label="spec.label ?? field"
51
+ :hint="spec.hint"
35
52
  />
36
53
  <qx-input-autocomplete
37
54
  v-if="spec.type === 'autocomplete'"
38
55
  v-model="spec.value"
39
56
  :autocomplete="spec.options"
40
- :label="field"
57
+ :label="spec.label ?? field"
58
+ :hint="spec.hint"
59
+ :no-filter="spec.noFilter"
60
+ :immediate="spec.immediate"
41
61
  />
42
62
  </div>
43
63
  <div
@@ -68,7 +88,7 @@
68
88
 
69
89
  <script lang="ts" setup>
70
90
  import { useDialogPluginComponent } from 'quasar'
71
- import { computed, reactive, ref } from 'vue'
91
+ import { computed, reactive, ref, watch } from 'vue'
72
92
 
73
93
  defineEmits([...useDialogPluginComponent.emits])
74
94
 
@@ -77,20 +97,37 @@ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
77
97
 
78
98
  type row = Record<string, string | number | boolean>
79
99
 
80
- type prefill = Record<
100
+ export type prefill = Record<
81
101
  string,
82
- | { type: 'string'; value: string }
83
- | { type: 'number'; value: number }
84
- | { type: 'boolean'; value: boolean }
85
- | { type: 'select'; value: string; options: string[] }
86
- | { type: 'autocomplete'; value: string; options: string[] }
102
+ | { type: 'string'; value: string; label?: string; hint?: string }
103
+ | { type: 'number'; value: number; label?: string; hint?: string }
104
+ | { type: 'boolean'; value: boolean; label?: string }
105
+ | { type: 'date'; value: string; label?: string; hint?: string }
106
+ | { type: 'time'; value: string; label?: string; hint?: string }
107
+ | {
108
+ type: 'select'
109
+ value: string
110
+ options: string[]
111
+ label?: string
112
+ hint?: string
113
+ }
114
+ | {
115
+ type: 'autocomplete'
116
+ value: string
117
+ options: string[]
118
+ label?: string
119
+ hint?: string
120
+ noFilter?: boolean
121
+ immediate?: boolean
122
+ }
87
123
  >
88
124
 
89
- const { title, message, prefill, validators } = defineProps<{
125
+ const { title, message, prefill, validators, onChange } = defineProps<{
90
126
  title: string
91
127
  message: string
92
128
  prefill: prefill
93
129
  validators: [(_: row) => boolean, string][]
130
+ onChange: (current: prefill, old: prefill) => void
94
131
  }>()
95
132
 
96
133
  const content = reactive(JSON.clone(prefill))
@@ -103,4 +140,9 @@ function err(): false | string {
103
140
  }
104
141
  return false
105
142
  }
143
+
144
+ watch(
145
+ () => JSON.stringify(content),
146
+ (newbie, oldie) => onChange(content, JSON.parse(oldie))
147
+ )
106
148
  </script>
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <q-dialog
3
+ ref="dialogRef"
4
+ @hide="onDialogHide"
5
+ >
6
+ <q-card style="width: 400px">
7
+ <q-card-section>
8
+ <p class="text-h6">{{ title }}</p>
9
+ <p v-html="message" />
10
+ <div style="max-height: 340px; overflow-y: auto">
11
+ <div v-for="item in items">
12
+ <q-radio
13
+ v-model="select"
14
+ :val="item"
15
+ :label="item"
16
+ />
17
+ </div>
18
+ <div class="row q-pr-md">
19
+ <q-radio
20
+ class="col-auto"
21
+ v-model="select"
22
+ :val="add"
23
+ />
24
+ <q-input
25
+ class="col"
26
+ v-model="add"
27
+ :label="newLabel"
28
+ dense
29
+ />
30
+ </div>
31
+ </div>
32
+ </q-card-section>
33
+
34
+ <q-card-actions align="right">
35
+ <q-btn
36
+ label="Cancel"
37
+ flat
38
+ @click="onDialogCancel"
39
+ />
40
+ <q-btn
41
+ label="OK"
42
+ flat
43
+ @click="onDialogOK(select)"
44
+ :disable="
45
+ ![...items, add].includes(select) || !isValid(select)
46
+ "
47
+ />
48
+ </q-card-actions>
49
+ </q-card>
50
+ </q-dialog>
51
+ </template>
52
+
53
+ <script lang="ts" setup>
54
+ import { useDialogPluginComponent } from 'quasar'
55
+ import { ref, watch } from 'vue'
56
+
57
+ defineEmits([...useDialogPluginComponent.emits])
58
+
59
+ const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
60
+ useDialogPluginComponent()
61
+
62
+ const props = defineProps<{
63
+ title: string
64
+ message: string
65
+ items: string[]
66
+ prefill: string
67
+ newLabel: string
68
+ isValid: (_: string) => boolean
69
+ }>()
70
+
71
+ let select = ref(props.prefill)
72
+ let add = ref('')
73
+
74
+ watch(add, () => {
75
+ select.value = add.value
76
+ })
77
+ </script>
@@ -0,0 +1,16 @@
1
+ import { Dialog, QDialogOptions } from 'quasar'
2
+ import type { Component } from 'vue'
3
+
4
+ export async function base(component: Component, props: any) {
5
+ return new Promise<any>((resolve, reject) =>
6
+ Dialog.create({
7
+ component,
8
+ componentProps: {
9
+ persistent: true,
10
+ ...props,
11
+ },
12
+ })
13
+ .onOk((data: any) => resolve(data))
14
+ .onCancel(() => reject('dialog cancelled by user.'))
15
+ )
16
+ }
@@ -0,0 +1,75 @@
1
+ import dialogForm from './custom/dialogForm.vue'
2
+ import { base } from './custom_base'
3
+
4
+ type FormPrefill = Record<
5
+ string,
6
+ | string
7
+ | number
8
+ | boolean
9
+ | { type: 'string'; value: string; label?: string; hint?: string }
10
+ | { type: 'number'; value: number; label?: string; hint?: string }
11
+ | { type: 'boolean'; value: boolean; label?: string }
12
+ | { type: 'date'; value: string; label?: string; hint?: string }
13
+ | { type: 'time'; value: string; label?: string; hint?: string }
14
+ | {
15
+ type: 'select'
16
+ value: string
17
+ options: string[]
18
+ label?: string
19
+ hint?: string
20
+ }
21
+ | {
22
+ type: 'autocomplete'
23
+ value: string
24
+ options: string[]
25
+ label?: string
26
+ hint?: string
27
+ noFilter?: boolean
28
+ immediate?: boolean
29
+ }
30
+ >
31
+
32
+ type FormPrefillStandard<T extends FormPrefill> = {
33
+ [P in keyof T]: T[P] extends object
34
+ ? T[P]
35
+ : T[P] extends string
36
+ ? { type: 'string'; value: string; label?: string; hint?: string }
37
+ : T[P] extends number
38
+ ? { type: 'number'; value: number; label?: string; hint?: string }
39
+ : T[P] extends boolean
40
+ ? { type: 'boolean'; value: boolean; label?: string }
41
+ : never
42
+ }
43
+
44
+ type FormFlat<T extends FormPrefill> = {
45
+ [P in keyof T]: T[P] extends object ? T[P]['value'] : T[P]
46
+ }
47
+
48
+ /**
49
+ * Ask for an objects.
50
+ */
51
+ export async function askForm<T extends FormPrefill>(
52
+ title: string,
53
+ message: string,
54
+ prefill: T,
55
+ validators: [(_: FormFlat<T>) => boolean, string][] = [[$ => true, '']],
56
+ onChange: (
57
+ current: FormPrefillStandard<T>,
58
+ old: FormPrefillStandard<T>
59
+ ) => void = (c, o) => {}
60
+ ): Promise<FormFlat<T>> {
61
+ const standardPrefill = Object.mapValues(prefill, $ => {
62
+ if (typeof $ === 'string') return { type: 'string', value: $ }
63
+ if (typeof $ === 'number') return { type: 'number', value: $ }
64
+ if (typeof $ === 'boolean') return { type: 'boolean', value: $ }
65
+ return $
66
+ })
67
+
68
+ return await base(dialogForm, {
69
+ title,
70
+ message,
71
+ prefill: standardPrefill,
72
+ validators,
73
+ onChange,
74
+ })
75
+ }
@@ -1,4 +1,5 @@
1
1
  import * as basic from './basic'
2
2
  import * as advanced from './advanced'
3
+ import * as form from './form'
3
4
 
4
- export const qDialog = { ...basic, ...advanced }
5
+ export const qDialog = { ...basic, ...advanced, ...form }
@@ -9,7 +9,7 @@ type onfulfilled<T, U> = ((value: T) => Promisable<U>) | undefined | null
9
9
  type onrejected<U> = ((reason: any) => Promisable<U>) | undefined | null
10
10
 
11
11
  abstract class BuilderUI {
12
- private isSlience: boolean = false
12
+ private isSilence: boolean = false
13
13
  private isConfirm: boolean = false
14
14
  private confirmContent: { title: string; msg: string } = {
15
15
  title: '',
@@ -21,7 +21,7 @@ abstract class BuilderUI {
21
21
  protected abstract dataLogic(): Promise<void>
22
22
 
23
23
  protected async run() {
24
- if (this.isSlience) ui.loadingOff()
24
+ if (this.isSilence) ui.loadingOff()
25
25
 
26
26
  if (this.isConfirm) {
27
27
  let { title, msg } = this.confirmContent
@@ -32,7 +32,7 @@ abstract class BuilderUI {
32
32
 
33
33
  if (this.isNotify) ui.onNotify(this.notifyContent)
34
34
 
35
- if (this.isSlience) ui.loadingOn()
35
+ if (this.isSilence) ui.loadingOn()
36
36
  }
37
37
 
38
38
  confirm(title: string, ...msgs: string[]): this {
@@ -52,8 +52,17 @@ abstract class BuilderUI {
52
52
  return this
53
53
  }
54
54
 
55
+ /**
56
+ * @deprecated
57
+ */
55
58
  slience(): this {
56
- this.isSlience = true
59
+ this.isSilence = true
60
+ this.isNotify = false
61
+ return this
62
+ }
63
+
64
+ silence(): this {
65
+ this.isSilence = true
57
66
  this.isNotify = false
58
67
  return this
59
68
  }