@mythpe/quasar-ui-qui 0.0.96 → 0.0.98
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 +1 -1
- package/src/components/form/MOtp.vue +57 -53
- package/src/types/components.d.ts +28 -4
package/package.json
CHANGED
|
@@ -17,37 +17,39 @@ import { date } from 'quasar'
|
|
|
17
17
|
import type { MOtpProps as Props } from '../../types'
|
|
18
18
|
import { useMyth } from '../../composable'
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
// modelValue?: Props['modelValue'];
|
|
20
|
+
type P = {
|
|
22
21
|
inputLength?: Props['inputLength'];
|
|
23
|
-
|
|
22
|
+
string?: Props['string'];
|
|
24
23
|
time?: Props['time'];
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
noTiming?: Props['noTiming'];
|
|
25
|
+
noSendAgain?: Props['noSendAgain'];
|
|
27
26
|
topLabel?: Props['topLabel'];
|
|
28
27
|
topLabelProps?: Props['topLabelProps'];
|
|
29
28
|
autofocus?: Props['autofocus'];
|
|
30
29
|
errors?: Props['errors'];
|
|
30
|
+
type?: Props['type'];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
type Emits = {
|
|
34
|
-
(e: 'end'): void;
|
|
35
|
-
(e: 'send'): void;
|
|
36
|
-
}
|
|
37
|
-
const emit = defineEmits<Emits>()
|
|
38
|
-
const { __, props: pluginOptions } = useMyth()
|
|
39
33
|
const props = withDefaults(defineProps<P>(), {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
hideSendAgain: () => !1,
|
|
34
|
+
inputLength: 4,
|
|
35
|
+
string: !1,
|
|
36
|
+
time: 120,
|
|
37
|
+
noTiming: !1,
|
|
38
|
+
noSendAgain: !1,
|
|
46
39
|
topLabel: undefined,
|
|
47
40
|
topLabelProps: undefined,
|
|
48
|
-
autofocus:
|
|
49
|
-
errors: () => ([])
|
|
41
|
+
autofocus: !1,
|
|
42
|
+
errors: () => ([]),
|
|
43
|
+
type: 'tel'
|
|
50
44
|
})
|
|
45
|
+
type Func = () => void;
|
|
46
|
+
type Emits = {
|
|
47
|
+
(e: 'countdown', options: { start: Func, clear: Func, startTiming: Func }): void;
|
|
48
|
+
(e: 'resend', options: { start: Func, clear: Func, startTiming: Func }): void;
|
|
49
|
+
}
|
|
50
|
+
const emit = defineEmits<Emits>()
|
|
51
|
+
|
|
52
|
+
const { __, props: pluginOptions } = useMyth()
|
|
51
53
|
const modelValue = defineModel<Props['modelValue']>({ required: !1, default: undefined })
|
|
52
54
|
const otpErrors = computed(() => props.errors ? props.errors : [])
|
|
53
55
|
const length = computed<number>(() => parseInt(props.inputLength?.toString() || '0'))
|
|
@@ -57,22 +59,23 @@ watchEffect(() => {
|
|
|
57
59
|
fieldValues.value = (modelValue.value || '').toString().split('').slice(0, length.value)
|
|
58
60
|
})
|
|
59
61
|
|
|
60
|
-
const
|
|
61
|
-
const nonNullFields = fieldValues.value.filter(v => (v || v?.toString() !== '0'))
|
|
62
|
+
const inputValue = computed(() => {
|
|
63
|
+
// const nonNullFields = fieldValues.value.filter(v => (v || v?.toString() !== '0'))
|
|
64
|
+
const nonNullFields = fieldValues.value.filter(v => (v === 0 || v === '0') || ((v?.toString?.()?.length || 0) > 0))
|
|
62
65
|
if (length.value !== nonNullFields.length) {
|
|
63
66
|
return ''
|
|
64
67
|
}
|
|
65
68
|
return nonNullFields.join('')
|
|
66
69
|
})
|
|
67
70
|
|
|
68
|
-
watch(
|
|
69
|
-
if (
|
|
70
|
-
if (props.
|
|
71
|
-
if (!isNaN(
|
|
72
|
-
modelValue.value =
|
|
71
|
+
watch(inputValue, (v) => {
|
|
72
|
+
if (v) {
|
|
73
|
+
if (props.string) {
|
|
74
|
+
if (!isNaN(v) && v?.toString?.()?.length?.toString?.() === props.inputLength.toString()) {
|
|
75
|
+
modelValue.value = v
|
|
73
76
|
}
|
|
74
77
|
} else {
|
|
75
|
-
modelValue.value =
|
|
78
|
+
modelValue.value = v
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
})
|
|
@@ -93,12 +96,15 @@ const focus = (index: number) => {
|
|
|
93
96
|
if (index < length.value) {
|
|
94
97
|
fields.value[index]?.select()
|
|
95
98
|
} else {
|
|
96
|
-
if (
|
|
99
|
+
if (inputValue.value) {
|
|
97
100
|
fields.value[index - 1]?.blur()
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
}
|
|
105
|
+
const select = (index: number) => {
|
|
106
|
+
fields.value[index]?.select()
|
|
107
|
+
}
|
|
102
108
|
|
|
103
109
|
const blur = (index: number) => {
|
|
104
110
|
fields.value[index]?.blur()
|
|
@@ -106,12 +112,8 @@ const blur = (index: number) => {
|
|
|
106
112
|
|
|
107
113
|
const onUpdate = (value: string | number | null, index: number) => {
|
|
108
114
|
if (value) {
|
|
109
|
-
if (props.
|
|
110
|
-
nextTick(() =>
|
|
111
|
-
setTimeout(() => {
|
|
112
|
-
focus(index)
|
|
113
|
-
}, 100)
|
|
114
|
-
})
|
|
115
|
+
if (props.string && isNaN(value)) {
|
|
116
|
+
nextTick(() => setTimeout(() => focus(index), 100))
|
|
115
117
|
return
|
|
116
118
|
}
|
|
117
119
|
focus(index + 1)
|
|
@@ -122,7 +124,7 @@ const onUpdate = (value: string | number | null, index: number) => {
|
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
const onKeyDown = (evt: KeyboardEvent/*, index: number */) => {
|
|
125
|
-
if (props.
|
|
127
|
+
if (props.string && evt.key && evt.key.toString() !== '0' && !parseInt(evt.key) && evt.key?.length === 1) {
|
|
126
128
|
evt.preventDefault()
|
|
127
129
|
}
|
|
128
130
|
}
|
|
@@ -132,12 +134,12 @@ const onKeyUp = (evt: KeyboardEvent, index: number) => {
|
|
|
132
134
|
return
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
if (['
|
|
137
|
+
if (['delete', 'backspace'].includes(key?.toLowerCase())) {
|
|
136
138
|
focus(index - 1)
|
|
137
139
|
return
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
if (key === 'ArrowLeft'
|
|
142
|
+
if (key === 'ArrowLeft') {
|
|
141
143
|
focus(index - 1)
|
|
142
144
|
} else if (key === 'ArrowRight') {
|
|
143
145
|
focus(index + 1)
|
|
@@ -153,7 +155,7 @@ const onKeyUp = (evt: KeyboardEvent, index: number) => {
|
|
|
153
155
|
}
|
|
154
156
|
const onPaste = (evt: ClipboardEvent, index: number) => {
|
|
155
157
|
const value: any = evt.clipboardData?.getData('text')?.toString() || ''
|
|
156
|
-
if (props.
|
|
158
|
+
if (props.string && isNaN(value)) {
|
|
157
159
|
evt.preventDefault()
|
|
158
160
|
return
|
|
159
161
|
}
|
|
@@ -162,25 +164,28 @@ const onPaste = (evt: ClipboardEvent, index: number) => {
|
|
|
162
164
|
blur(index)
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
|
-
|
|
167
|
+
const onFocusIn = (evt: FocusEvent, index: number) => {
|
|
168
|
+
select(index)
|
|
169
|
+
}
|
|
166
170
|
const seconds = ref<number>(0)
|
|
167
171
|
const temp = ref<number>(0)
|
|
172
|
+
let interval: ReturnType<typeof setInterval> | null = null
|
|
168
173
|
watch(() => seconds.value, v => (temp.value = v))
|
|
169
174
|
const startTiming = () => {
|
|
170
|
-
if (props.
|
|
175
|
+
if (props.noTiming) {
|
|
171
176
|
return
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
if (seconds.value > 0) {
|
|
175
180
|
--seconds.value
|
|
176
181
|
if (!seconds.value) {
|
|
177
|
-
emit('
|
|
182
|
+
emit('countdown', { start, clear, startTiming })
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
186
|
const getTime = computed<string | null>(() => {
|
|
182
187
|
const nullValue = null
|
|
183
|
-
if (props.
|
|
188
|
+
if (props.noTiming) {
|
|
184
189
|
return nullValue
|
|
185
190
|
}
|
|
186
191
|
if (seconds.value < 1) {
|
|
@@ -193,12 +198,11 @@ const getTime = computed<string | null>(() => {
|
|
|
193
198
|
return `${(m < 9 ? '0' : '') + m}:${(s < 9 ? '0' : '') + s}`
|
|
194
199
|
})
|
|
195
200
|
const disabled = computed(() => seconds.value > 0)
|
|
196
|
-
let interval: any = null
|
|
197
201
|
const clear = () => {
|
|
198
202
|
interval && clearInterval(interval)
|
|
199
203
|
}
|
|
200
204
|
const start = () => {
|
|
201
|
-
if (props.
|
|
205
|
+
if (props.noTiming) {
|
|
202
206
|
return
|
|
203
207
|
}
|
|
204
208
|
clear()
|
|
@@ -207,13 +211,13 @@ const start = () => {
|
|
|
207
211
|
}
|
|
208
212
|
watch(() => props.time, () => start(), { immediate: !0, deep: !0 })
|
|
209
213
|
onBeforeUnmount(() => clear())
|
|
210
|
-
const
|
|
214
|
+
const onResend = () => {
|
|
211
215
|
if (disabled.value) {
|
|
212
216
|
return
|
|
213
217
|
}
|
|
214
|
-
emit('
|
|
218
|
+
emit('resend', { start, clear, startTiming })
|
|
215
219
|
}
|
|
216
|
-
defineExpose({ start })
|
|
220
|
+
defineExpose({ start, clear, startTiming, inputValue })
|
|
217
221
|
defineOptions({
|
|
218
222
|
name: 'MOtp',
|
|
219
223
|
inheritAttrs: !1
|
|
@@ -232,9 +236,7 @@ defineOptions({
|
|
|
232
236
|
class="justify-start q-mb-md text-body1"
|
|
233
237
|
v-bind="topLabelProps"
|
|
234
238
|
>
|
|
235
|
-
|
|
236
|
-
{{ __(topLabel) }}
|
|
237
|
-
</div>
|
|
239
|
+
{{ __(topLabel) }}
|
|
238
240
|
</MRow>
|
|
239
241
|
<div :class="`row ${$q.lang.rtl ? 'reverse' : ''} q-gutter-x-sm justify-center`">
|
|
240
242
|
<q-input
|
|
@@ -244,6 +246,7 @@ defineOptions({
|
|
|
244
246
|
v-model="fieldValues[i - 1]"
|
|
245
247
|
:autofocus="autofocus && i === 1"
|
|
246
248
|
:error="otpErrors.length > 0"
|
|
249
|
+
:type="type"
|
|
247
250
|
hide-bottom-space
|
|
248
251
|
input-class="text-center"
|
|
249
252
|
maxlength="1"
|
|
@@ -251,6 +254,7 @@ defineOptions({
|
|
|
251
254
|
outlined
|
|
252
255
|
style="width: 6ch"
|
|
253
256
|
v-bind="{...pluginOptions.otp as any,...$attrs}"
|
|
257
|
+
@focusin="onFocusIn($event, i - 1)"
|
|
254
258
|
@keydown="onKeyDown($event)"
|
|
255
259
|
@keyup="onKeyUp($event, i - 1)"
|
|
256
260
|
@paste.prevent="onPaste($event,i - 1)"
|
|
@@ -266,7 +270,7 @@ defineOptions({
|
|
|
266
270
|
<slot name="after-input" />
|
|
267
271
|
<MFadeTransition>
|
|
268
272
|
<div
|
|
269
|
-
v-if="!
|
|
273
|
+
v-if="!noTiming"
|
|
270
274
|
class="q-mt-sm"
|
|
271
275
|
>
|
|
272
276
|
<span>{{ __('myth.otp.expire_line') }}: </span>
|
|
@@ -275,13 +279,13 @@ defineOptions({
|
|
|
275
279
|
</MFadeTransition>
|
|
276
280
|
<MFadeTransition>
|
|
277
281
|
<div
|
|
278
|
-
v-if="!
|
|
282
|
+
v-if="!noSendAgain"
|
|
279
283
|
class="q-mt-sm"
|
|
280
284
|
>
|
|
281
285
|
<span>{{ __('myth.otp.send_again_title') }} </span>
|
|
282
286
|
<span
|
|
283
287
|
:class="{'text-decoration-underline':!0, disabled,'cursor-pointer': !disabled}"
|
|
284
|
-
@click="
|
|
288
|
+
@click="onResend()"
|
|
285
289
|
>{{ __('myth.otp.send_again_btn') }}</span>
|
|
286
290
|
</div>
|
|
287
291
|
</MFadeTransition>
|
|
@@ -562,13 +562,37 @@ export type MOptionsProps = Omit<QOptionGroupProps, 'name' | 'modelValue' | 'opt
|
|
|
562
562
|
}
|
|
563
563
|
|
|
564
564
|
export type MOtpProps = Omit<QInputProps, 'modelValue'> & {
|
|
565
|
-
|
|
565
|
+
/**
|
|
566
|
+
* Value of input after typing all inputs.
|
|
567
|
+
*/
|
|
568
|
+
modelValue?: any;
|
|
569
|
+
/**
|
|
570
|
+
* Length of inputs.
|
|
571
|
+
*/
|
|
566
572
|
inputLength?: string | number;
|
|
567
|
-
|
|
573
|
+
/**
|
|
574
|
+
* Values is string not number.
|
|
575
|
+
*/
|
|
576
|
+
string?: boolean;
|
|
577
|
+
/**
|
|
578
|
+
* Resend time countdown in seconds.
|
|
579
|
+
*/
|
|
568
580
|
time?: string | number;
|
|
569
|
-
|
|
570
|
-
|
|
581
|
+
/**
|
|
582
|
+
* Don't use timing to resend code.
|
|
583
|
+
*/
|
|
584
|
+
noTiming?: boolean;
|
|
585
|
+
/**
|
|
586
|
+
* Don't use send again button.
|
|
587
|
+
*/
|
|
588
|
+
noSendAgain?: boolean;
|
|
589
|
+
/**
|
|
590
|
+
* Top input label.
|
|
591
|
+
*/
|
|
571
592
|
topLabel?: string | undefined;
|
|
593
|
+
/**
|
|
594
|
+
* Top input props.
|
|
595
|
+
*/
|
|
572
596
|
topLabelProps?: any | undefined;
|
|
573
597
|
errors?: string[];
|
|
574
598
|
}
|