@d-mok/quasar-app-extension-quasar-axe 3.1.46 → 3.1.48

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.46",
3
+ "version": "3.1.48",
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
 
@@ -89,7 +84,8 @@ export async function askTable<T extends object>(
89
84
  message: string = '',
90
85
  prefills: T[][],
91
86
  validators: [(_: T) => boolean, string][] = [[$ => true, '']],
92
- arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']]
87
+ arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']],
88
+ headersMap: Record<string, string> = {}
93
89
  ): Promise<T[]> {
94
90
  if (prefills.flat().length === 0)
95
91
  throwError('Error', 'All prefills are empty array.')
@@ -100,6 +96,7 @@ export async function askTable<T extends object>(
100
96
  sample: [...prefills].reverse().find($ => $.length > 0)![0],
101
97
  validators,
102
98
  arrayValidators,
99
+ headersMap,
103
100
  cancel: true,
104
101
  allowAddRow: true,
105
102
  })
@@ -113,7 +110,8 @@ export async function editTable<T extends object>(
113
110
  message: string = '',
114
111
  prefill: T[],
115
112
  validators: [(_: T) => boolean, string][] = [[$ => true, '']],
116
- arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']]
113
+ arrayValidators: [(_: T[]) => boolean, string][] = [[$ => true, '']],
114
+ headersMap: Record<string, string> = {}
117
115
  ): Promise<T[]> {
118
116
  if (prefill.length === 0) throwError('Error', 'prefill is empty array.')
119
117
  return await base(dialogTable, {
@@ -123,6 +121,7 @@ export async function editTable<T extends object>(
123
121
  sample: prefill[0],
124
122
  validators,
125
123
  arrayValidators,
124
+ headersMap,
126
125
  cancel: true,
127
126
  allowAddRow: false,
128
127
  })
@@ -148,43 +147,6 @@ export async function askArray(
148
147
  return result.pluck('value').unmatch([''])
149
148
  }
150
149
 
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
150
  /**
189
151
  * Show a dialog with text area display.
190
152
  */
@@ -269,3 +231,47 @@ export async function askFiles(
269
231
  cancel: true,
270
232
  })
271
233
  }
234
+
235
+ /**
236
+ * Ask to select one text by a radio button.
237
+ */
238
+ export async function askRadioText(
239
+ title: string,
240
+ message: string = '',
241
+ items: string[],
242
+ prefill: string = items[0]!,
243
+ newLabel: string = '',
244
+ isValid: Predicate<string> = $ => true
245
+ ): Promise<string> {
246
+ return await base(dialogRadioText, {
247
+ title,
248
+ message,
249
+ items,
250
+ prefill,
251
+ newLabel,
252
+ isValid,
253
+ cancel: true,
254
+ })
255
+ }
256
+
257
+ /**
258
+ * Ask to select multiple text by checkboxs.
259
+ */
260
+ export async function askCheckboxText(
261
+ title: string,
262
+ message: string = '',
263
+ items: string[],
264
+ prefill: string[] = [],
265
+ newLabel: string = '',
266
+ isValid: Predicate<string[]> = $ => true
267
+ ): Promise<string[]> {
268
+ return await base(dialogCheckboxText, {
269
+ title,
270
+ message,
271
+ items,
272
+ prefill,
273
+ newLabel,
274
+ isValid,
275
+ cancel: true,
276
+ })
277
+ }
@@ -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>
@@ -25,6 +25,7 @@
25
25
  v-model="dummy"
26
26
  :sample="sample"
27
27
  :allowAddRow="allowAddRow"
28
+ :headersMap="headersMap"
28
29
  v-if="exist"
29
30
  ></handson>
30
31
  </q-card>
@@ -83,6 +84,7 @@ const {
83
84
  arrayValidators,
84
85
  cancel,
85
86
  allowAddRow,
87
+ headersMap,
86
88
  } = defineProps<{
87
89
  title: string
88
90
  message: string
@@ -92,6 +94,7 @@ const {
92
94
  arrayValidators: [(_: row[]) => boolean, string][]
93
95
  cancel: boolean
94
96
  allowAddRow: boolean
97
+ headersMap: Record<string, string>
95
98
  }>()
96
99
 
97
100
  // delay render
@@ -7,7 +7,7 @@
7
7
  width="100%"
8
8
  stretchH="all"
9
9
  :data="content"
10
- :colHeaders="Object.keys(sample)"
10
+ :colHeaders="Object.keys(sample).map($ => headersMap[$] ?? $)"
11
11
  :columns="columns()"
12
12
  :rowHeaders="true"
13
13
  :afterChange="shake"
@@ -40,9 +40,10 @@ const content = defineModel<Record<string, string | number | boolean>[]>({
40
40
  required: true,
41
41
  })
42
42
 
43
- const { sample, allowAddRow } = defineProps<{
43
+ const { sample, allowAddRow, headersMap } = defineProps<{
44
44
  sample: Record<string, string | number | boolean>
45
45
  allowAddRow: boolean
46
+ headersMap: Record<string, string>
46
47
  }>()
47
48
 
48
49
  function columns() {
@@ -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 }