Skip to content

Commit 0bf9e2b

Browse files
author
anhle
committed
🏷️ JS to TS
1 parent 1001b70 commit 0bf9e2b

13 files changed

+291
-108
lines changed

example/App.vue

+117-37
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,150 @@
11
<template>
2-
<div>
3-
<div>
2+
<div class="form">
3+
<div class="form-item">
44
<label>First Name</label>
5-
<input v-model="info.firstName" />
5+
<input v-model="info.firstName" :class="{ 'input-error': result.firstName.$invalid }" />
6+
<div v-if="result.firstName.$invalid" class="form-item-message">{{ result.firstName.$messages }}</div>
67
</div>
7-
<div>
8+
<div class="form-item">
89
<label>Last Name</label>
9-
<input v-model="info.lastName" />
10+
<input v-model="info.lastName" :class="{ 'input-error': result.lastName.$invalid }" />
11+
<div v-if="result.lastName.$invalid" class="form-item-message">{{ result.lastName.$messages }}</div>
1012
</div>
11-
<div>
13+
<div class="form-item">
1214
<label>District</label>
13-
<input v-model="info.address.district" />
15+
<input v-model="info.address.district" :class="{ 'input-error': result.address.district.$invalid }" />
16+
<div v-if="result.address.district.$invalid" class="form-item-message">
17+
{{ result.address.district.$messages }}
18+
</div>
1419
</div>
15-
<div>
20+
<div class="form-item">
1621
<label>Street</label>
17-
<input v-model="info.address.street" />
22+
<input v-model="info.address.street" :class="{ 'input-error': result.address.street.$invalid }" />
23+
<div v-if="result.address.street.$invalid" class="form-item-message">{{ result.address.street.$messages }}</div>
1824
</div>
19-
<div>
20-
<button @click="result.address.street.$test()">Test</button>
25+
<div class="form-item">
26+
<label>Number</label>
27+
<input v-model="info.address.no" :class="{ 'input-error': result.address.no.$invalid }" />
28+
<div v-if="result.address.no.$invalid" class="form-item-message">{{ result.address.no.$messages }}</div>
2129
</div>
22-
<div>{{ result }}</div>
30+
<button class="validate-btn" @click="validate">Validate</button>
31+
</div>
32+
<div class="tree">
33+
<JsonTreeView :data="JSON.stringify(result)" />
2334
</div>
2435
</template>
2536

26-
<script>
27-
import { ref, reactive } from 'vue';
28-
import useValidate from 'vue-tiny-validate';
29-
export default {
37+
<script lang="ts">
38+
import { ref, reactive, defineComponent } from 'vue';
39+
import { JsonTreeView } from 'json-tree-view-vue3';
40+
import useValidate from '../src';
41+
export default defineComponent({
3042
name: 'App',
43+
components: {
44+
JsonTreeView,
45+
},
3146
setup() {
3247
const info = reactive({
33-
firstName: 'Duy Anh',
34-
lastName: 'Le',
48+
firstName: '',
49+
lastName: '',
3550
address: {
36-
district: 'Thanh Xuan',
37-
street: 'Quan Nhan',
51+
district: '',
52+
street: '',
53+
no: '',
3854
},
3955
});
4056
41-
const rules = reactive({
42-
firstName: [{ $test: value => value !== '', $message: 'This field is required' }],
43-
lastName: [{ $test: value => value !== '', $message: 'This field is required' }],
57+
const required = (value: string): boolean => value !== '';
58+
59+
const atLeastTenChars = (value: string): boolean => String(value).length >= 10;
60+
61+
const isNumber = (value: string): boolean => value !== '' && !isNaN(Number(value));
62+
63+
const isNumberMessage = (value: string): string => {
64+
return `Input ${value} is not allowed. A number is required`;
65+
};
66+
67+
const rules = {
68+
firstName: [{ $test: required, $message: 'Input is required' }],
69+
lastName: [{ $test: required, $message: 'Input is required' }],
4470
address: {
45-
district: [
46-
{
47-
$test: value => value.length > 5,
48-
$message: 'This field must have at least 5 characters',
49-
},
50-
],
71+
district: [{ $test: required, $message: 'Input is required' }],
5172
street: [
52-
{
53-
$test: value => value.length > 10,
54-
$message: 'This field must have at least 10 characters',
55-
},
73+
{ $test: required, $message: 'Input is required' },
74+
{ $test: atLeastTenChars, $message: 'At least 10 chars' },
75+
],
76+
no: [
77+
{ $test: required, $message: 'Input is required' },
78+
{ $test: isNumber, $message: isNumberMessage },
5679
],
5780
},
58-
});
81+
};
5982
60-
const { result, test } = useValidate(info, rules);
83+
const { result } = useValidate(info, rules);
6184
62-
return { info, result, test };
85+
const validate = () => {
86+
result.value.$test();
87+
};
88+
89+
return { info, result, validate };
6390
},
64-
};
91+
});
6592
</script>
6693
<style>
94+
* {
95+
box-sizing: border-box;
96+
}
97+
body {
98+
margin: 16px;
99+
}
100+
101+
.form-item {
102+
margin-bottom: 24px;
103+
position: relative;
104+
}
105+
106+
.form-item-message {
107+
position: absolute;
108+
bottom: -20px;
109+
color: mediumvioletred;
110+
font-size: 14px;
111+
}
112+
113+
.root-item {
114+
font-family: monospace;
115+
border: 1px solid lightgray;
116+
border-radius: 4px;
117+
}
118+
119+
.json-view-item:not(.root-item) {
120+
margin-left: 32px;
121+
}
122+
123+
.validate-btn {
124+
display: block;
125+
font-size: 16px;
126+
background: lightgray;
127+
border-radius: 4px;
128+
border: 1px solid grey;
129+
margin-bottom: 16px;
130+
padding: 8px;
131+
}
132+
67133
label {
68134
display: block;
135+
margin-bottom: 4px;
136+
}
137+
138+
input {
139+
display: inline-block;
140+
width: 100%;
141+
padding: 8px 16px;
142+
margin: 0;
143+
border-radius: 4px;
144+
border: 1px solid lightgray;
145+
}
146+
147+
.input-error {
148+
border-color: red;
69149
}
70150
</style>

