@ramathibodi/nuxt-commons 0.0.1

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.
Files changed (49) hide show
  1. package/README.md +81 -0
  2. package/dist/module.cjs +5 -0
  3. package/dist/module.d.mts +7 -0
  4. package/dist/module.d.ts +7 -0
  5. package/dist/module.json +8 -0
  6. package/dist/module.mjs +34 -0
  7. package/dist/runtime/components/Alert.vue +52 -0
  8. package/dist/runtime/components/BarcodeReader.vue +98 -0
  9. package/dist/runtime/components/Calendar.vue +99 -0
  10. package/dist/runtime/components/Camera.vue +116 -0
  11. package/dist/runtime/components/ExportCSV.vue +55 -0
  12. package/dist/runtime/components/FileBtn.vue +56 -0
  13. package/dist/runtime/components/ImportCSV.vue +64 -0
  14. package/dist/runtime/components/Pdf/Print.vue +63 -0
  15. package/dist/runtime/components/Pdf/View.vue +70 -0
  16. package/dist/runtime/components/TabsGroup.vue +28 -0
  17. package/dist/runtime/components/TextBarcode.vue +52 -0
  18. package/dist/runtime/components/dialog/Confirm.vue +100 -0
  19. package/dist/runtime/components/dialog/Index.vue +72 -0
  20. package/dist/runtime/components/dialog/Loading.vue +34 -0
  21. package/dist/runtime/components/form/Date.vue +163 -0
  22. package/dist/runtime/components/form/DateTime.vue +107 -0
  23. package/dist/runtime/components/form/File.vue +187 -0
  24. package/dist/runtime/components/form/Login.vue +131 -0
  25. package/dist/runtime/components/form/Pad.vue +179 -0
  26. package/dist/runtime/components/form/SignPad.vue +186 -0
  27. package/dist/runtime/components/form/Time.vue +158 -0
  28. package/dist/runtime/components/form/images/CameraCrop.vue +58 -0
  29. package/dist/runtime/components/form/images/Edit.vue +143 -0
  30. package/dist/runtime/components/form/images/Preview.vue +48 -0
  31. package/dist/runtime/components/label/Date.vue +29 -0
  32. package/dist/runtime/components/label/FormatMoney.vue +29 -0
  33. package/dist/runtime/composables/alert.d.ts +13 -0
  34. package/dist/runtime/composables/alert.mjs +44 -0
  35. package/dist/runtime/composables/utils/validation.d.ts +32 -0
  36. package/dist/runtime/composables/utils/validation.mjs +36 -0
  37. package/dist/runtime/labs/form/EditMobile.vue +153 -0
  38. package/dist/runtime/labs/form/TextFieldMask.vue +43 -0
  39. package/dist/runtime/plugins/vueSignaturePad.d.ts +2 -0
  40. package/dist/runtime/plugins/vueSignaturePad.mjs +5 -0
  41. package/dist/runtime/types/alert.d.ts +11 -0
  42. package/dist/runtime/types/modules.d.ts +5 -0
  43. package/dist/runtime/utils/datetime.d.ts +25 -0
  44. package/dist/runtime/utils/datetime.mjs +166 -0
  45. package/dist/runtime/utils/object.d.ts +8 -0
  46. package/dist/runtime/utils/object.mjs +28 -0
  47. package/dist/types.d.mts +16 -0
  48. package/dist/types.d.ts +16 -0
  49. package/package.json +90 -0
