@naptics/vue-collection 0.1.3 → 0.1.4
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/i18n/index.js +4 -4
- package/index.js +1 -1
- package/package.json +1 -1
- package/utils/breakpoints.js +21 -21
- package/utils/component.js +75 -0
- package/utils/deferred.js +12 -12
- package/utils/identifiable.js +28 -31
- package/utils/stringMaxLength.js +8 -13
- package/utils/tailwind.js +1 -1
- package/utils/utils.js +7 -7
- package/utils/vModel.js +82 -73
- package/utils/validation.js +55 -81
- package/utils/vue.js +7 -5
package/i18n/index.js
CHANGED
|
@@ -6,7 +6,7 @@ let provider = undefined;
|
|
|
6
6
|
* @param newProvider
|
|
7
7
|
*/
|
|
8
8
|
export function registerTranslationProvider(newProvider) {
|
|
9
|
-
|
|
9
|
+
provider = newProvider;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* Translates the specified key with the according message.
|
|
@@ -15,7 +15,7 @@ export function registerTranslationProvider(newProvider) {
|
|
|
15
15
|
* @returns the translated message.
|
|
16
16
|
*/
|
|
17
17
|
export function trsl(key, params) {
|
|
18
|
-
|
|
18
|
+
return provider?.trsl(key, params) ?? key;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Translates the specified key using pluralization.
|
|
@@ -27,5 +27,5 @@ export function trsl(key, params) {
|
|
|
27
27
|
* @see trsl
|
|
28
28
|
*/
|
|
29
29
|
export function trslc(key, count, params) {
|
|
30
|
-
|
|
31
|
-
}
|
|
30
|
+
return provider?.trslc(key, count, params) ?? key;
|
|
31
|
+
}
|
package/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createComponent, createView } from './utils/component';
|
|
2
|
-
export { createComponent, createView };
|
|
2
|
+
export { createComponent, createView };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@naptics/vue-collection",
|
|
3
3
|
"author": "Timo Siegenthaler",
|
|
4
4
|
"description": "Vue Collection is a collection of styled and fully functional Vue components which can easily be integrated into our projects.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.4",
|
|
6
6
|
"main": "./index.js",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
package/utils/breakpoints.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { computed, ref } from 'vue';
|
|
2
2
|
export const breakpoints = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
sm: 640,
|
|
4
|
+
md: 768,
|
|
5
|
+
lg: 1024,
|
|
6
|
+
xl: 1280,
|
|
7
|
+
'2xl': 1536
|
|
8
8
|
};
|
|
9
9
|
export const bodyWidth = ref(document.body.clientWidth);
|
|
10
10
|
/**
|
|
@@ -12,29 +12,29 @@ export const bodyWidth = ref(document.body.clientWidth);
|
|
|
12
12
|
* It sets a `window.onresize` listener.
|
|
13
13
|
*/
|
|
14
14
|
export function addDocumentResizeEventListener() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
window.onresize = () => {
|
|
16
|
+
bodyWidth.value = document.body.clientWidth;
|
|
17
|
+
};
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Returns a ref whose value is `true` as long as the `document.body.clientWitdh` is above the specified breakpoint.
|
|
21
21
|
*/
|
|
22
22
|
export function isWidthBreakpoint(breakpoint) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
switch (breakpoint) {
|
|
24
|
+
case 'sm':
|
|
25
|
+
return isWidthSm;
|
|
26
|
+
case 'md':
|
|
27
|
+
return isWidthMd;
|
|
28
|
+
case 'lg':
|
|
29
|
+
return isWidthLg;
|
|
30
|
+
case 'xl':
|
|
31
|
+
return isWidthXl;
|
|
32
|
+
case '2xl':
|
|
33
|
+
return isWidth2xl;
|
|
34
|
+
}
|
|
35
35
|
}
|
|
36
36
|
export const isWidth2xl = computed(() => bodyWidth.value > breakpoints['2xl']);
|
|
37
37
|
export const isWidthXl = computed(() => bodyWidth.value > breakpoints.xl);
|
|
38
38
|
export const isWidthLg = computed(() => bodyWidth.value > breakpoints.lg);
|
|
39
39
|
export const isWidthMd = computed(() => bodyWidth.value > breakpoints.md);
|
|
40
|
-
export const isWidthSm = computed(() => bodyWidth.value > breakpoints.sm);
|
|
40
|
+
export const isWidthSm = computed(() => bodyWidth.value > breakpoints.sm);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createVNode as _createVNode, Fragment as _Fragment } from "vue";
|
|
2
|
+
import { defineComponent, reactive, toRef } from 'vue';
|
|
3
|
+
/**
|
|
4
|
+
* Components should be created using this helper function.
|
|
5
|
+
* It only takes three arguments, the name and the props of the component and the setup function.
|
|
6
|
+
* All other arguments which the {@link defineComponent} method of vue may provide,
|
|
7
|
+
* should not be used for a better consistency across all components.
|
|
8
|
+
* @param name the name of the component, should be more than one word.
|
|
9
|
+
* @param props the props of the component.
|
|
10
|
+
* @param setup the setup function, which will be called when the component is mounted.
|
|
11
|
+
* @returns the created vue-component.
|
|
12
|
+
*/
|
|
13
|
+
export function createComponent(name, props, setup) {
|
|
14
|
+
return defineComponent({
|
|
15
|
+
name,
|
|
16
|
+
props,
|
|
17
|
+
emits: [],
|
|
18
|
+
setup
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* When using this function, the created component will make available all props
|
|
23
|
+
* specifiedin `slotPropKeys` as slot. In this way, they can be used by either setting
|
|
24
|
+
* the prop directly or by using a slot with the same name. This is useful for older
|
|
25
|
+
* components (in `.vue` files), because they are dependent on slots.
|
|
26
|
+
* @see {@link createComponent}
|
|
27
|
+
*/
|
|
28
|
+
export function createComponentWithSlots(name, props, slotPropKeys, setup) {
|
|
29
|
+
const newSetup = (props, context) => {
|
|
30
|
+
const slottedProps = createSlotProps(props, context.slots, ...slotPropKeys);
|
|
31
|
+
return setup(slottedProps, context);
|
|
32
|
+
};
|
|
33
|
+
return createComponent(name, props, newSetup);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Views should be created using this helper function. Views are special components, which don't have props.
|
|
37
|
+
* They are often the parent objects in a view hierarchy and contain many components.
|
|
38
|
+
* This function is syntactic sugar to create views and just calls {@link createComponent}.
|
|
39
|
+
* @param name the name of the component, should be more than one word.
|
|
40
|
+
* @param setup the setup function, which will be called when the component is mounted.
|
|
41
|
+
* @returns the created vue-component.
|
|
42
|
+
*/
|
|
43
|
+
export function createView(name, setup) {
|
|
44
|
+
return defineComponent({
|
|
45
|
+
name,
|
|
46
|
+
emits: [],
|
|
47
|
+
setup: (props, context) => setup(context)
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extracts props from another prop object and returns a reactive object with the specified props.
|
|
52
|
+
* @param props the props to extract from
|
|
53
|
+
* @param keys the keys to extract from the props
|
|
54
|
+
* @returns the new object with the specified props
|
|
55
|
+
* @example
|
|
56
|
+
* const parentProps = { title: 'hi', text: 'ho' }
|
|
57
|
+
* const childProps = extractProps(parentProps, 'title')
|
|
58
|
+
* console.log(childProps) // { title: 'hi' }
|
|
59
|
+
*/
|
|
60
|
+
export function extractProps(props, ...keys) {
|
|
61
|
+
const partial = {};
|
|
62
|
+
for (const key of keys) partial[key] = toRef(props, key);
|
|
63
|
+
return reactive(partial);
|
|
64
|
+
}
|
|
65
|
+
function createSlotProps(props, slots, ...keys) {
|
|
66
|
+
const newProps = {
|
|
67
|
+
...props
|
|
68
|
+
};
|
|
69
|
+
keys.forEach(key => {
|
|
70
|
+
const slot = slots[key];
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
if (slot) newProps[key] = (...args) => _createVNode(_Fragment, null, [slot?.(...args)]);
|
|
73
|
+
});
|
|
74
|
+
return newProps;
|
|
75
|
+
}
|
package/utils/deferred.js
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
* @returns promise, resolve and reject
|
|
4
4
|
*/
|
|
5
5
|
export function deferred() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
6
|
+
let resolve;
|
|
7
|
+
let reject;
|
|
8
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
9
|
+
resolve = _resolve;
|
|
10
|
+
reject = _reject;
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
promise,
|
|
14
|
+
resolve,
|
|
15
|
+
reject
|
|
16
|
+
};
|
|
17
|
+
}
|
package/utils/identifiable.js
CHANGED
|
@@ -6,13 +6,9 @@ import { isNullish } from './utils';
|
|
|
6
6
|
* @returns The first item with the specified `id` or `undefined` if none exists.
|
|
7
7
|
*/
|
|
8
8
|
function find(array, id) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (filtered.length > 0)
|
|
13
|
-
return filtered[0];
|
|
14
|
-
else
|
|
15
|
-
return undefined;
|
|
9
|
+
if (isNullish(array)) return undefined;
|
|
10
|
+
const filtered = array.filter(item => item.id === id);
|
|
11
|
+
if (filtered.length > 0) return filtered[0];else return undefined;
|
|
16
12
|
}
|
|
17
13
|
/**
|
|
18
14
|
* Checks if the given array contains an item with the `id`.
|
|
@@ -21,11 +17,11 @@ function find(array, id) {
|
|
|
21
17
|
* @returns `true` if there is at least one item in the array with the given `id`.
|
|
22
18
|
*/
|
|
23
19
|
function contains(array, id) {
|
|
24
|
-
|
|
20
|
+
return find(array, id) !== undefined;
|
|
25
21
|
}
|
|
26
22
|
function insertSingle(baseArray, insertItem) {
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
const index = baseArray.findIndex(item => item.id === insertItem.id);
|
|
24
|
+
index === -1 ? baseArray.push(insertItem) : baseArray.splice(index, 1, insertItem);
|
|
29
25
|
}
|
|
30
26
|
/**
|
|
31
27
|
* Inserts the items into the given array, replacing items with the same `id`.
|
|
@@ -36,8 +32,8 @@ function insertSingle(baseArray, insertItem) {
|
|
|
36
32
|
* @returns The reference to the same array, which was passed.
|
|
37
33
|
*/
|
|
38
34
|
function insert(array, ...insertItems) {
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
insertItems.forEach(item => insertSingle(array, item));
|
|
36
|
+
return array;
|
|
41
37
|
}
|
|
42
38
|
/**
|
|
43
39
|
* Removes all items with the specified `ids` from the given array.
|
|
@@ -46,17 +42,14 @@ function insert(array, ...insertItems) {
|
|
|
46
42
|
* @returns The reference to the same array, which was passed.
|
|
47
43
|
*/
|
|
48
44
|
function remove(array, ...ids) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
return array;
|
|
45
|
+
ids.forEach(id => {
|
|
46
|
+
let noMatches = false;
|
|
47
|
+
while (!noMatches) {
|
|
48
|
+
const index = array.findIndex(item => item.id === id);
|
|
49
|
+
if (index != -1) array.splice(index, 1);else noMatches = true;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return array;
|
|
60
53
|
}
|
|
61
54
|
/**
|
|
62
55
|
* Compares the two arrays and checks if they both have
|
|
@@ -66,15 +59,19 @@ function remove(array, ...ids) {
|
|
|
66
59
|
* @returns `true` if the arrays contain item with the same `ids` in the same order.
|
|
67
60
|
*/
|
|
68
61
|
function areSameArrays(first, second) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
return true;
|
|
62
|
+
if (first.length != second.length) return false;
|
|
63
|
+
for (let i = 0; i < first.length; i++) {
|
|
64
|
+
if (first[i]?.id !== second[i]?.id) return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
76
67
|
}
|
|
77
68
|
/**
|
|
78
69
|
* This object contains utility functions to deal with {@link Identifiable} objects.
|
|
79
70
|
*/
|
|
80
|
-
export const Id = {
|
|
71
|
+
export const Id = {
|
|
72
|
+
find,
|
|
73
|
+
contains,
|
|
74
|
+
insert,
|
|
75
|
+
remove,
|
|
76
|
+
areSameArrays
|
|
77
|
+
};
|
package/utils/stringMaxLength.js
CHANGED
|
@@ -5,10 +5,7 @@
|
|
|
5
5
|
* @see maxLengthSplitCenter
|
|
6
6
|
*/
|
|
7
7
|
export function maxLength(input, maxLength) {
|
|
8
|
-
|
|
9
|
-
return `${input.substring(0, maxLength - 3).trim()}...`;
|
|
10
|
-
else
|
|
11
|
-
return input || '';
|
|
8
|
+
if (input && input.length > maxLength) return `${input.substring(0, maxLength - 3).trim()}...`;else return input || '';
|
|
12
9
|
}
|
|
13
10
|
/**
|
|
14
11
|
* Returns a shortened string with '...' in the center of the string if it is longer than the given `maxLength`.
|
|
@@ -17,12 +14,10 @@ export function maxLength(input, maxLength) {
|
|
|
17
14
|
* @see maxLength
|
|
18
15
|
*/
|
|
19
16
|
export function maxLengthSplitCenter(input, maxLength) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return input || '';
|
|
28
|
-
}
|
|
17
|
+
if (input && input.length > maxLength) {
|
|
18
|
+
const chars = maxLength - 3;
|
|
19
|
+
const charsAtStart = Math.ceil(chars / 2);
|
|
20
|
+
const charsAtEnd = Math.floor(chars / 2);
|
|
21
|
+
return `${input.substring(0, charsAtStart).trim()}...${input.substring(input.length - charsAtEnd).trim()}`;
|
|
22
|
+
} else return input || '';
|
|
23
|
+
}
|
package/utils/tailwind.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export {};
|
package/utils/utils.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @returns `true` if the value is anything but `null` or `undefined`.
|
|
8
8
|
*/
|
|
9
9
|
export function notNullish(value) {
|
|
10
|
-
|
|
10
|
+
return value !== null && value !== undefined;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Determines if a value is null or undefined.
|
|
@@ -15,7 +15,7 @@ export function notNullish(value) {
|
|
|
15
15
|
* @returns `true` if the value is `null` or `undefined`.
|
|
16
16
|
*/
|
|
17
17
|
export function isNullish(value) {
|
|
18
|
-
|
|
18
|
+
return value === null || value === undefined;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Determines if the value of a ref is not nullish.
|
|
@@ -24,16 +24,16 @@ export function isNullish(value) {
|
|
|
24
24
|
* @see notNullish
|
|
25
25
|
*/
|
|
26
26
|
export function notNullishRef(ref) {
|
|
27
|
-
|
|
27
|
+
return notNullish(ref.value);
|
|
28
28
|
}
|
|
29
29
|
export function isAnyObject(object) {
|
|
30
|
-
|
|
30
|
+
return typeof object === 'object' && !Array.isArray(object);
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Returns the same object casted to a {@link ReadonlyObject}.
|
|
34
34
|
*/
|
|
35
35
|
export function readonlyObject(object) {
|
|
36
|
-
|
|
36
|
+
return object;
|
|
37
37
|
}
|
|
38
38
|
/*
|
|
39
39
|
* ---------- Unique Id ----------
|
|
@@ -43,5 +43,5 @@ let currentId = 1;
|
|
|
43
43
|
* Generates and returns a non random but unique id.
|
|
44
44
|
*/
|
|
45
45
|
export function uniqueId() {
|
|
46
|
-
|
|
47
|
-
}
|
|
46
|
+
return currentId++;
|
|
47
|
+
}
|
package/utils/vModel.js
CHANGED
|
@@ -8,54 +8,54 @@
|
|
|
8
8
|
* @returns An object containing the value-property and the update-function.
|
|
9
9
|
*/
|
|
10
10
|
export function vModelProps(propType) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
return {
|
|
12
|
+
/**
|
|
13
|
+
* The value of the component.
|
|
14
|
+
*/
|
|
15
|
+
value: propType,
|
|
16
|
+
/**
|
|
17
|
+
* This will be called, when the value of the component has changed.
|
|
18
|
+
*/
|
|
19
|
+
onUpdateValue: Function
|
|
20
|
+
};
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* Creates props for a required `v-model` of the given type.
|
|
24
24
|
* @see {@link vModelProps}
|
|
25
25
|
*/
|
|
26
26
|
export function vModelRequiredProps(propType) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
return {
|
|
28
|
+
/**
|
|
29
|
+
* The value of the component.
|
|
30
|
+
*/
|
|
31
|
+
value: {
|
|
32
|
+
type: propType,
|
|
33
|
+
required: true
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* This will be called, when the value of the component has changed.
|
|
37
|
+
*/
|
|
38
|
+
onUpdateValue: Function
|
|
39
|
+
};
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Creates props for a `v-model` of the given type with a default value.
|
|
43
43
|
* @see {@link vModelProps}
|
|
44
44
|
*/
|
|
45
45
|
export function vModelDefaultProps(propType, defaultValue) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
return {
|
|
47
|
+
/**
|
|
48
|
+
* The value of the component.
|
|
49
|
+
*/
|
|
50
|
+
value: {
|
|
51
|
+
type: propType,
|
|
52
|
+
default: defaultValue
|
|
53
|
+
},
|
|
54
|
+
/**
|
|
55
|
+
* This will be called, when the value of the component has changed.
|
|
56
|
+
*/
|
|
57
|
+
onUpdateValue: Function
|
|
58
|
+
};
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Uses the given `ref` as a `v-model`, to create a two-way binding with the `ref`.
|
|
@@ -63,12 +63,12 @@ export function vModelDefaultProps(propType, defaultValue) {
|
|
|
63
63
|
* @returns An object of type {@link VModel}, which connects the `ref` to the `v-model`.
|
|
64
64
|
*/
|
|
65
65
|
export function vModelForRef(ref) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
return {
|
|
67
|
+
value: ref.value,
|
|
68
|
+
onUpdateValue: newValue => {
|
|
69
|
+
ref.value = newValue;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* This creates a `v-model` for a property of an object. The property of the object is taken as the
|
|
@@ -93,12 +93,15 @@ export function vModelForRef(ref) {
|
|
|
93
93
|
* ```
|
|
94
94
|
*/
|
|
95
95
|
export function vModelForObjectProperty(object, key, onUpdate) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
return {
|
|
97
|
+
value: object[key],
|
|
98
|
+
onUpdateValue: newValue => {
|
|
99
|
+
onUpdate?.({
|
|
100
|
+
...object,
|
|
101
|
+
[key]: newValue
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
102
105
|
}
|
|
103
106
|
/**
|
|
104
107
|
* This creates a `v-model` which operates on one property of a parent `v-model`. It takes the value of
|
|
@@ -130,12 +133,15 @@ export function vModelForObjectProperty(object, key, onUpdate) {
|
|
|
130
133
|
* ```
|
|
131
134
|
*/
|
|
132
135
|
export function vModelForVModelProperty(vModel, key) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
return {
|
|
137
|
+
value: vModel.value[key],
|
|
138
|
+
onUpdateValue: newValue => {
|
|
139
|
+
vModel.onUpdateValue?.({
|
|
140
|
+
...vModel.value,
|
|
141
|
+
[key]: newValue
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
};
|
|
139
145
|
}
|
|
140
146
|
/**
|
|
141
147
|
* This function does the same thing as {@link vModelForVModelProperty},
|
|
@@ -168,12 +174,15 @@ export function vModelForVModelProperty(vModel, key) {
|
|
|
168
174
|
* ```
|
|
169
175
|
*/
|
|
170
176
|
export function vModelForVModelPropertyMapType(vModel, key, mapType) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
return {
|
|
178
|
+
value: vModel.value[key],
|
|
179
|
+
onUpdateValue: newValue => {
|
|
180
|
+
vModel.onUpdateValue?.({
|
|
181
|
+
...vModel.value,
|
|
182
|
+
[key]: mapType(newValue)
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
};
|
|
177
186
|
}
|
|
178
187
|
/**
|
|
179
188
|
* Creates an array of `v-models`, one for every element of an array. All changes in
|
|
@@ -200,16 +209,16 @@ export function vModelForVModelPropertyMapType(vModel, key, mapType) {
|
|
|
200
209
|
* ```
|
|
201
210
|
*/
|
|
202
211
|
export function vModelsForArray(array, onUpdate) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
212
|
+
return array.map((entry, index) => ({
|
|
213
|
+
value: entry,
|
|
214
|
+
onUpdateValue: entry => {
|
|
215
|
+
const newArray = [...array];
|
|
216
|
+
newArray[index] = entry;
|
|
217
|
+
onUpdate?.(newArray);
|
|
218
|
+
},
|
|
219
|
+
remove: () => {
|
|
220
|
+
const newArray = [...array.slice(0, index), ...array.slice(index + 1)];
|
|
221
|
+
onUpdate?.(newArray);
|
|
222
|
+
}
|
|
223
|
+
}));
|
|
224
|
+
}
|
package/utils/validation.js
CHANGED
|
@@ -3,17 +3,22 @@ import { trsl } from '../i18n';
|
|
|
3
3
|
* Creates a valid result.
|
|
4
4
|
*/
|
|
5
5
|
export function validResult() {
|
|
6
|
-
|
|
6
|
+
return {
|
|
7
|
+
isValid: true
|
|
8
|
+
};
|
|
7
9
|
}
|
|
8
10
|
/**
|
|
9
11
|
* Creates an invalid result with the provided error message.
|
|
10
12
|
*/
|
|
11
13
|
export function invalidResult(errorMessage) {
|
|
12
|
-
|
|
14
|
+
return {
|
|
15
|
+
isValid: false,
|
|
16
|
+
errorMessage
|
|
17
|
+
};
|
|
13
18
|
}
|
|
14
19
|
const TRANSLATION_KEY_BASE = 'vue-collection.validation.rules';
|
|
15
20
|
function invalidResultInternal(key, params) {
|
|
16
|
-
|
|
21
|
+
return invalidResult(trsl(`${TRANSLATION_KEY_BASE}.${key}`, params));
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
19
24
|
* Validates a given input with the specified rules.
|
|
@@ -25,12 +30,11 @@ function invalidResultInternal(key, params) {
|
|
|
25
30
|
* @returns an object containing the result of the validation.
|
|
26
31
|
*/
|
|
27
32
|
export function validate(input, rules) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return validResult();
|
|
33
|
+
for (const rule of rules) {
|
|
34
|
+
const validationResult = rule(input);
|
|
35
|
+
if (!validationResult.isValid) return validationResult;
|
|
36
|
+
}
|
|
37
|
+
return validResult();
|
|
34
38
|
}
|
|
35
39
|
/*
|
|
36
40
|
* ---------- Validation Rules ----------
|
|
@@ -39,20 +43,14 @@ export function validate(input, rules) {
|
|
|
39
43
|
* This rule expects the trimmed input-value to be truthy.
|
|
40
44
|
*/
|
|
41
45
|
export const required = input => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return validResult();
|
|
45
|
-
else
|
|
46
|
-
return invalidResultInternal('required');
|
|
46
|
+
const trimmed = input?.trim();
|
|
47
|
+
if (trimmed) return validResult();else return invalidResultInternal('required');
|
|
47
48
|
};
|
|
48
49
|
/**
|
|
49
50
|
* This rule expects the input to be an integer.
|
|
50
51
|
*/
|
|
51
52
|
export const integer = input => {
|
|
52
|
-
|
|
53
|
-
return validResult();
|
|
54
|
-
else
|
|
55
|
-
return invalidResultInternal('integer');
|
|
53
|
+
if (!input || Number.isInteger(+input)) return validResult();else return invalidResultInternal('integer');
|
|
56
54
|
};
|
|
57
55
|
/**
|
|
58
56
|
* This rule expects the input to be in the specified length range. An empty input
|
|
@@ -61,17 +59,18 @@ export const integer = input => {
|
|
|
61
59
|
* @param max The maximum length of the string.
|
|
62
60
|
*/
|
|
63
61
|
export function length(min, max) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
return input => {
|
|
63
|
+
if (!input) return validResult();
|
|
64
|
+
if (min !== undefined && max !== undefined && !(min <= input.length && input.length <= max)) return invalidResultInternal('length.min-max', {
|
|
65
|
+
min,
|
|
66
|
+
max
|
|
67
|
+
});else if (min !== undefined && !(min <= input.length)) return invalidResultInternal('length.min', {
|
|
68
|
+
min
|
|
69
|
+
});else if (max !== undefined && !(input.length <= max)) return invalidResultInternal('length.max', {
|
|
70
|
+
max
|
|
71
|
+
});
|
|
72
|
+
return validResult();
|
|
73
|
+
};
|
|
75
74
|
}
|
|
76
75
|
/**
|
|
77
76
|
* This rule expects the input to be a number in the specified range.
|
|
@@ -79,50 +78,34 @@ export function length(min, max) {
|
|
|
79
78
|
* @param max the upper bound, if `undefined` there is no upper bound.
|
|
80
79
|
*/
|
|
81
80
|
export function numberRange(min, max) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
return input => {
|
|
82
|
+
if (!input) return validResult();
|
|
83
|
+
const parsed = Number.parseFloat(input);
|
|
84
|
+
if (Number.isNaN(parsed)) return invalidResultInternal('number-range.nan');
|
|
85
|
+
if (min !== undefined && max !== undefined && !(min <= parsed && parsed <= max)) return invalidResultInternal('number-range.min-max', {
|
|
86
|
+
min,
|
|
87
|
+
max
|
|
88
|
+
});else if (min !== undefined && !(min <= parsed)) return invalidResultInternal('number-range.min', {
|
|
89
|
+
min
|
|
90
|
+
});else if (max !== undefined && !(parsed <= max)) return invalidResultInternal('number-range.max', {
|
|
91
|
+
max
|
|
92
|
+
});
|
|
93
|
+
return validResult();
|
|
94
|
+
};
|
|
96
95
|
}
|
|
97
96
|
export const VALIDATION_FORMAT_EMAIL = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/;
|
|
98
97
|
/**
|
|
99
98
|
* This rule expects the input-value to be a valid email adress matching {@link VALIDATION_FORMAT_EMAIL}.
|
|
100
99
|
*/
|
|
101
100
|
export const email = input => {
|
|
102
|
-
|
|
103
|
-
return validResult();
|
|
104
|
-
else
|
|
105
|
-
return invalidResultInternal('email');
|
|
101
|
+
if (!input || VALIDATION_FORMAT_EMAIL.test(input)) return validResult();else return invalidResultInternal('email');
|
|
106
102
|
};
|
|
107
103
|
export const VALIDATION_FORMAT_PASSWORD = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$^+\-=!*()@%&?]).{8,}$/;
|
|
108
104
|
/**
|
|
109
105
|
* This rule expects the input-value to be a password matching {@link VALIDATION_FORMAT_PASSWORD}.
|
|
110
106
|
*/
|
|
111
107
|
export const password = input => {
|
|
112
|
-
|
|
113
|
-
return validResult();
|
|
114
|
-
else if (input.length < 8)
|
|
115
|
-
return invalidResultInternal('password.to-short');
|
|
116
|
-
else if (!/[a-z]+/.test(input))
|
|
117
|
-
return invalidResultInternal('password.no-lowercase');
|
|
118
|
-
else if (!/[A-Z]+/.test(input))
|
|
119
|
-
return invalidResultInternal('password.no-uppercase');
|
|
120
|
-
else if (!/\d+/.test(input))
|
|
121
|
-
return invalidResultInternal('password.no-digits');
|
|
122
|
-
else if (!/[#$^+\-=!*()@%&?]+/.test(input))
|
|
123
|
-
return invalidResultInternal('password.no-special-chars');
|
|
124
|
-
else
|
|
125
|
-
return invalidResultInternal('password.unknown');
|
|
108
|
+
if (!input || VALIDATION_FORMAT_PASSWORD.test(input)) return validResult();else if (input.length < 8) return invalidResultInternal('password.to-short');else if (!/[a-z]+/.test(input)) return invalidResultInternal('password.no-lowercase');else if (!/[A-Z]+/.test(input)) return invalidResultInternal('password.no-uppercase');else if (!/\d+/.test(input)) return invalidResultInternal('password.no-digits');else if (!/[#$^+\-=!*()@%&?]+/.test(input)) return invalidResultInternal('password.no-special-chars');else return invalidResultInternal('password.unknown');
|
|
126
109
|
};
|
|
127
110
|
/**
|
|
128
111
|
* This rule expects the input-value to match another (input-) value.
|
|
@@ -131,36 +114,27 @@ export const password = input => {
|
|
|
131
114
|
* @param other the other value to match
|
|
132
115
|
*/
|
|
133
116
|
export function matches(other) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
else
|
|
138
|
-
return invalidResultInternal('matches');
|
|
139
|
-
};
|
|
117
|
+
return input => {
|
|
118
|
+
if (input === other) return validResult();else return invalidResultInternal('matches');
|
|
119
|
+
};
|
|
140
120
|
}
|
|
141
121
|
/**
|
|
142
122
|
* This rule expects the input-value to match one option in an array.
|
|
143
123
|
* @param options the options which the input can match
|
|
144
124
|
*/
|
|
145
125
|
export function option(options) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
else
|
|
150
|
-
return invalidResultInternal('option');
|
|
151
|
-
};
|
|
126
|
+
return input => {
|
|
127
|
+
if (!input || options.includes(input || '')) return validResult();else return invalidResultInternal('option');
|
|
128
|
+
};
|
|
152
129
|
}
|
|
153
130
|
/**
|
|
154
131
|
* This rule expects the input-value to match the regex pattern
|
|
155
132
|
* @param pattern the pattern the input should match.
|
|
156
133
|
*/
|
|
157
134
|
export function regex(pattern) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
else
|
|
162
|
-
return invalidResultInternal('regex');
|
|
163
|
-
};
|
|
135
|
+
return input => {
|
|
136
|
+
if (!input || pattern.test(input || '')) return validResult();else return invalidResultInternal('regex');
|
|
137
|
+
};
|
|
164
138
|
}
|
|
165
139
|
/**
|
|
166
140
|
* This rule can be used if the validation logic happens somewhere else.
|
|
@@ -169,5 +143,5 @@ export function regex(pattern) {
|
|
|
169
143
|
* Like always, a falsy input is always valid to not interefere with the {@link required} rule.
|
|
170
144
|
*/
|
|
171
145
|
export function external(isValid, errorMessage) {
|
|
172
|
-
|
|
173
|
-
}
|
|
146
|
+
return input => !input || isValid ? validResult() : invalidResult(errorMessage);
|
|
147
|
+
}
|
package/utils/vue.js
CHANGED
|
@@ -5,9 +5,11 @@ import { watch } from 'vue';
|
|
|
5
5
|
* @param updater the updater funtion which provides the updates
|
|
6
6
|
*/
|
|
7
7
|
export function updateWith(ref, updater) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
watch(updater, newValue => {
|
|
9
|
+
ref.value = newValue;
|
|
10
|
+
}, {
|
|
11
|
+
immediate: true
|
|
12
|
+
});
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Conveience function to create a watcher for a ref
|
|
@@ -15,5 +17,5 @@ export function updateWith(ref, updater) {
|
|
|
15
17
|
* @param onChange the function, which is executed on change of a value
|
|
16
18
|
*/
|
|
17
19
|
export function watchRef(ref, onChange) {
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
+
watch(() => ref.value, onChange);
|
|
21
|
+
}
|