example/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
</head>
88
<body>
99
<div id="app"></div>
10-
<script type="module" src="/main.js"></script>
10+
<script type="module" src="/main.ts"></script>
1111
</body>
1212
</html>

example/main.js

-4
This file was deleted.

example/main.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createApp } from 'vue'
2+
import App from './App.vue'
3+
4+
createApp(App).mount('#app')

example/shims-vue.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
declare module '*.vue' {
2+
import { DefineComponent } from 'vue'
3+
const component: DefineComponent<{}, {}, any>
4+
export default component
5+
}

example/vite.env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424
},
2525
"homepage": "https://github.com/FrontLabsOfficial/vue-tiny-validate/tree/master#readme",
2626
"scripts": {
27+
"type": "tsc src/index.ts --declaration --emitDeclarationOnly --skipLibCheck --outDir dist",
2728
"clean": "rm -rf dist dist-example",
2829
"pretty": "prettier --write '**/*.{js,css,md,vue,html}'",
2930
"dev": "vite",
30-
"build": "yarn clean && yarn pretty && vite build --mode library && yarn size",
31+
"build": "yarn clean && yarn pretty && vite build --mode library && yarn type && yarn size",
3132
"build:example": "year clean && yarn pretty && vite build --mode example",
3233
"prepare": "husky install",
3334
"size": "size-limit",
@@ -38,10 +39,13 @@
3839
"@vitejs/plugin-vue": "^1.2.3",
3940
"@vue/compiler-sfc": "^3.0.5",
4041
"husky": ">=6",
42+
"json-tree-view-vue3": "^0.1.15",
4143
"lint-staged": ">=10",
4244
"np": "^7.5.0",
4345
"prettier": "^2.3.0",
46+
"sass": "^1.34.1",
4447
"size-limit": "^4.11.0",
48+
"typescript": "^4.3.2",
4549
"vite": "^2.3.5",
4650
"vue": "^3.0.11"
4751
},

src/helpers.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const TEST_FUNCTION = (): boolean => true;
2+
3+
export const ERROR_MESSAGE: string = '';
4+
5+
export const RESULT = { $invalid: false, $errors: [], $messages: [], $test: () => {}, $reset: () => {} };
6+
7+
export const hasOwn = (obj: { [key: string]: any }, key: string): boolean => typeof obj[key] !== 'undefined';
8+
9+
export const isObject = (obj: { [key: string]: any }): boolean => Object.prototype.toString.call(obj) === '[object Object]';

src/index.js renamed to src/index.ts

+31-44
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,21 @@
1-
import { ref, computed, reactive, watch, isRef } from 'vue';
1+
import { computed, reactive } from 'vue';
2+
import { TEST_FUNCTION, ERROR_MESSAGE, RESULT, hasOwn, isObject } from './helpers'
3+
import { Data, Rules, Rule, Dirt, Entry, UnknownObject, Result, InitializeArgs, EntryData, EntryItem, Error } from './types'
24