@@ -0,0 +1,158 @@
1
+ <script lang="ts" setup>
2
+ import { ref, watch, watchEffect, nextTick } from 'vue'
3
+ import Datepicker from '@vuepic/vue-datepicker'
4
+ import '@vuepic/vue-datepicker/dist/main.css'
5
+ import { Datetime } from '../../utils/datetime'
6
+
7
+ interface Props {
8
+ readonly?: boolean
9
+ enableSeconds?: boolean
10
+ locale?: 'TH' | 'EN'
11
+ pickerOnly?: boolean
12
+ modelValue?: string | null
13
+ }
14
+
15
+ const props = withDefaults(defineProps<Props>(), {
16
+ readonly: false,
17
+ locale: 'TH',
18
+ pickerOnly: false,
19
+ enableSeconds: false,
20
+ })
21
+
22
+ const emit = defineEmits(['update:modelValue'])
23
+
24
+ const time = ref<string | null>(null)
25
+ const tempTime = ref<string | null>(null)
26
+ const isMenuOpen = ref(false)
27
+ const isTextFieldFocused = ref(false)
28
+ const isTextFieldTyped = ref(false)
29
+
30
+ function onTextFieldFocus(event: Event) {
31
+ isTextFieldFocused.value = true
32
+ nextTick(() => {
33
+ (event.target as HTMLInputElement).select()
34
+ })
35
+ }
36
+
37
+ function onTextFieldTyped() {
38
+ if (!isTextFieldTyped.value) {
39
+ isTextFieldTyped.value = true
40
+ isMenuOpen.value = false
41
+ }
42
+ }
43
+
44
+ function onTextFieldBlur() {
45
+ if (isTextFieldTyped.value) {
46
+ setTime(tempTime.value)
47
+ isTextFieldTyped.value = false
48
+ }
49
+ isTextFieldFocused.value = false
50
+ }
51
+
52
+ function onTextFieldEnterKey() {
53
+ onTextFieldBlur()
54
+ }
55
+
56
+ function onTextFieldClear() {
57
+ reset()
58
+ }
59
+
60
+ function reset() {
61
+ time.value = null
62
+ tempTime.value = null
63
+ }
64
+
65
+ function setTime(TimeString: string | null) {
66
+ const dateTime = Datetime().fromStringTime(TimeString, undefined, props.locale)
67
+ if (!dateTime.luxonDateTime.isValid) {
68
+ tempTime.value = null
69
+ time.value = null
70
+ }
71
+ else {
72
+ time.value = dateTime.toFormat('HH:mm:ss', 'EN')
73
+ tempTime.value = time.value
74
+ }
75
+ }
76
+
77
+ function setDatePicker(DateString: string | null) {
78
+ setTime(DateString)
79
+ }
80
+
81
+ watchEffect(() => {
82
+ if (!isTextFieldFocused.value && time.value) {
83
+ const dateTime = Datetime().fromString(time.value, undefined, props.locale)
84
+ tempTime.value = dateTime.toFormat((props.enableSeconds) ? 'HH:mm:ss' : 'HH:mm', props.locale)
85
+ }
86
+ else {
87
+ tempTime.value = time.value
88
+ }
89
+ })
90
+
91
+ watch(time, (newValue) => {
92
+ if (!isMenuOpen.value) emit('update:modelValue', newValue)
93
+ })
94
+
95
+ watch(isMenuOpen, () => {
96
+ if (isMenuOpen.value && !time.value) {
97
+ time.value = Datetime().now().toFormat((props.enableSeconds) ? 'HH:mm:ss' : 'HH:mm:00')
98
+ }
99
+ if (!isMenuOpen.value) emit('update:modelValue', time.value)
100
+ })
101
+
102
+ watch(() => props.modelValue, () => {
103
+ setTime(props.modelValue || null)
104
+ }, { immediate: true })
105
+
106
+ const isOpenMenu = (value: string) => {
107
+ if (value === 'txtField' && props.pickerOnly) {
108
+ isMenuOpen.value = true
109
+ }
110
+ else if (value === 'icon' && !props.pickerOnly) {
111
+ isMenuOpen.value = true
112
+ }
113
+ }
114
+ </script>
115
+
116
+ <template>
117
+ <v-menu
118
+ v-model="isMenuOpen"
119
+ :close-on-content-click="false"
120
+ :open-on-click="false"
121
+ >
122
+ <template #activator="{ props }">
123
+ <v-text-field
124
+ ref="textField"
125
+ v-model="tempTime"
126
+ :readonly="readonly"
127
+ v-bind="$attrs"
128
+ @focus="onTextFieldFocus"
129
+ @blur="onTextFieldBlur"
130
+ @keydown="onTextFieldTyped"
131
+ @keyup.enter="onTextFieldEnterKey"
132
+ @click:clear="onTextFieldClear"
133
+ @click="isOpenMenu('txtField')"
134
+ >
135
+ <template #append-inner>
136
+ <v-icon
137
+ v-bind="props"
138
+ @click="isOpenMenu('icon')"
139
+ >
140
+ fa:fa-regular fa-clock
141
+ </v-icon>
142
+ </template>
143
+ </v-text-field>
144
+ </template>
145
+ <Datepicker
146
+ v-model="time"
147
+ model-type="HH:mm:ss"
148
+ :enable-seconds="enableSeconds"
149
+ minutes-grid-increment="1"
150
+ time-picker
151
+ auto-apply
152
+ :close-on-auto-apply="false"
153
+ inline
154
+ :locale="locale"
155
+ @update:model-value="setDatePicker"
156
+ />
157
+ </v-menu>
158
+ </template>
@@ -0,0 +1,58 @@
1
+ <script lang="ts" setup>
2
+ import { ref } from 'vue'
3
+ import { isUndefined } from 'lodash'
4
+
5
+ interface Props {
6
+ modelValue?: string
7
+ }
8
+
9
+ const props = defineProps<Props>()
10
+ const emit = defineEmits(['update:modelValue', 'closeDialog', 'openCamera'])
11
+
12
+ const selectedImage = ref<string>()
13
+ const isDialogOpen = ref<boolean>(false)
14
+ const cameraRef = ref()
15
+
16
+ const handleAddImage = (image: string) => {
17
+ selectedImage.value = image
18
+ isDialogOpen.value = true
19
+ }
20
+
21
+ const handleCloseDialog = () => {
22
+ isDialogOpen.value = false
23
+ emit("openCamera", true);
24
+ emit('closeDialog', false)
25
+ }
26
+
27
+ const handleCameraClose = (shouldClose: boolean, editStatus: string) => {
28
+ if (isUndefined(editStatus)) {
29
+ emit('closeDialog', shouldClose)
30
+ }
31
+ }
32
+
33
+ const handleImageEdit = (imageData: any) => {
34
+ emit('update:modelValue', imageData)
35
+ }
36
+
37
+ const handleOpenCamera = () => {
38
+ emit('openCamera', true)
39
+ isDialogOpen.value = false
40
+ cameraRef.value.openCamera()
41
+ }
42
+ </script>
43
+
44
+ <template>
45
+ <Camera
46
+ ref="cameraRef"
47
+ @update:model-value="handleAddImage"
48
+ @close-dialog="handleCameraClose"
49
+ />
50
+ <v-dialog v-model="isDialogOpen">
51
+ <FormImagesEdit
52
+ v-model="selectedImage"
53
+ @update:model-value="handleImageEdit"
54
+ @close-dialog="handleCloseDialog"
55
+ @open-camera="handleOpenCamera"
56
+ />
57
+ </v-dialog>
58
+ </template>
@@ -0,0 +1,143 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted, watch } from 'vue'
3
+ import Cropper from 'cropperjs'
4
+ import 'cropperjs/dist/cropper.css'
5
+
6
+ interface Props {
7
+ modelValue?: string
8
+ readonly?: boolean
9
+ flat?: boolean
10
+ }
11
+
12
+ const props = withDefaults(defineProps<Props>(), {
13
+ readonly: false,
14
+ flat: false,
15
+ })
16
+
17
+ const emit = defineEmits(['update:modelValue', 'closeDialog', 'openCamera'])
18
+
19
+ const cropper = ref<Cropper | null>(null)
20
+ const imageCanvas = ref<HTMLCanvasElement | null>(null)
21
+
22
+ const loadImageToCanvas = () => {
23
+ if (imageCanvas.value && props.modelValue) {
24
+ const canvas = imageCanvas.value as HTMLCanvasElement
25
+ const context = canvas.getContext('2d')
26
+ const image = new Image()
27
+ image.src = props.modelValue
28
+ image.onload = () => {
29
+ canvas.width = image.width
30
+ canvas.height = image.height
31
+ context?.clearRect(0, 0, canvas.width, canvas.height)
32
+ context?.drawImage(image, 0, 0)
33
+ if (!props.readonly) {
34
+ cropper.value?.destroy()
35
+ cropper.value = new Cropper(canvas, {
36
+ aspectRatio: 1,
37
+ rotatable: true,
38
+ })
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ onMounted(() => {
45
+ loadImageToCanvas()
46
+ })
47
+
48
+ watch(() => props.modelValue, (newValue, oldValue) => {
49
+ if (newValue !== oldValue) {
50
+ loadImageToCanvas()
51
+ }
52
+ })
53
+
54
+ const handleCrop = () => {
55
+ const croppedCanvas = cropper.value?.getCroppedCanvas()
56
+ if (croppedCanvas) {
57
+ const dataURL = croppedCanvas.toDataURL()
58
+ emit('update:modelValue', dataURL)
59
+ emit("closeDialog", false);
60
+ }
61
+ }
62
+
63
+ const resetCrop = () => {
64
+ emit("closeDialog", false);
65
+ emit("openCamera", true);
66
+ }
67
+
68
+ const close = () => {
69
+ emit('closeDialog', false)
70
+ }
71
+ </script>
72
+
73
+ <template>
74
+ <v-card :flat="flat">
75
+ <v-card-title>
76
+ <v-row
77
+ align="center"
78
+ justify="end"
79
+ >
80
+ <slot
81
+ name="closeButton"
82
+ :close="close"
83
+ >
84
+ <v-btn
85
+ icon="mdi mdi-close"
86
+ variant="text"
87
+ @click="close"
88
+ />
89
+ </slot>
90
+ </v-row>
91
+ </v-card-title>
92
+ <v-card-text>
93
+ <v-row
94
+ v-if="props.readonly"
95
+ justify="center"
96
+ >
97
+ <v-img
98
+ :src="props.modelValue as string"
99
+ height="70vh"
100
+ />
101
+ </v-row>
102
+ <v-row
103
+ v-else
104
+ justify="center"
105
+ >
106
+ <v-col cols="12">
107
+ <canvas
108
+ ref="imageCanvas"
109
+ class="canvas-photo"
110
+ style="max-height: 70vh; max-width: 40vh"
111
+ />
112
+ <v-row
113
+ justify="center"
114
+ class="mt-2"
115
+ >
116
+ <slot
117
+ name="actionButtons"
118
+ :reset-crop="resetCrop"
119
+ :handle-crop="handleCrop"
120
+ >
121
+ <v-btn
122
+ class="ma-1"
123
+ prepend-icon="mdi mdi-camera"
124
+ color="primary"
125
+ @click="resetCrop"
126
+ >
127
+ Retake
128
+ </v-btn>
129
+ <v-btn
130
+ class="ma-1"
131
+ prepend-icon="fa-solid fa-check"
132
+ color="success"
133
+ @click="handleCrop"
134
+ >
135
+ Done
136
+ </v-btn>
137
+ </slot>
138
+ </v-row>
139
+ </v-col>
140
+ </v-row>
141
+ </v-card-text>
142
+ </v-card>
143
+ </template>
@@ -0,0 +1,48 @@
1
+ <script lang="ts" setup>
2
+ interface Props {
3
+ modelValue?: any
4
+ readonly?: boolean
5
+ }
6
+
7
+ const props = withDefaults(defineProps<Props>(), {
8
+ readonly: false,
9
+ })
10
+
11
+ const emit = defineEmits([
12
+ 'update:modelValue',
13
+ 'closeDialogPreview',
14
+ 'toPaint',
15
+ ])
16
+
17
+ const editImage = () => {
18
+ emit('toPaint')
19
+ }
20
+ </script>
21
+
22
+ <template>
23
+ <v-card>
24
+ <v-toolbar>
25
+ <v-toolbar-title>Preview</v-toolbar-title>
26
+ <v-spacer />
27
+ <v-btn
28
+ v-if="!props.readonly"
29
+ icon="fa:fa-solid fa-pen"
30
+ variant="text"
31
+ @click="editImage"
32
+ />
33
+ <v-btn
34
+ icon="fa:fa-solid fa-xmark"
35
+ variant="text"
36
+ @click="$emit('closeDialogPreview', false)"
37
+ />
38
+ </v-toolbar>
39
+ <v-card-text>
40
+ <v-row>
41
+ <v-img
42
+ :src="modelValue.data"
43
+ height="70dvh"
44
+ />
45
+ </v-row>
46
+ </v-card-text>
47
+ </v-card>
48
+ </template>
@@ -0,0 +1,29 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+ import { type dateFormat, type dateTimeFormat, Datetime } from '../../utils/datetime'
4
+
5
+ interface Props {
6
+ modelValue?: string
7
+ locale?: 'TH' | 'EN'
8
+ fromFormat?: string
9
+ format?: dateFormat | dateTimeFormat | string
10
+ }
11
+
12
+ const props = withDefaults(defineProps<Props>(), {
13
+ locale: 'TH',
14
+ format: 'shortDate',
15
+ })
16
+
17
+ const formattedDate = computed(() => {
18
+ if (!props.modelValue) {
19
+ return null
20
+ }
21
+
22
+ const dateTime = Datetime().fromString(props.modelValue, props.fromFormat)
23
+ return dateTime.luxonDateTime.isValid ? dateTime.toFormat(props.format, props.locale) : props.modelValue
24
+ })
25
+ </script>
26
+
27
+ <template>
28
+ {{ formattedDate }}
29
+ </template>
@@ -0,0 +1,29 @@
1
+ <script lang="ts" setup>
2
+ import currency from 'currency.js'
3
+ import { computed } from 'vue'
4
+
5
+ interface Props {
6
+ modelValue: number
7
+ decimal?: number
8
+ currency?: string
9
+ }
10
+
11
+ const props = withDefaults(defineProps<Props>(), {
12
+ modelValue: 0,
13
+ decimal: 2,
14
+ currency: 'USD',
15
+ })
16
+
17
+ const formattedCurrency = computed(() => {
18
+ return currency(props.modelValue, {
19
+ pattern: `# !`,
20
+ symbol: props.currency,
21
+ separator: ',',
22
+ precision: props.decimal,
23
+ }).format()
24
+ })
25
+ </script>
26
+
27
+ <template>
28
+ {{ formattedCurrency }}
29
+ </template>
@@ -0,0 +1,13 @@
1
+ import { type InjectionKey, type Ref } from 'vue';
2
+ import type { AlertItem } from '../types/alert';
3
+ interface AlertProvide {
4
+ addAlert: (item: Partial<AlertItem>) => void;
5
+ takeAlert: (location?: string) => AlertItem | undefined;
6
+ hasAlert: (location?: string) => boolean;
7
+ clear: () => void;
8
+ items: Ref<AlertItem[]>;
9
+ }
10
+ export declare const AlertKey: InjectionKey<AlertProvide>;
11
+ export declare function createAlert(): void;
12
+ export declare function useAlert(): AlertProvide | null;
13
+ export {};
@@ -0,0 +1,44 @@
1
+ import { inject, provide, ref } from "vue";
2
+ import { head, isUndefined, now, remove } from "lodash-es";
3
+ export const AlertKey = Symbol.for("ramacare:alert");
4
+ export function createAlert() {
5
+ const items = ref([]);
6
+ provide(AlertKey, {
7
+ addAlert(item) {
8
+ const defaultValues = {
9
+ statusCode: 0,
10
+ statusMessage: "",
11
+ alertLocation: "default",
12
+ alertType: "info",
13
+ alertIcon: "",
14
+ alertDateTime: now(),
15
+ alertExpireIn: 1e3 * 60,
16
+ // 1 minute
17
+ data: {}
18
+ };
19
+ const newItem = { ...defaultValues, ...item };
20
+ if (!isUndefined(newItem.message)) {
21
+ items.value.push(newItem);
22
+ }
23
+ },
24
+ takeAlert(location = "default") {
25
+ const item = head(items.value.filter((i) => i.alertLocation === location));
26
+ if (item)
27
+ remove(items.value, (i) => i === item);
28
+ return item;
29
+ },
30
+ hasAlert(location = "default") {
31
+ return items.value.some((i) => i.alertLocation === location);
32
+ },
33
+ clear() {
34
+ items.value = [];
35
+ },
36
+ items
37
+ });
38
+ setInterval(() => {
39
+ items.value = items.value.filter((i) => Number(i.alertDateTime) + Number(i.alertExpireIn) > now());
40
+ }, 1e3);
41
+ }
42
+ export function useAlert() {
43
+ return inject(AlertKey, null);
44
+ }
@@ -0,0 +1,32 @@
1
+ export declare function useRules(): {
2
+ require: (customError?: string) => (value: any) => string | true;
3
+ requireIf: (conditionIf: boolean, customError?: string) => (value: any) => string | true;
4
+ requireTrue: (customError?: string) => (value: any) => string | true;
5
+ requireTrueIf: (conditionIf: boolean, customError?: string) => (value: any) => string | true;
6
+ numeric: (customError?: string) => (value: any) => string | true;
7
+ range: (minValue: number, maxValue: number, customError?: string) => (value: any) => string | true;
8
+ integer: (customError?: string) => (value: any) => string | true;
9
+ unique: (data: Array<any> | object, fieldName: string, customError?: string) => (value: any) => string | true;
10
+ length: (length: number, customError?: string) => (value: any) => string | true;
11
+ lengthGreater: (length: number, customError?: string) => (value: any) => string | true;
12
+ lengthLess: (length: number, customError?: string) => (value: any) => string | true;
13
+ telephone: (customError?: string) => (value: any) => string | true;
14
+ email: (customError?: string) => (value: any) => string | true;
15
+ regex: (regex: string, customError?: string) => (value: any) => string | true;
16
+ rules: import("vue").Ref<{
17
+ require: (customError?: string) => (value: any) => string | true;
18
+ requireIf: (conditionIf: boolean, customError?: string) => (value: any) => string | true;
19
+ requireTrue: (customError?: string) => (value: any) => string | true;
20
+ requireTrueIf: (conditionIf: boolean, customError?: string) => (value: any) => string | true;
21
+ numeric: (customError?: string) => (value: any) => string | true;
22
+ range: (minValue: number, maxValue: number, customError?: string) => (value: any) => string | true;
23
+ integer: (customError?: string) => (value: any) => string | true;
24
+ unique: (data: Array<any> | object, fieldName: string, customError?: string) => (value: any) => string | true;
25
+ length: (length: number, customError?: string) => (value: any) => string | true;
26
+ lengthGreater: (length: number, customError?: string) => (value: any) => string | true;
27
+ lengthLess: (length: number, customError?: string) => (value: any) => string | true;
28
+ telephone: (customError?: string) => (value: any) => string | true;
29
+ email: (customError?: string) => (value: any) => string | true;
30
+ regex: (regex: string, customError?: string) => (value: any) => string | true;
31
+ }>;
32
+ };
@@ -0,0 +1,36 @@
1
+ import { ref } from "vue";
2
+ import { isInteger, find } from "lodash-es";
3
+ export function useRules() {
4
+ const condition = (condition2, customError) => condition2 || customError;
5
+ const require = (customError = "This field is required") => (value) => condition(!!value || value === false || value === 0, customError);
6
+ const requireIf = (conditionIf, customError = "This field is required") => (value) => condition(!!value || value === false || value === 0 || !conditionIf, customError);
7
+ const requireTrue = (customError = "This field must be true") => (value) => condition(!!value, customError);
8
+ const requireTrueIf = (conditionIf, customError = "This field must be true") => (value) => condition(!!value || !conditionIf, customError);
9
+ const numeric = (customError = "This field must be a number") => (value) => condition(!value || !Number.isNaN(value), customError);
10
+ const range = (minValue, maxValue, customError = `Value is out of range (${minValue}-${maxValue})`) => (value) => condition(!value || value >= minValue && value <= maxValue, customError);
11
+ const integer = (customError = "This field must be an integer") => (value) => condition(!value || isInteger(value) || /^\+?-?\d+$/.test(value), customError);
12
+ const unique = (data, fieldName, customError = "This field must be unique") => (value) => condition(!value || !data || !find(data, [fieldName, value]), customError);
13
+ const length = (length2, customError = `Length must be ${length2}`) => (value) => condition(!value || value.length == length2, customError);
14
+ const lengthGreater = (length2, customError = `Length must be greater than ${length2}`) => (value) => condition(!value || value.length >= length2, customError);
15
+ const lengthLess = (length2, customError = `Length must be less than ${length2}`) => (value) => condition(!value || value.length <= length2, customError);
16
+ const telephone = (customError = "Invalid telephone number") => (value) => condition(!value || /^(?:\s*(?:0[23457][0-9]{7}|0[689][0-9]{8})(?:#[0-9]{1,5})?\s*(?:\([^()]+\))?\s*(?:,(?=.+))?)*$/.test(value), customError);
17
+ const email = (customError = "Invalid email address") => (value) => condition(!value || /^([a-zA-Z0-9_\-.]+)@([a-zA-Z0-9_\-.]+)\.([a-zA-Z]{2,5})$/.test(value), customError);
18
+ const regex = (regex2, customError = "Invalid format") => (value) => condition(!value || new RegExp(regex2).test(value), customError);
19
+ const rules = ref({
20
+ require,
21
+ requireIf,
22
+ requireTrue,
23
+ requireTrueIf,
24
+ numeric,
25
+ range,
26
+ integer,
27
+ unique,
28
+ length,
29
+ lengthGreater,
30
+ lengthLess,
31
+ telephone,
32
+ email,
33
+ regex
34
+ });
35
+ return { rules, ...rules.value };
36
+ }