@mythpe/quasar-ui-qui 0.1.40 → 0.1.42

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": "@mythpe/quasar-ui-qui",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "MyTh Quasar UI Kit App Extension",
5
5
  "author": {
6
6
  "name": "MyTh Ahmed Faiz",
@@ -34,10 +34,11 @@
34
34
  "ckeditor5": "^43.3.1",
35
35
  "lodash": "^4.17.21",
36
36
  "lodash-inflection": "^1.5.0",
37
+ "qs": "^6.13.1",
37
38
  "typed.js": "^2.1.0",
38
39
  "vee-validate": "^4.14.7",
39
40
  "vue-i18n": "^11.0.0-beta.2",
40
- "qs": "^6.13.1"
41
+ "vue3-signature": "^0.2.4"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@quasar/app-webpack": "^3.15.1",
@@ -0,0 +1,295 @@
1
+ <!--
2
+ - MyTh Ahmed Faiz Copyright © 2016-2024 All rights reserved.
3
+ - Email: mythpe@gmail.com
4
+ - Mobile: +966590470092
5
+ - Website: https://www.4myth.com
6
+ - Github: https://github.com/mythpe
7
+ -->
8
+
9
+ <script lang="ts" setup>
10
+
11
+ import { useField } from 'vee-validate'
12
+ import { computed, reactive, ref, toValue, watch } from 'vue'
13
+ import Vue3Signature from 'vue3-signature'
14
+ import type { MSignaturePadProps as Props, SignaturePadWaterMark } from '../../types'
15
+ import { useBindInput, useMyth } from '../../composable'
16
+
17
+ interface P {
18
+ name: Props['name'];
19
+ clearOnResize?: Props['clearOnResize'];
20
+ large?: Props['large'];
21
+ disabled?: Props['disabled'];
22
+ required?: Props['required'];
23
+ color?: Props['color'];
24
+ bg?: Props['bg'];
25
+ height?: Props['height'];
26
+ width?: Props['width'];
27
+ label?: Props['label'];
28
+ center?: Props['center'];
29
+ labelProps?: Props['labelProps'];
30
+ // url?: Props['url'];
31
+ noBtn?: Props['noBtn'];
32
+ waterMark?: Props['waterMark'];
33
+ viewMode?: Props['viewMode'];
34
+
35
+ auto?: Props['auto'];
36
+ col?: Props['col'];
37
+ xs?: Props['xs'];
38
+ sm?: Props['sm'];
39
+ md?: Props['md'];
40
+ lg?: Props['lg'];
41
+ xl?: Props['xl'];
42
+ fieldOptions?: Props['fieldOptions'];
43
+ }
44
+
45
+ const props = withDefaults(defineProps<P>(), {
46
+ name: () => '',
47
+ auto: undefined,
48
+ col: undefined,
49
+ xs: undefined,
50
+ sm: undefined,
51
+ md: undefined,
52
+ lg: undefined,
53
+ xl: undefined,
54
+
55
+ clearOnResize: !1,
56
+ large: !1,
57
+ disabled: !1,
58
+ required: !1,
59
+ color: 'rgb(0, 0, 0)',
60
+ bg: 'rgb(255,255,255)',
61
+ height: '300px',
62
+ width: '100%',
63
+ label: undefined,
64
+ center: !1,
65
+ labelProps: undefined,
66
+ // url: undefined,
67
+ noBtn: !1,
68
+ waterMark: undefined,
69
+ fieldOptions: undefined,
70
+
71
+ viewMode: !1
72
+ })
73
+
74
+ type Emits = {
75
+ (e: 'clear'): void;
76
+ (e: 'undo'): void;
77
+ }
78
+ const emit = defineEmits<Emits>()
79
+
80
+ const { __, alertError } = useMyth()
81
+
82
+ defineModel<Props['modelValue']>({ required: !1, default: undefined })
83
+ const url = defineModel<Props['url']>('url', { required: !1, default: undefined })
84
+ const helper = useBindInput<P & Props>(() => props, 'signaturePad')
85
+ const { getLabel, inputRules, hasTopLabel } = helper
86
+ const inputScope = useField<Props['modelValue']>(() => props.name, inputRules, {
87
+ syncVModel: !0,
88
+ label: getLabel,
89
+ ...toValue<any>(props.fieldOptions)
90
+ })
91
+ const { errorMessage, handleChange, handleBlur } = inputScope
92
+
93
+ const padRef = ref<InstanceType<typeof Vue3Signature> | null>(null)
94
+ const confirmed = ref(!1)
95
+ const disabledRef = ref(!1)
96
+ const isDisabled = computed(() => props.disabled || disabledRef.value || confirmed.value)
97
+ const reset = () => {
98
+ confirmed.value = !1
99
+ url.value = undefined
100
+ disabledRef.value = !1
101
+ }
102
+ const save = (type: 'image/jpeg' | 'image/svg+xml') => {
103
+ const isEmpty = !!padRef.value?.isEmpty()
104
+ if (isEmpty && props.required) {
105
+ alertError(__('validation.messages.required', { field: __(props.label || __('signature')) }))
106
+ return
107
+ }
108
+ confirmed.value = !0
109
+ if (url.value) {
110
+ url.value = undefined
111
+ padRef.value?.clear()
112
+ }
113
+ const v = isEmpty ? null : padRef.value?.save(type)
114
+ handleBlur(undefined, !0)
115
+ handleChange(v, !0)
116
+ }
117
+ const clear = () => {
118
+ padRef.value?.clear()
119
+ handleBlur(undefined, !0)
120
+ confirmed.value && handleChange(null, !0)
121
+ emit('clear')
122
+ reset()
123
+ }
124
+ const undo = () => {
125
+ padRef.value?.undo()
126
+ emit('undo')
127
+ handleBlur(undefined, !0)
128
+ confirmed.value && handleChange(null, !1)
129
+ reset()
130
+ }
131
+ const isEmpty = () => !!padRef.value?.isEmpty()
132
+ const addWaterMark = (opt: SignaturePadWaterMark) => padRef.value?.addWaterMark(opt)
133
+ const fromDataURL = (url: string) => padRef.value?.fromDataURL(url)
134
+ const scopes = reactive(inputScope)
135
+ defineExpose({ reset, save, clear, undo, disabled: isDisabled, isEmpty, addWaterMark, fromDataURL, padRef, ...scopes })
136
+ defineOptions({ name: 'MSignaturePad', inheritAttrs: !1 })
137
+ watch(url, (url) => {
138
+ if (url) {
139
+ fromDataURL(url)
140
+ if (!confirmed.value) {
141
+ confirmed.value = !0
142
+ }
143
+ }
144
+ }, { immediate: !0 })
145
+ </script>
146
+
147
+ <template>
148
+ <MCol
149
+ :auto="auto"
150
+ :class="[$attrs.class,{'m--input__required':inputRules?.required!==undefined,'m--input__is-top-label':hasTopLabel,'m--input__error':!!errorMessage,'m--input__view':viewMode}]"
151
+ :col="col"
152
+ :lg="lg"
153
+ :md="md"
154
+ :name="name"
155
+ :sm="sm"
156
+ :xs="xs"
157
+ >
158
+ <slot
159
+ :confirmed="confirmed"
160
+ name="top-input"
161
+ v-bind="scopes"
162
+ />
163
+ <MTransition>
164
+ <div
165
+ key="top"
166
+ class="row justify-between items-center q-mb-lg"
167
+ >
168
+ <div
169
+ :class="{
170
+ 'text-center': center,
171
+ 'q-mb-xs text-weight-bold' : !0,
172
+ 'text-body1': !large,
173
+ 'text-h6': large,
174
+ 'text-negative': !!errorMessage,
175
+ 'text-positive': !errorMessage && confirmed
176
+ }"
177
+ v-bind="labelProps"
178
+ >
179
+ <span v-if="!!label">{{ __(label) }}</span>
180
+ <span
181
+ v-if="label && required && !confirmed"
182
+ class="text-negative print-hide"
183
+ >&nbsp;*</span>
184
+ <q-icon
185
+ v-if="confirmed"
186
+ color="positive"
187
+ name="verified"
188
+ right
189
+ size="20px"
190
+ />
191
+ </div>
192
+ </div>
193
+ <div
194
+ v-if="!!errorMessage"
195
+ key="signature-errors"
196
+ class="text-negative text-caption q-mb-lg"
197
+ >
198
+ <q-icon
199
+ name="ion-ios-information-circle-outline"
200
+ size="18px"
201
+ />
202
+ {{ errorMessage }}
203
+ </div>
204
+ <div
205
+ key="signature-pad"
206
+ :class="{
207
+ 'bg-negative': !!errorMessage,
208
+ 'print-shadow-none bordered' : !0
209
+ }"
210
+ >
211
+ <div>
212
+ <slot
213
+ name="pad"
214
+ v-bind="{...scopes,width,height,reset,save,clear,undo,confirmed}"
215
+ >
216
+ <Vue3Signature
217
+ v-if="!url"
218
+ ref="padRef"
219
+ :class="{'q-mx-auto': !0, 'hidden': !!$slots.pad}"
220
+ :clear-on-resize="clearOnResize"
221
+ :disabled="isDisabled"
222
+ :h="height"
223
+ :sig-option="{
224
+ penColor: color,
225
+ backgroundColor: bg
226
+ }"
227
+ :w="width"
228
+ :water-mark="waterMark"
229
+ />
230
+ <template v-else-if="!!url">
231
+ <q-img
232
+ :height="height"
233
+ :src="url"
234
+ :width="width"
235
+ />
236
+ </template>
237
+ </slot>
238
+ <slot
239
+ :confirmed="confirmed"
240
+ v-bind="scopes"
241
+ />
242
+ </div>
243
+ </div>
244
+ <div
245
+ v-show="!disabled && !noBtn"
246
+ key="signature-buttons"
247
+ class="q-mt-sm print-hide"
248
+ >
249
+ <template v-if="!confirmed">
250
+ <div class="column q-gutter-sm">
251
+ <MBtn
252
+ :label="__('labels.confirm_signature')"
253
+ color="positive"
254
+ flat
255
+ icon="ion-ios-hand"
256
+ @click="save('image/jpeg')"
257
+ />
258
+ </div>
259
+ <MRow class="justify-between">
260
+ <MBtn
261
+ :label="__('labels.undo')"
262
+ flat
263
+ icon="ion-ios-undo"
264
+ @click="undo"
265
+ />
266
+ <q-space />
267
+ <MBtn
268
+ :label="__('labels.clear_signature')"
269
+ color="negative"
270
+ flat
271
+ icon="ion-ios-repeat"
272
+ @click="clear"
273
+ />
274
+ </MRow>
275
+ </template>
276
+ <MTransition>
277
+ <MColumn v-if="confirmed">
278
+ <MBtn
279
+ :label="__('labels.clear_signature')"
280
+ color="negative"
281
+ flat
282
+ icon="ion-ios-refresh"
283
+ @click="clear"
284
+ />
285
+ </MColumn>
286
+ </MTransition>
287
+ </div>
288
+ </MTransition>
289
+ <slot
290
+ :confirmed="confirmed"
291
+ name="bottom-input"
292
+ v-bind="scopes"
293
+ />
294
+ </MCol>
295
+ </template>
@@ -30,6 +30,7 @@ import MPassword from './MPassword.vue'
30
30
  import MPicker from './MPicker.vue'