3-
const TEST_FUNCTION = () => true;
5+
const useValidate = (data: Data, rules: Rules) => {
6+
const dirt = reactive<Dirt>({});
7+
const rawData = reactive<UnknownObject>({});
8+
const entry = reactive<Entry>({});
9+
const result = computed<Result>(() => getResult(entry, dirt));
410

5-
const ERROR_MESSAGE = '';
11+
const getResult = (entry: Entry, dirt: Dirt): Result => {
12+
const result: Result = { ...RESULT, $dirty: false };
13+
const keys: Array<string> = Object.keys(entry);
614

7-
const RESULT = { $invalid: false, $errors: [], $messages: [] };
15+
let testFns: Array<Function> = [];
16+
let resetFns: Array<Function> = [];
817

9-
const hasOwn = (obj, key) => typeof obj[key] !== 'undefined';
10-
11-
const isObject = obj => Object.prototype.toString.call(obj) === '[object Object]';
12-
13-
const useValidate = (data, rules) => {
14-
const dirt = reactive({});
15-
16-
const rawData = reactive({});
17-
18-
const entry = reactive({});
19-
20-
const result = computed(() => getResult(entry, dirt));
21-
22-
const getResult = (entry, dirt) => {
23-
const result = {
24-
...RESULT,
25-
$dirty: false,
26-
};
27-
28-
const keys = Object.keys(entry);
29-
30-
let testFns = [];
31-
let resetFns = [];
32-
33-
const setOverallResult = (result, childResult) => {
18+
const setOverallResult = (result: Result, childResult: Result): void => {
3419
if (!result.$dirty && childResult.$dirty) result.$dirty = true;
3520
if (!result.$invalid && childResult.$invalid) result.$invalid = true;
3621

@@ -43,7 +28,7 @@ const useValidate = (data, rules) => {
4328

4429
keys.forEach(key => {
4530
if (isObject(entry[key]) && !hasOwn(entry[key], '$invalid')) {
46-
const childResult = getResult(entry[key], dirt[key]);
31+
const childResult = getResult(entry[key] as Entry, dirt[key] as Dirt);
4732
result[key] = { ...childResult };
4833

4934
setOverallResult(result, childResult);
@@ -66,24 +51,24 @@ const useValidate = (data, rules) => {
6651
return result;
6752
};
6853

69-
const initialize = (data, rules, dirt, rawData, entry) => {
70-
const keys = Object.keys(data);
54+
const initialize = (data: Data, rules: Rules, dirt: Dirt, rawData: UnknownObject, entry: Entry): void => {
55+
const keys: Array<string> = Object.keys(data);
7156

72-
keys.forEach(key => {
57+
keys.forEach((key) => {
7358
if (isObject(data[key])) {
7459
rawData[key] = {};
7560
dirt[key] = reactive({});
7661
entry[key] = reactive({});
7762

78-
const args = [data[key], rules[key], dirt[key], rawData[key], entry[key]];
63+
const args :InitializeArgs = [data[key], rules[key] as Rules, dirt[key] as Dirt, rawData[key], entry[key] as Entry];
7964

8065
return initialize(...args);
8166
}
8267

8368
dirt[key] = false;
8469
rawData[key] = data[key];
8570

86-
const entryData = { data, rules, dirt, rawData, entry };
71+
const entryData: EntryData = { data, rules, dirt, rawData, entry };
8772

8873
entry[key] = {
8974
...RESULT,
@@ -93,34 +78,36 @@ const useValidate = (data, rules) => {
9378
});
9479
};
9580

96-
const test = (entryData, key) => {
81+
const test = (entryData: EntryData, key: string): void => {
9782
const { data, rules, dirt, rawData, entry } = entryData;
9883

9984
dirt[key] = dirt[key] || data[key] !== rawData[key];
10085

101-
let $errors = [];
102-
let $messages = [];
86+
let $errors: Array<Error> = [];
87+
let $messages: Array<string> = [];
88+
89+
const ruleItem = rules[key] as Array<Rule>
10390

104-
rules[key].forEach((rule, index) => {
105-
const { $test = DEFAULT_TEST_FUNCTION, $message = DEFAULT_ERROR_MESSAGE } = rule;
91+
ruleItem.forEach((rule, index) => {
92+
const { $test = TEST_FUNCTION, $message = ERROR_MESSAGE } = rule;
10693
const testValue = $test(data[key]);
10794

10895
if (!testValue) {
109-
const testMessage = typeof $message === 'function' ? $message(data) : $message;
96+
const testMessage = typeof $message === 'function' ? $message(data[key]) : $message;
11097
$messages = [...$messages, testMessage];
11198
$errors = [...$errors, { name: $test.name, index }];
11299
}
113100
});
114101

115-
entry[key] = { ...entry[key], $errors, $messages, $invalid: Boolean($errors.length) };
102+
entry[key] = { ...entry[key], $errors, $messages, $invalid: Boolean($errors.length) } as EntryItem;
116103
};
117104

118-
const reset = (entryData, key) => {
105+
const reset = (entryData: EntryData, key: string): void => {
119106
const { dirt } = entryData;
120107
dirt[key] = false;
121108
};
122109

123-
initialize(isRef(data) ? data.value : data, rules, dirt, rawData, entry);
110+
initialize(data, rules, dirt, rawData, entry);
124111

125112
return { result, test: result.value.$test, reset: result.value.$reset };
126113
};

0 commit comments

Comments
 (0)