@naptics/vue-collection 0.0.3 → 0.0.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/README.md +5 -1
- package/components/NAlert.js +81 -0
- package/components/NBadge.js +57 -0
- package/components/NBreadcrub.js +66 -0
- package/components/NButton.js +65 -0
- package/components/NCheckbox.js +42 -0
- package/components/NCheckboxLabel.js +39 -0
- package/components/NCrudModal.js +105 -0
- package/components/NDialog.js +160 -0
- package/components/NDropdown.js +108 -0
- package/components/NDropzone.js +210 -0
- package/components/NForm.js +28 -0
- package/components/NFormModal.js +54 -0
- package/components/NIconButton.js +81 -0
- package/components/NIconCircle.js +66 -0
- package/components/NInput.js +105 -0
- package/components/NInputPhone.js +46 -0
- package/components/NInputSelect.js +114 -0
- package/components/NInputSuggestion.js +63 -0
- package/components/NLink.js +59 -0
- package/components/NList.js +24 -0
- package/components/NLoadingIndicator.js +53 -0
- package/components/NModal.js +210 -0
- package/components/NPagination.js +108 -0
- package/components/NSearchbar.js +66 -0
- package/components/NSearchbarList.js +36 -0
- package/components/NSelect.js +84 -0
- package/components/NSuggestionList.js +156 -0
- package/components/NTable.js +126 -0
- package/components/NTableAction.js +49 -0
- package/components/NTextArea.js +128 -0
- package/components/NTooltip.js +178 -0
- package/components/NValInput.js +104 -0
- package/components/ValidatedForm.js +18 -18
- package/i18n/index.js +4 -8
- package/index.js +1 -1
- package/package.json +9 -2
- package/utils/breakpoints.js +21 -21
- package/utils/component.js +17 -9
- package/utils/deferred.js +12 -12
- package/utils/identifiable.js +27 -29
- package/utils/stringMaxLength.js +8 -13
- package/utils/tailwind.js +1 -1
- package/utils/utils.js +5 -5
- package/utils/vModel.js +82 -73
- package/utils/validation.js +55 -81
- package/utils/vue.js +7 -5
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ You can take a look at all components on [GitHub Pages](https://naptics.github.i
|
|
|
10
10
|
|
|
11
11
|
This section shows how to create a new Vue project with the recommended tech-stack to be ready to install Vue Collection afterwards.
|
|
12
12
|
|
|
13
|
-
1. Create a new vue-project with `npm init vue@latest`. Add at least typescript, jsx,
|
|
13
|
+
1. Create a new vue-project with `npm init vue@latest`. Add at least typescript, jsx, eslint and prettier.
|
|
14
14
|
2. Remove boilerplate code.
|
|
15
15
|
3. Follow the instructions to [install tailwind](https://tailwindcss.com/docs/installation/using-postcss).
|
|
16
16
|
- `npm install -D tailwindcss postcss autoprefixer`
|
|
@@ -36,6 +36,10 @@ These are the steps to add Vue Collection to an existing project. You may have t
|
|
|
36
36
|
4. Add tailwind config -> See below
|
|
37
37
|
5. Register i18n provider -> See below
|
|
38
38
|
|
|
39
|
+
## Create new components
|
|
40
|
+
|
|
41
|
+
After you have installed Vue Collection, you can start building components using the `createComponent` and the `createView` funcitions, which are exported by Vue Collection. They enforce you to only use the neccesary API provided by Vue and create components in a uniform way.
|
|
42
|
+
|
|
39
43
|
## Tailwind Config
|
|
40
44
|
|
|
41
45
|
With this tailwind config file, Vue Collection works as expected. Feel free to change the `default`, `primary` and `secondary` colors and add whatever is needed in your project.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createVNode as _createVNode } from "vue";
|
|
2
|
+
import { createComponent, createProps } from '../utils/component';
|
|
3
|
+
import { CheckCircleIcon, ExclamationCircleIcon, InformationCircleIcon, XMarkIcon } from '@heroicons/vue/24/solid';
|
|
4
|
+
import { computed } from 'vue';
|
|
5
|
+
import NIconButton from './NIconButton';
|
|
6
|
+
import NLoadingIndicator from './NLoadingIndicator';
|
|
7
|
+
export const nAlertProps = createProps({
|
|
8
|
+
/**
|
|
9
|
+
* The variant of the alert. This defines its color and icon.
|
|
10
|
+
*/
|
|
11
|
+
variant: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: 'success'
|
|
14
|
+
},
|
|
15
|
+
/**
|
|
16
|
+
* The text of the alert.
|
|
17
|
+
*/
|
|
18
|
+
text: String,
|
|
19
|
+
/**
|
|
20
|
+
* If set to `true` the X-button of the alert is hidden.
|
|
21
|
+
*/
|
|
22
|
+
hideX: Boolean,
|
|
23
|
+
/**
|
|
24
|
+
* This is called, when the X-button is clicked.
|
|
25
|
+
*/
|
|
26
|
+
onDismiss: Function
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* The `NAlert` is a fully styled alert with multiple variants.
|
|
30
|
+
* It can be used as a normal blocking element or as part of an alert queue.
|
|
31
|
+
*/
|
|
32
|
+
export default createComponent('NAlert', nAlertProps, props => {
|
|
33
|
+
const variant = computed(() => VARIANTS[props.variant]);
|
|
34
|
+
return () => _createVNode("div", {
|
|
35
|
+
"class": `rounded-md p-3 shadow-lg bg-${variant.value.color}-50`
|
|
36
|
+
}, [_createVNode("div", {
|
|
37
|
+
"class": "flex items-center"
|
|
38
|
+
}, [_createVNode("div", {
|
|
39
|
+
"class": "flex flex-shrink-0 items-center"
|
|
40
|
+
}, [variant.value.icon()]), _createVNode("div", {
|
|
41
|
+
"class": "ml-3 flex-grow"
|
|
42
|
+
}, [_createVNode("p", {
|
|
43
|
+
"class": `text-sm font-medium text-${variant.value.color}-900`
|
|
44
|
+
}, [props.text])]), !props.hideX && _createVNode("div", {
|
|
45
|
+
"class": "flex items-center flex-shrink-0 ml-3"
|
|
46
|
+
}, [_createVNode(NIconButton, {
|
|
47
|
+
"color": variant.value.color,
|
|
48
|
+
"size": 5,
|
|
49
|
+
"icon": XMarkIcon,
|
|
50
|
+
"onClick": props.onDismiss
|
|
51
|
+
}, null)])])]);
|
|
52
|
+
});
|
|
53
|
+
const icon = (icon, color) => () => _createVNode(icon, {
|
|
54
|
+
"class": `h-5 w-5 text-${color}-500`
|
|
55
|
+
}, null);
|
|
56
|
+
const VARIANTS = {
|
|
57
|
+
success: {
|
|
58
|
+
icon: icon(CheckCircleIcon, 'green'),
|
|
59
|
+
color: 'green'
|
|
60
|
+
},
|
|
61
|
+
info: {
|
|
62
|
+
icon: icon(InformationCircleIcon, 'blue'),
|
|
63
|
+
color: 'blue'
|
|
64
|
+
},
|
|
65
|
+
warning: {
|
|
66
|
+
icon: icon(ExclamationCircleIcon, 'yellow'),
|
|
67
|
+
color: 'yellow'
|
|
68
|
+
},
|
|
69
|
+
error: {
|
|
70
|
+
icon: icon(ExclamationCircleIcon, 'red'),
|
|
71
|
+
color: 'red'
|
|
72
|
+
},
|
|
73
|
+
loading: {
|
|
74
|
+
icon: () => _createVNode(NLoadingIndicator, {
|
|
75
|
+
"color": "blue",
|
|
76
|
+
"size": 7,
|
|
77
|
+
"shade": 500
|
|
78
|
+
}, null),
|
|
79
|
+
color: 'blue'
|
|
80
|
+
}
|
|
81
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createVNode as _createVNode } from "vue";
|
|
2
|
+
import { createComponent, createProps } from '../utils/component';
|
|
3
|
+
import NTooltip, { mapTooltipProps, nToolTipPropsForImplementor } from './NTooltip';
|
|
4
|
+
export const nBadgeProps = createProps({
|
|
5
|
+
/**
|
|
6
|
+
* The text of the badge. Can alternatively be passed in the default slot.
|
|
7
|
+
*/
|
|
8
|
+
text: String,
|
|
9
|
+
/**
|
|
10
|
+
* The text size, a standard tailwind text-size class.
|
|
11
|
+
*/
|
|
12
|
+
textSize: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: 'text-sm'
|
|
15
|
+
},
|
|
16
|
+
/**
|
|
17
|
+
* The color of the badge.
|
|
18
|
+
*/
|
|
19
|
+
color: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: 'primary'
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* The background shade of the badge.
|
|
25
|
+
*/
|
|
26
|
+
shade: {
|
|
27
|
+
type: Number,
|
|
28
|
+
default: 200
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* The text shade of the badge.
|
|
32
|
+
*/
|
|
33
|
+
textShade: {
|
|
34
|
+
type: Number,
|
|
35
|
+
default: 900
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* If set to `true` the badges text is all-caps. Default is `true`.
|
|
39
|
+
*/
|
|
40
|
+
allCaps: {
|
|
41
|
+
type: Boolean,
|
|
42
|
+
default: true
|
|
43
|
+
},
|
|
44
|
+
...nToolTipPropsForImplementor
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* The `NBadge` is a styled element to wrap a text.
|
|
48
|
+
*/
|
|
49
|
+
export default createComponent('NBadge', nBadgeProps, (props, {
|
|
50
|
+
slots
|
|
51
|
+
}) => {
|
|
52
|
+
return () => _createVNode(NTooltip, mapTooltipProps(props), {
|
|
53
|
+
default: () => [_createVNode("div", {
|
|
54
|
+
"class": ['px-2 py-1 rounded-md font-semibold shadow', `${props.textSize} bg-${props.color}-${props.shade} text-${props.color}-${props.textShade}`, props.allCaps ? 'uppercase' : '']
|
|
55
|
+
}, [slots.default?.() || props.text])]
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createVNode as _createVNode, Fragment as _Fragment } from "vue";
|
|
2
|
+
import { createComponent, createProps } from '../utils/component';
|
|
3
|
+
import { ChevronRightIcon } from '@heroicons/vue/24/solid';
|
|
4
|
+
import NLink from './NLink';
|
|
5
|
+
export const nBreadcrumbProps = createProps({
|
|
6
|
+
/**
|
|
7
|
+
* The items of the breadcrumb.
|
|
8
|
+
*/
|
|
9
|
+
items: {
|
|
10
|
+
type: Array,
|
|
11
|
+
default: () => []
|
|
12
|
+
},
|
|
13
|
+
/**
|
|
14
|
+
* The color of the breadcrumbs text and icons.
|
|
15
|
+
*/
|
|
16
|
+
color: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: 'primary'
|
|
19
|
+
},
|
|
20
|
+
/**
|
|
21
|
+
* The text-size of the breadcrumb labels.
|
|
22
|
+
*/
|
|
23
|
+
textSize: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: 'text-base'
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* The icon which is used as a seperator between two breadcrumb items.
|
|
29
|
+
*/
|
|
30
|
+
icon: {
|
|
31
|
+
type: Function,
|
|
32
|
+
default: ChevronRightIcon
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* The size of the icon in tailwind units.
|
|
36
|
+
*/
|
|
37
|
+
iconSize: {
|
|
38
|
+
type: Number,
|
|
39
|
+
default: 5
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* A slot the replace the breadcrumb labels.
|
|
43
|
+
*/
|
|
44
|
+
item: Function,
|
|
45
|
+
/**
|
|
46
|
+
* A slot to replace the separators between the breadcrumb labels.
|
|
47
|
+
* The passsed item is the item before the seperator.
|
|
48
|
+
*/
|
|
49
|
+
seperator: Function
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* The `NBreadcrumb` is a styled breadcrumb which can be used as a navigation in hierarchical views.
|
|
53
|
+
*/
|
|
54
|
+
export default createComponent('NBreadcrumb', nBreadcrumbProps, props => {
|
|
55
|
+
return () => _createVNode("div", {
|
|
56
|
+
"class": "flex flex-wrap items-center"
|
|
57
|
+
}, [props.items.map((item, index) => _createVNode(_Fragment, null, [props.item?.(item, index) || _createVNode(NLink, {
|
|
58
|
+
"textSize": props.textSize,
|
|
59
|
+
"route": item.route,
|
|
60
|
+
"color": props.color
|
|
61
|
+
}, {
|
|
62
|
+
default: () => [item.label]
|
|
63
|
+
}), index < props.items.length - 1 && (props.seperator?.(item, index) || _createVNode(props.icon, {
|
|
64
|
+
"class": `mx-2 w-${props.iconSize} h-${props.iconSize} text-${props.color}-500`
|
|
65
|
+
}, null))]))]);
|
|
66
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createVNode as _createVNode } from "vue";
|
|
2
|
+
import { createComponent, createProps } from '../utils/component';
|
|
3
|
+
import { computed } from 'vue';
|
|
4
|
+
import NLoadingIndicator from './NLoadingIndicator';
|
|
5
|
+
import NTooltip, { mapTooltipProps, nToolTipPropsForImplementor } from './NTooltip';
|
|
6
|
+
export const nButtonProps = createProps({
|
|
7
|
+
/**
|
|
8
|
+
* The color of the button.
|
|
9
|
+
*/
|
|
10
|
+
color: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: 'primary'
|
|
13
|
+
},
|
|
14
|
+
/**
|
|
15
|
+
* The html attribute, which indicates the type of the button.
|
|
16
|
+
*/
|
|
17
|
+
type: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: 'button'
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* If set to `true` the button is disabled and no interaction is possible.
|
|
23
|
+
*/
|
|
24
|
+
disabled: Boolean,
|
|
25
|
+
/**
|
|
26
|
+
* If set to `true` the button will show a loading animation.
|
|
27
|
+
* Setting `loading` to `true` will also disable the button.
|
|
28
|
+
*/
|
|
29
|
+
loading: Boolean,
|
|
30
|
+
/**
|
|
31
|
+
* If set to `true` the button will appear smaller.
|
|
32
|
+
*/
|
|
33
|
+
small: Boolean,
|
|
34
|
+
/**
|
|
35
|
+
* This is called, when the button is clicked.
|
|
36
|
+
*/
|
|
37
|
+
onClick: Function,
|
|
38
|
+
...nToolTipPropsForImplementor
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* The `NButton` is a styled button.
|
|
42
|
+
*/
|
|
43
|
+
export default createComponent('NButton', nButtonProps, (props, {
|
|
44
|
+
slots
|
|
45
|
+
}) => {
|
|
46
|
+
const isDisabled = computed(() => props.loading || props.disabled);
|
|
47
|
+
return () => _createVNode(NTooltip, mapTooltipProps(props), {
|
|
48
|
+
default: () => [_createVNode("button", {
|
|
49
|
+
"disabled": isDisabled.value,
|
|
50
|
+
"type": props.type,
|
|
51
|
+
"class": [`block w-full font-medium rounded-md focus:outline-none focus-visible:ring-2 shadow text-${props.color}-900 relative`, isDisabled.value ? `bg-${props.color}-100 text-opacity-20 cursor-default` : `bg-${props.color}-200 hover:bg-${props.color}-300 focus-visible:ring-${props.color}-500`, props.small ? 'py-1 px-2 text-xs' : 'py-2 px-4 text-sm'],
|
|
52
|
+
"onClick": props.onClick
|
|
53
|
+
}, [_createVNode("span", {
|
|
54
|
+
"class": {
|
|
55
|
+
'opacity-10': props.loading
|
|
56
|
+
}
|
|
57
|
+
}, [slots.default?.()]), props.loading && _createVNode("div", {
|
|
58
|
+
"class": "absolute inset-0 flex items-center justify-center opacity-50"
|
|
59
|
+
}, [_createVNode(NLoadingIndicator, {
|
|
60
|
+
"color": props.color,
|
|
61
|
+
"size": props.small ? 4 : 6,
|
|
62
|
+
"shade": 600
|
|
63
|
+
}, null)])])]
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createVNode as _createVNode } from "vue";
|
|
2
|
+
import { createComponent, createProps } from '../utils/component';
|
|
3
|
+
import { vModelProps } from '../utils/vModel';
|
|
4
|
+
import { nextTick, ref } from 'vue';
|
|
5
|
+
export const nCheckboxProps = createProps({
|
|
6
|
+
...vModelProps(Boolean),
|
|
7
|
+
/**
|
|
8
|
+
* The color of the checkbox.
|
|
9
|
+
*/
|
|
10
|
+
color: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: 'primary'
|
|
13
|
+
},
|
|
14
|
+
/**
|
|
15
|
+
* If set to `true` the checkbox is disabled and no interaction is possible.
|
|
16
|
+
*/
|
|
17
|
+
disabled: Boolean
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* The `NCheckbox` is a styled checkbox.
|
|
21
|
+
*/
|
|
22
|
+
export default createComponent('NCheckbox', nCheckboxProps, props => {
|
|
23
|
+
const toggle = () => {
|
|
24
|
+
props.onUpdateValue?.(!props.value);
|
|
25
|
+
forceUpdate();
|
|
26
|
+
};
|
|
27
|
+
const checkBoxRef = ref();
|
|
28
|
+
const updateKey = ref(0);
|
|
29
|
+
const forceUpdate = () => {
|
|
30
|
+
updateKey.value += 1;
|
|
31
|
+
nextTick(() => checkBoxRef.value?.focus());
|
|
32
|
+
};
|
|
33
|
+
return () => _createVNode("input", {
|
|
34
|
+
"type": "checkbox",
|
|
35
|
+
"ref": checkBoxRef,
|
|
36
|
+
"checked": props.value,
|
|
37
|
+
"disabled": props.disabled,
|
|
38
|
+
"onClick": toggle,
|
|
39
|
+
"key": updateKey.value,
|
|
40
|
+
"class": [`h-5 w-5 border-default-300 rounded focus:outline-none focus:ring-0 focus-visible:ring-2 focus-visible:ring-${props.color}-500`, props.disabled ? `cursor-default bg-default-100 text-${props.color}-200` : `cursor-pointer text-${props.color}-400`]
|
|
41
|
+
}, null);
|
|
42
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createVNode as _createVNode } from "vue";
|
|
2
|
+
import { createComponent, createProps } from '../utils/component';
|
|
3
|
+
import NCheckbox, { nCheckboxProps } from './NCheckbox';
|
|
4
|
+
export const nCheckboxLabelProps = createProps({
|
|
5
|
+
...nCheckboxProps,
|
|
6
|
+
/**
|
|
7
|
+
* The title of the checkbox.
|
|
8
|
+
*/
|
|
9
|
+
title: String,
|
|
10
|
+
/**
|
|
11
|
+
* The description of the checkbox.
|
|
12
|
+
*/
|
|
13
|
+
description: String,
|
|
14
|
+
/**
|
|
15
|
+
* If set to `true`, a smaller margin is applied between the label and the checkbox.
|
|
16
|
+
*/
|
|
17
|
+
compact: Boolean
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* The `NCheckboxLabel` is a checkbox with a title and a description.
|
|
21
|
+
*/
|
|
22
|
+
export default createComponent('NCheckboxLabel', nCheckboxLabelProps, props => {
|
|
23
|
+
const toggleValue = () => {
|
|
24
|
+
if (!props.disabled) props.onUpdateValue?.(!props.value);
|
|
25
|
+
};
|
|
26
|
+
return () => _createVNode("div", {
|
|
27
|
+
"class": "flex items-center"
|
|
28
|
+
}, [_createVNode(NCheckbox, props, null), _createVNode("div", {
|
|
29
|
+
"class": `${props.compact ? 'ml-2' : 'ml-3'} text-sm`
|
|
30
|
+
}, [_createVNode("label", {
|
|
31
|
+
"onClick": toggleValue,
|
|
32
|
+
"class": ['font-medium select-none', props.disabled ? 'text-default-300' : 'text-default-700 cursor-pointer']
|
|
33
|
+
}, [props.title]), _createVNode("p", {
|
|
34
|
+
"class": props.disabled ? 'text-default-300' : 'text-default-500'
|
|
35
|
+
}, [_createVNode("span", {
|
|
36
|
+
"onClick": toggleValue,
|
|
37
|
+
"class": ['select-none', props.disabled ? '' : 'cursor-pointer']
|
|
38
|
+
}, [props.description])])])]);
|
|
39
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { mergeProps as _mergeProps, createVNode as _createVNode } from "vue";
|
|
2
|
+
import { trsl } from '../i18n';
|
|
3
|
+
import { createComponent, createProps } from '../utils/component';
|
|
4
|
+
import { ref } from 'vue';
|
|
5
|
+
import NButton from './NButton';
|
|
6
|
+
import NDialog from './NDialog';
|
|
7
|
+
import NFormModal, { nFormModalProps } from './NFormModal';
|
|
8
|
+
export const nCrudModalProps = createProps({
|
|
9
|
+
...nFormModalProps,
|
|
10
|
+
/**
|
|
11
|
+
* The text of the remove-button.
|
|
12
|
+
*/
|
|
13
|
+
removeText: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: trsl('vue-collection.action.remove')
|
|
16
|
+
},
|
|
17
|
+
/**
|
|
18
|
+
* The color of the remove-button.
|
|
19
|
+
*/
|
|
20
|
+
removeColor: {
|
|
21
|
+
type: String,
|
|
22
|
+
default: 'red'
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* The title of the dialog which appears when clicking on the remove-button.
|
|
26
|
+
*/
|
|
27
|
+
removeDialogTitle: String,
|
|
28
|
+
/**
|
|
29
|
+
* The text of the dialog which appears when clicking on the remove-button.
|
|
30
|
+
*/
|
|
31
|
+
removeDialogText: String,
|
|
32
|
+
/**
|
|
33
|
+
* The variant of the dialog which appears when clicking on the remove-button. Default is `remove`.
|
|
34
|
+
*/
|
|
35
|
+
removeDialogVariant: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: 'remove'
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* The text of the dialog's ok-button. Is already set by the `removeDialogVariant` but can be overridden.
|
|
41
|
+
*/
|
|
42
|
+
removeDialogOkText: String,
|
|
43
|
+
/**
|
|
44
|
+
* If set to `true` the modal will close itself when `onRemove` is called.
|
|
45
|
+
*/
|
|
46
|
+
closeOnRemove: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: true
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* This is called, when the remove-button has been clicked and the dialog has been accepted.
|
|
52
|
+
*/
|
|
53
|
+
onRemove: Function
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* The `NCrudModal` is a {@link NFormModal} which has some convenience features for a CRUD-scenario.
|
|
57
|
+
* It has an integrated remove-button with a user-dialog to remove the editing element.
|
|
58
|
+
* When the dialog is accepted `onRemove` is called.
|
|
59
|
+
*/
|
|
60
|
+
export default createComponent('NCrudModal', nCrudModalProps, (props, {
|
|
61
|
+
slots
|
|
62
|
+
}) => {
|
|
63
|
+
const removeDialog = ref();
|
|
64
|
+
const remove = () => {
|
|
65
|
+
removeDialog.value?.show().then(result => {
|
|
66
|
+
if (result) {
|
|
67
|
+
props.onRemove?.();
|
|
68
|
+
if (props.closeOnRemove) props.onUpdateValue?.(false);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
return () => _createVNode(NFormModal, _mergeProps(props, {
|
|
73
|
+
"footer": props.footer || (({
|
|
74
|
+
ok,
|
|
75
|
+
cancel
|
|
76
|
+
}) => _createVNode("div", {
|
|
77
|
+
"class": "flex justify-between"
|
|
78
|
+
}, [_createVNode("div", null, [_createVNode(NButton, {
|
|
79
|
+
"color": props.removeColor,
|
|
80
|
+
"onClick": remove
|
|
81
|
+
}, {
|
|
82
|
+
default: () => [props.removeText]
|
|
83
|
+
})]), _createVNode("div", null, [_createVNode(NButton, {
|
|
84
|
+
"color": props.cancelColor,
|
|
85
|
+
"onClick": cancel
|
|
86
|
+
}, {
|
|
87
|
+
default: () => [props.cancelText]
|
|
88
|
+
}), _createVNode(NButton, {
|
|
89
|
+
"color": props.okColor,
|
|
90
|
+
"onClick": ok,
|
|
91
|
+
"class": "ml-2",
|
|
92
|
+
"disabled": props.okDisabled
|
|
93
|
+
}, {
|
|
94
|
+
default: () => [props.okText]
|
|
95
|
+
})])]))
|
|
96
|
+
}), {
|
|
97
|
+
default: () => [slots.default?.(), _createVNode(NDialog, {
|
|
98
|
+
"ref": removeDialog,
|
|
99
|
+
"variant": props.removeDialogVariant,
|
|
100
|
+
"title": props.removeDialogTitle,
|
|
101
|
+
"text": props.removeDialogText,
|
|
102
|
+
"okText": props.removeDialogOkText
|
|
103
|
+
}, null)]
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { mergeProps as _mergeProps, createVNode as _createVNode } from "vue";
|
|
2
|
+
import { deferred } from '../utils/deferred';
|
|
3
|
+
import { createComponent, createProps, extractProps } from '../utils/component';
|
|
4
|
+
import { CheckIcon, ExclamationTriangleIcon, LightBulbIcon, TrashIcon } from '@heroicons/vue/24/outline';
|
|
5
|
+
import { computed, ref } from 'vue';
|
|
6
|
+
import NIconCircle from './NIconCircle';
|
|
7
|
+
import NModal from './NModal';
|
|
8
|
+
import { DialogTitle } from '@headlessui/vue';
|
|
9
|
+
import { trsl } from '../i18n';
|
|
10
|
+
import { vModelForRef } from '../utils/vModel';
|
|
11
|
+
export const nDialogProps = createProps({
|
|
12
|
+
/**
|
|
13
|
+
* The title of the dialog.
|
|
14
|
+
*/
|
|
15
|
+
title: String,
|
|
16
|
+
/**
|
|
17
|
+
* The text of the dialog.
|
|
18
|
+
*/
|
|
19
|
+
text: String,
|
|
20
|
+
/**
|
|
21
|
+
* The variant of the dialog.
|
|
22
|
+
* This determines the default icon and its color
|
|
23
|
+
* as well as the default text and color of the ok-button.
|
|
24
|
+
*/
|
|
25
|
+
variant: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: 'warning'
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* The icon of the alert. This overrides the `icon` of the `variant`.
|
|
31
|
+
*/
|
|
32
|
+
icon: Function,
|
|
33
|
+
/**
|
|
34
|
+
* The color of the alert's icon. This overrides the `iconColor` of the `variant`.
|
|
35
|
+
*/
|
|
36
|
+
iconColor: String,
|
|
37
|
+
/**
|
|
38
|
+
* The text of the ok-button. This overrides the `okText` of the `variant`.
|
|
39
|
+
*/
|
|
40
|
+
okText: String,
|
|
41
|
+
/**
|
|
42
|
+
* The color of the ok-button. This overrides the `okColor` of the `variant`.
|
|
43
|
+
*/
|
|
44
|
+
okColor: String,
|
|
45
|
+
/**
|
|
46
|
+
* The text of the cancel-button.
|
|
47
|
+
*/
|
|
48
|
+
cancelText: {
|
|
49
|
+
type: String,
|
|
50
|
+
default: trsl('vue-collection.action.cancel')
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* The color of the cancel-button.
|
|
54
|
+
*/
|
|
55
|
+
cancelColor: {
|
|
56
|
+
type: String,
|
|
57
|
+
default: 'default'
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* If set to `true` the cancel-button is hidden.
|
|
61
|
+
*/
|
|
62
|
+
hideCancel: Boolean
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* A `NDialog` is an element to interact directly with the user.
|
|
66
|
+
* It can be controlled via a ref to prompt the user and to receive their answer.
|
|
67
|
+
* @example
|
|
68
|
+
* const dialogRef = ref<NDialogExposed>()
|
|
69
|
+
* ...
|
|
70
|
+
* const onShowDialog = () => {
|
|
71
|
+
* dialofRef.value?.show().then(result => {
|
|
72
|
+
* if (result) // dialog accepted
|
|
73
|
+
* else // dialog cancelled
|
|
74
|
+
* })
|
|
75
|
+
* }
|
|
76
|
+
* ...
|
|
77
|
+
* <NDialog ref={dialogRef} />
|
|
78
|
+
*/
|
|
79
|
+
export default createComponent('NDialog', nDialogProps, (props, context) => {
|
|
80
|
+
const showDialog = ref(false);
|
|
81
|
+
let deferredPromise = null;
|
|
82
|
+
const show = () => {
|
|
83
|
+
if (deferredPromise != null) {
|
|
84
|
+
deferredPromise.reject('show() was called on the open dialog.');
|
|
85
|
+
deferredPromise = null;
|
|
86
|
+
}
|
|
87
|
+
showDialog.value = true;
|
|
88
|
+
deferredPromise = deferred();
|
|
89
|
+
return deferredPromise.promise;
|
|
90
|
+
};
|
|
91
|
+
context.expose({
|
|
92
|
+
show
|
|
93
|
+
});
|
|
94
|
+
const resolveWith = result => {
|
|
95
|
+
deferredPromise?.resolve(result);
|
|
96
|
+
deferredPromise = null;
|
|
97
|
+
};
|
|
98
|
+
const ok = () => resolveWith(true);
|
|
99
|
+
const cancel = () => resolveWith(false);
|
|
100
|
+
const defaults = computed(() => VARIANT_DEFAULTS[props.variant]);
|
|
101
|
+
return () => _createVNode(NModal, _mergeProps(vModelForRef(showDialog), extractProps(props, 'cancelColor', 'cancelText', 'hideCancel'), {
|
|
102
|
+
"onOk": ok,
|
|
103
|
+
"onCancel": cancel,
|
|
104
|
+
"okColor": props.okColor || defaults.value.okColor,
|
|
105
|
+
"okText": props.okText || defaults.value.okText,
|
|
106
|
+
"hideX": true,
|
|
107
|
+
"hideHeader": true
|
|
108
|
+
}), {
|
|
109
|
+
default: () => [_createVNode("div", {
|
|
110
|
+
"class": "flex space-x-4 py-2"
|
|
111
|
+
}, [_createVNode("div", {
|
|
112
|
+
"class": "flex-grow-0"
|
|
113
|
+
}, [_createVNode(NIconCircle, {
|
|
114
|
+
"icon": props.icon || defaults.value.icon,
|
|
115
|
+
"iconSize": 6,
|
|
116
|
+
"color": props.iconColor || defaults.value.iconColor
|
|
117
|
+
}, null)]), _createVNode("div", {
|
|
118
|
+
"class": "flex-grow"
|
|
119
|
+
}, [_createVNode(DialogTitle, {
|
|
120
|
+
"as": "h4",
|
|
121
|
+
"class": "font-medium text-lg text-default-700 mb-1"
|
|
122
|
+
}, {
|
|
123
|
+
default: () => [props.title]
|
|
124
|
+
}), context.slots.default?.() || _createVNode("p", {
|
|
125
|
+
"class": "text-sm text-default-500"
|
|
126
|
+
}, [props.text])])])]
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
const VARIANT_DEFAULTS = {
|
|
130
|
+
success: {
|
|
131
|
+
icon: CheckIcon,
|
|
132
|
+
iconColor: 'green',
|
|
133
|
+
okText: trsl('vue-collection.action.all-right'),
|
|
134
|
+
okColor: 'green'
|
|
135
|
+
},
|
|
136
|
+
info: {
|
|
137
|
+
icon: LightBulbIcon,
|
|
138
|
+
iconColor: 'blue',
|
|
139
|
+
okText: trsl('vue-collection.action.all-right'),
|
|
140
|
+
okColor: 'blue'
|
|
141
|
+
},
|
|
142
|
+
warning: {
|
|
143
|
+
icon: ExclamationTriangleIcon,
|
|
144
|
+
iconColor: 'yellow',
|
|
145
|
+
okText: trsl('vue-collection.action.proceed'),
|
|
146
|
+
okColor: 'yellow'
|
|
147
|
+
},
|
|
148
|
+
danger: {
|
|
149
|
+
icon: ExclamationTriangleIcon,
|
|
150
|
+
iconColor: 'red',
|
|
151
|
+
okText: trsl('vue-collection.action.proceed'),
|
|
152
|
+
okColor: 'red'
|
|
153
|
+
},
|
|
154
|
+
remove: {
|
|
155
|
+
icon: TrashIcon,
|
|
156
|
+
iconColor: 'red',
|
|
157
|
+
okText: trsl('vue-collection.action.remove'),
|
|
158
|
+
okColor: 'red'
|
|
159
|
+
}
|
|
160
|
+
};
|