31
31
  import MRadio from './MRadio.vue'
32
32
  import MSelect from './MSelect.vue'
33
+ import MSignaturePad from './MSignaturePad.vue'
33
34
  import MTime from './MTime.vue'
34
35
  import MToggle from './MToggle.vue'
35
36
  import MUploader from './MUploader.vue'
@@ -59,6 +60,7 @@ export {
59
60
  MPicker,
60
61
  MRadio,
61
62
  MSelect,
63
+ MSignaturePad,
62
64
  MTime,
63
65
  MToggle,
64
66
  MUploader
@@ -26,6 +26,7 @@ export const defineAsyncComponents = function (app: App) {
26
26
  app.component('MPicker', defineAsyncComponent(() => import('../components/form/MPicker.vue')))
27
27
  app.component('MRadio', defineAsyncComponent(() => import('../components/form/MRadio.vue')))
28
28
  app.component('MSelect', defineAsyncComponent(() => import('../components/form/MSelect.vue')))
29
+ app.component('MSignaturePad', defineAsyncComponent(() => import('../components/form/MSignaturePad.vue')))
29
30
  app.component('MTime', defineAsyncComponent(() => import('../components/form/MTime.vue')))
30
31
  app.component('MToggle', defineAsyncComponent(() => import('../components/form/MToggle.vue')))
31
32
  app.component('MUploader', defineAsyncComponent(() => import('../components/form/MUploader.vue')))
@@ -0,0 +1,184 @@
1
+ import type { BaseInputsProps, MColProps } from '../components'
2
+ import type { VNode } from 'vue'
3
+
4
+ export interface SignaturePadWaterMark {
5
+ /**
6
+ * Watermark text. default is: ''
7
+ * e.g. "Your Name", "Your Company", "Date"
8
+ */
9
+ text: string;
10
+ /**
11
+ * Mark font. Default is: '20px sans-serif'
12
+ */
13
+ font: string;
14
+ /**
15
+ * Fill Text and Stroke Text, 'all' | 'stroke' | 'fill'.
16
+ * Default is: 'fill'.
17
+ */
18
+ style: 'all' | 'stroke' | 'fill';
19
+ /**
20
+ * Fill color.
21
+ * Default is: '#333'
22
+ */
23
+ fillStyle: CSSStyleDeclaration['color'];
24
+ /**
25
+ * Stroke Color.
26
+ * Default is: '#333'.
27
+ */
28
+ strokeStyle: CSSStyleDeclaration['color'];
29
+ /**
30
+ * Fill positionX.
31
+ * Default is: 20
32
+ */
33
+ x: number;
34
+ /**
35
+ * fill positionY.
36
+ * Default is: 20
37
+ */
38
+ y: number;
39
+ /**
40
+ * Stroke positionX.
41
+ * Default is: 40
42
+ */
43
+ sx: number;
44
+ /**
45
+ * Stroke positionY.
46
+ * Default is: 40
47
+ */
48
+ sy: number;
49
+ }
50
+
51
+ export interface MSignaturePadProps extends Omit<MColProps, 'name'>, Pick<BaseInputsProps, 'fieldOptions'> {
52
+ /**
53
+ * Base64 data of the signature.
54
+ */
55
+ modelValue?: string | null | undefined;
56
+ /**
57
+ * Name of field.
58
+ * Default is: signature.
59
+ */
60
+ name: string;
61
+ /**
62
+ * Signature pen color.
63
+ * Default is: 'rgb(0, 0, 0)'
64
+ */
65
+ color?: CSSStyleDeclaration['color'];
66
+ /**
67
+ * Signature pad background color.
68
+ * Default is: 'rgb(255,255,255)'
69
+ */
70
+ bg?: CSSStyleDeclaration['backgroundColor'];
71
+ /**
72
+ * Parent container width.
73
+ * Need units like 100px or 100%
74
+ * Default is: '100%'
75
+ */
76
+ width?: string;
77
+ /**
78
+ * Parent container height.
79
+ * Need units like 100px or 100%
80
+ * Default is: '200px'
81
+ */
82
+ height?: string;
83
+ /**
84
+ * Canvas is cleared on window resize.
85
+ * Default is: false.
86
+ */
87
+ clearOnResize?: boolean;
88
+ /**
89
+ * Disable Signature pad.
90
+ * Default is: false.
91
+ */
92
+ disabled?: boolean;
93
+ /**
94
+ * Add Required validation.
95
+ * Default is: false.
96
+ */
97
+ required?: boolean;
98
+ /**
99
+ * Signature pad label.
100
+ * Default is: undefined.
101
+ */
102
+ label?: string | undefined | null;
103
+ /**
104
+ * Props of signature pad label.
105
+ * Default is: undefined.
106
+ */
107
+ labelProps?: Record<string, any>;
108
+ /**
109
+ * Signature pad center label.
110
+ * Default is: false.
111
+ */
112
+ center?: boolean | undefined;
113
+ /**
114
+ * Default value for signature url.
115
+ * Default is: undefined.
116
+ */
117
+ url?: string | undefined;
118
+ /**
119
+ * Hide control buttons.
120
+ * Default is: false.
121
+ */
122
+ noBtn?: boolean;
123
+ /**
124
+ * Size of signature pad label.
125
+ * Default is: false.
126
+ */
127
+ large?: boolean;
128
+ /**
129
+ * Signature pad watermark text.
130
+ * Default is: undefined.
131
+ */
132
+ waterMark?: SignaturePadWaterMark;
133
+ /**
134
+ * Set Component in view mode.
135
+ */
136
+ viewMode?: boolean;
137
+ }
138
+
139
+ export interface MSignaturePadMethods {
140
+ /**
141
+ * Save image as PNG/JPEG/SVG
142
+ * @param type
143
+ */
144
+ save: (type: 'image/jpeg' | 'image/svg+xml') => string;
145
+ /**
146
+ * Clear canvas
147
+ */
148
+ clear: () => void;
149
+ /**
150
+ * Returns true if canvas is empty.
151
+ * otherwise returns false
152
+ */
153
+ isEmpty: () => boolean;
154
+ /**
155
+ * Remove the last step of pen.
156
+ */
157
+ undo: () => void;
158
+ /**
159
+ * Reset Pad state.
160
+ */
161
+ reset: () => void;
162
+ /**
163
+ * Add Text Watermark.
164
+ */
165
+ addWaterMark: (opt: SignaturePadWaterMark) => void;
166
+ /**
167
+ * Draws signature image from data URL.
168
+ */
169
+ fromDataURL: (url: string) => void;
170
+ }
171
+
172
+ export interface MSignaturePadSlots {
173
+ 'top-input': () => VNode[];
174
+ default: () => VNode[];
175
+ pad: (scope: {
176
+ width: MSignaturePadProps['width'];
177
+ height: MSignaturePadProps['height'];
178
+ reset: MSignaturePadMethods['reset'];
179
+ save: MSignaturePadMethods['save'];
180
+ clear: MSignaturePadMethods['clear'];
181
+ undo: MSignaturePadMethods['undo']
182
+ }) => VNode[];
183
+ 'bottom-input': () => VNode[];
184
+ }
@@ -43,6 +43,7 @@ import type { MAvatarViewerProps, MAvatarViewerSlots } from './api/MAvatarViewer
43
43
  import type { MTransitionProps, MTransitionsSlots } from './api/MTransition'
44
44
  import type { MAxiosProps, MAxiosSlots } from './api/MAxios'
45
45
  import type { MSelectProps, MSelectSlots } from './api/MSelect'
46
+ import type { MSignaturePadProps, MSignaturePadSlots } from './api/MSignaturePad'
46
47
 
47
48
  export type MBtnProps = QBtnProps & {
48
49
  ariaLabel?: boolean | string | null | undefined;
@@ -989,6 +990,7 @@ declare module '@vue/runtime-core' {
989
990
  MPicker: GlobalComponentConstructor<MPickerProps, MPickerSlots>;
990
991
  MRadio: GlobalComponentConstructor<MRadioProps, MRadioSlots>;
991
992
  MSelect: GlobalComponentConstructor<MSelectProps, MSelectSlots>;
993
+ MSignaturePad: GlobalComponentConstructor<MSignaturePadProps, MSignaturePadSlots>;
992
994
  MTime: GlobalComponentConstructor<MTimeProps, MTimeSlots>;
993
995
  MToggle: GlobalComponentConstructor<MToggleProps, MToggleSlots>;
994
996
  MUploader: GlobalComponentConstructor<MUploaderProps, MUploaderSlots>;
@@ -21,4 +21,5 @@ export * from './theme'
21
21
  export * from './api/MAvatarViewer'
22
22
  export * from './api/MAxios'
23
23
  export * from './api/MSelect'
24
+ export * from './api/MSignaturePad'
24
25
  export * from './api/MTransition'
@@ -44,6 +44,7 @@ import type {
44
44
  import type { MDatatableProps, MDtBtnProps } from './m-datatable'
45
45
  import type { MAvatarViewerProps } from './api/MAvatarViewer'
46
46
  import type { MSelectProps } from './api/MSelect'
47
+ import type { MSignaturePadProps } from './api/MSignaturePad'
47
48
 
48
49
  export interface MythComponentsProps {
49
50
  // Grid.
@@ -123,6 +124,10 @@ export interface MythComponentsProps {
123
124
  * MSelect component.
124
125
  */
125
126
  select?: Partial<MSelectProps>;
127
+ /**
128
+ * MSignaturePad component.
129
+ */
130
+ signaturePad?: Partial<MSignaturePadProps>;
126
131
  /**
127
132
  * MPicker component.
128
133
  */