@naptics/vue-collection 0.0.2 → 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.
Files changed (61) hide show
  1. package/README.md +123 -15
  2. package/components/NAlert.js +81 -0
  3. package/components/NBadge.js +57 -0
  4. package/components/NBreadcrub.js +66 -0
  5. package/components/NButton.js +65 -0
  6. package/components/NCheckbox.js +42 -0
  7. package/components/NCheckboxLabel.js +39 -0
  8. package/components/NCrudModal.js +105 -0
  9. package/components/NDialog.js +160 -0
  10. package/components/NDropdown.js +108 -0
  11. package/components/NDropzone.js +210 -0
  12. package/components/NForm.js +28 -0
  13. package/components/NFormModal.js +54 -0
  14. package/components/NIconButton.js +81 -0
  15. package/components/NIconCircle.js +66 -0
  16. package/components/NInput.js +105 -0
  17. package/components/NInputPhone.d.ts +1 -1
  18. package/components/NInputPhone.js +46 -0
  19. package/components/NInputPhone.jsx +2 -1
  20. package/components/NInputSelect.js +114 -0
  21. package/components/NInputSuggestion.d.ts +1 -1
  22. package/components/NInputSuggestion.js +63 -0
  23. package/components/NLink.js +59 -0
  24. package/components/NList.js +24 -0
  25. package/components/NLoadingIndicator.js +53 -0
  26. package/components/NModal.js +210 -0
  27. package/components/NPagination.js +108 -0
  28. package/components/NSearchbar.js +66 -0
  29. package/components/NSearchbarList.js +36 -0
  30. package/components/NSelect.js +84 -0
  31. package/components/NSuggestionList.js +156 -0
  32. package/components/NTable.js +126 -0
  33. package/components/NTableAction.js +49 -0
  34. package/components/NTextArea.d.ts +1 -1
  35. package/components/NTextArea.js +128 -0
  36. package/components/NTooltip.js +178 -0
  37. package/components/NValInput.d.ts +1 -1
  38. package/components/NValInput.js +104 -0
  39. package/components/ValidatedForm.js +18 -18
  40. package/i18n/index.d.ts +24 -1
  41. package/i18n/index.js +13 -16
  42. package/index.d.ts +2 -0
  43. package/index.js +2 -0
  44. package/package.json +11 -4
  45. package/utils/breakpoints.js +21 -21
  46. package/utils/component.js +17 -9
  47. package/utils/deferred.js +12 -12
  48. package/utils/identifiable.js +27 -29
  49. package/utils/stringMaxLength.js +8 -13
  50. package/utils/tailwind.js +1 -1
  51. package/utils/utils.js +5 -5
  52. package/utils/vModel.js +82 -73
  53. package/utils/validation.d.ts +9 -3
  54. package/utils/validation.js +67 -83
  55. package/utils/vue.js +7 -5
  56. package/i18n/de/template.json +0 -10
  57. package/i18n/de.d.ts +0 -61
  58. package/i18n/de.js +0 -5
  59. package/i18n/en/template.json +0 -10
  60. package/i18n/en.d.ts +0 -61
  61. package/i18n/en.js +0 -5
@@ -0,0 +1,126 @@
1
+ import { Fragment as _Fragment, createVNode as _createVNode } from "vue";
2
+ import { isWidthBreakpoint } from '../utils/breakpoints';
3
+ import { createComponent, createProps } from '../utils/component';
4
+ import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/solid';
5
+ import { computed, Fragment, ref, watch } from 'vue';
6
+ import NIconButton from './NIconButton';
7
+ import './NTable.css';
8
+ const N_TABLE_ACTION_KEY = 'action';
9
+ export const nTableProps = createProps({
10
+ /**
11
+ * The headings of the table. These define which columns are shown in the table and in which order.
12
+ */
13
+ headings: {
14
+ type: Array,
15
+ required: true
16
+ },
17
+ /**
18
+ * Details can be added additionally to headings.
19
+ * If details are set a little chevron icon is displayed at the end of each table row
20
+ * and if clicked on it the data is displayed in a key-value fashion.
21
+ * The values of the entries should be passed via the `items` prop
22
+ * in the same way as if the details were normal headings.
23
+ * Note that details can be added dynamically by adding the `breakpoint` property to the headings.
24
+ */
25
+ details: {
26
+ type: Array,
27
+ default: () => []
28
+ },
29
+ /**
30
+ * The items of the table. They consist of an array of table rows.
31
+ * Every tablerow is an object containing elements for the heading keys.
32
+ * The elements can either be a primitive value or a function which returns a {@link JSX.Element}.
33
+ * If the item should be treated as an action (e.g. icon-button to display at the end of the row)
34
+ * the dedicated key 'action' can be used.
35
+ * @see TableRow
36
+ * @example
37
+ * // These headings are defined
38
+ * const headings: TableHeading[] = [
39
+ * { key: 'id', label: 'ID' },
40
+ * { key: 'name', label: 'Name' },
41
+ * { key: 'status', label: 'Status' }
42
+ * ]
43
+ *
44
+ * // Appropriate rows for these headings
45
+ * const items: TableRow[] = [
46
+ * { id: 1, name: 'Hubert', status: () => <NBadge ... />, action: ... }, // Row 1
47
+ * { id: 2, name: 'Franzi', status: () => <NBadge ... />, action: ... } // Row 2
48
+ * ]
49
+ */
50
+ items: {
51
+ type: Array,
52
+ default: () => []
53
+ }
54
+ });
55
+ /**
56
+ * The `NTable` is a styled html table which accepts data and displays it appropriately.
57
+ */
58
+ export default createComponent('NTable', nTableProps, props => {
59
+ const headings = computed(() => {
60
+ // remove all headings which are below the breakpoint
61
+ const headings = props.headings.filter(heading => !heading.breakpoint || isWidthBreakpoint(heading.breakpoint).value);
62
+ // The column for actions is shown if there are details
63
+ // or if any of the items contain an element with the action-key.
64
+ if (showDetails.value || props.items.filter(row => row.action != null).length != 0) {
65
+ headings.push({
66
+ key: N_TABLE_ACTION_KEY
67
+ });
68
+ }
69
+ return headings;
70
+ });
71
+ const details = computed(() => {
72
+ // take all headings which are below the breakpoint
73
+ const details = props.headings.filter(heading => heading.breakpoint && !isWidthBreakpoint(heading.breakpoint).value);
74
+ details.push(...props.details);
75
+ return details;
76
+ });
77
+ const showDetails = computed(() => details.value.length > 0);
78
+ const detailsOpen = ref([]);
79
+ const isDetailsOpen = index => detailsOpen.value[index] || false;
80
+ const toggleDetailsOpen = index => detailsOpen.value[index] = !detailsOpen.value[index];
81
+ // if the items change, reset all open details to closed
82
+ // and create correct amount of booleans for all items
83
+ watch(() => props.items, newItems => detailsOpen.value = Array({
84
+ length: newItems.length
85
+ }).map(() => false), {
86
+ immediate: true
87
+ });
88
+ return () => _createVNode("div", {
89
+ "class": "overflow-x-auto"
90
+ }, [_createVNode("table", {
91
+ "class": "min-w-full text-default-500 text-sm"
92
+ }, [_createVNode("thead", {
93
+ "class": "bg-default-50 "
94
+ }, [_createVNode("tr", null, [headings.value.map(heading => _createVNode("th", {
95
+ "key": heading.key,
96
+ "scope": "col",
97
+ "class": "p-4 table-heading"
98
+ }, [heading.label]))])]), props.items.length > 0 && props.items.map((item, itemIndex) => _createVNode(_Fragment, {
99
+ "key": itemIndex
100
+ }, [_createVNode("tbody", {
101
+ "class": ['border-default-200 border-t', itemIndex % 2 === 0 ? 'bg-white' : 'bg-default-50']
102
+ }, [_createVNode("tr", null, [headings.value.map(heading => _createVNode("td", {
103
+ "key": itemIndex + '-' + heading.key,
104
+ "class": "p-4"
105
+ }, [_createVNode("div", {
106
+ "class": ['flex', heading.emph ? 'font-medium text-default-900' : '', heading.cellClass, heading.key == N_TABLE_ACTION_KEY ? 'justify-end items-center space-x-3' : '']
107
+ }, [item[heading.key] && buildItem(item[heading.key]), heading.key == N_TABLE_ACTION_KEY && showDetails.value && _createVNode(NIconButton, {
108
+ "icon": isDetailsOpen(itemIndex) ? ChevronDownIcon : ChevronUpIcon,
109
+ "onClick": () => toggleDetailsOpen(itemIndex)
110
+ }, null)])]))])]), showDetails.value && isDetailsOpen(itemIndex) && _createVNode("tbody", {
111
+ "class": itemIndex % 2 === 0 ? 'bg-white' : 'bg-default-50'
112
+ }, [details.value.map((detail, detailIndex) => _createVNode("tr", {
113
+ "key": `detail-${detailIndex}`
114
+ }, [_createVNode("td", {
115
+ "class": ['table-heading px-4 py-1', details.value.length - 1 == detailIndex ? 'pb-4' : '']
116
+ }, [detail.label]), _createVNode("td", {
117
+ "class": ['px-4 py-1', details.value.length - 1 == detailIndex ? 'pb-4' : ''],
118
+ "colspan": headings.value.length - 1
119
+ }, [item[detail.key] && buildItem(item[detail.key])])]))])]))])]);
120
+ });
121
+ /**
122
+ * Builds a JSX-Element out of the item
123
+ */
124
+ function buildItem(item) {
125
+ if (typeof item == 'string' || typeof item == 'number') return _createVNode(_Fragment, null, [item]);else return item();
126
+ }
@@ -0,0 +1,49 @@
1
+ import { isVNode as _isVNode, createVNode as _createVNode, Fragment as _Fragment } from "vue";
2
+ import { createComponent, createProps } from '../utils/component';
3
+ import { RouterLink } from 'vue-router';
4
+ import { nButtonProps } from './NButton';
5
+ function _isSlot(s) {
6
+ return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !_isVNode(s);
7
+ }
8
+ export const nTableActionProps = createProps({
9
+ /**
10
+ * The route of the action. If set the component will be a {@link RouterLink}.
11
+ */
12
+ route: [String, Object],
13
+ /**
14
+ * The text of the action.
15
+ */
16
+ text: String,
17
+ /**
18
+ * The html attribute, which indicates the type of the button.
19
+ */
20
+ type: nButtonProps.type,
21
+ /**
22
+ * This is called when the action is clicked.
23
+ * It is only called when the `route` prop is not set on the action.
24
+ */
25
+ onClick: Function
26
+ });
27
+ /**
28
+ * The `NTableAction` is a button or {@link RouterLink} which is styled to fit into a table.
29
+ * It is basically styled as an emphasized text in the table.
30
+ */
31
+ export default createComponent('NTableAction', nTableActionProps, (props, {
32
+ slots
33
+ }) => {
34
+ const content = () => slots.default?.() || _createVNode(_Fragment, null, [props.text]);
35
+ const classes = 'text-left font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-default-900 rounded-sm ring-offset-2 text-default-900 hover:underline hover:text-default-700';
36
+ return () => {
37
+ let _slot;
38
+ return props.route ? _createVNode(RouterLink, {
39
+ "to": props.route,
40
+ "class": classes
41
+ }, _isSlot(_slot = content()) ? _slot : {
42
+ default: () => [_slot]
43
+ }) : _createVNode("button", {
44
+ "type": props.type,
45
+ "class": classes,
46
+ "onClick": props.onClick
47
+ }, [content()]);
48
+ };
49
+ });
@@ -241,10 +241,10 @@ declare const _default: import("vue").DefineComponent<{
241
241
  tooltipMaxWidth: import("../utils/tailwind").TWMaxWidth;
242
242
  disabled: boolean;
243
243
  error: boolean;
244
- rules: import("../utils/validation").ValidationRule | import("../utils/validation").ValidationRule[];
245
244
  autocomplete: string;
246
245
  hideLabel: boolean;
247
246
  optional: boolean;
247
+ rules: import("../utils/validation").ValidationRule | import("../utils/validation").ValidationRule[];
248
248
  hideErrorMessage: boolean;
249
249
  disableBlurValidation: boolean;
250
250
  resizable: boolean;
@@ -0,0 +1,128 @@
1
+ import { withDirectives as _withDirectives, vShow as _vShow, createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
2
+ import { createComponent, createProps } from '../utils/component';
3
+ import { ref } from 'vue';
4
+ import { ExclamationCircleIcon } from '@heroicons/vue/24/solid';
5
+ import NTooltip, { mapTooltipProps, nToolTipPropsForImplementor } from './NTooltip';
6
+ import { vModelProps } from '../utils/vModel';
7
+ import NValInput, { validationProps } from './NValInput';
8
+ const nTextAreaBaseProps = createProps({
9
+ ...vModelProps(String),
10
+ /**
11
+ * The name of the text area. Is displayed as a label above the text area.
12
+ */
13
+ name: String,
14
+ /**
15
+ * The placeholder of the text area.
16
+ */
17
+ placeholder: String,
18
+ /**
19
+ * The html autocomplete attribute of the text area.
20
+ */
21
+ autocomplete: {
22
+ type: String,
23
+ default: 'off'
24
+ },
25
+ /**
26
+ * If set to `true`, the text area is resizable in y-direction.
27
+ */
28
+ resizable: {
29
+ type: Boolean,
30
+ default: true
31
+ },
32
+ /**
33
+ * The initial height of the text area in terms of
34
+ * how many text rows fit inside the text area.
35
+ * The height can be change if {@link nTextAreaProps.resizable} is `true`
36
+ */
37
+ rows: Number,
38
+ /**
39
+ * The maximum length of the input string. Entering longer strings are simply
40
+ * prevented, but no error message is shown to the user.
41
+ */
42
+ maxLength: Number,
43
+ /**
44
+ * If set to `true` the text area is displayed with a red border.
45
+ */
46
+ error: Boolean,
47
+ /**
48
+ * If set to `true` the text area is disabled and no interaction is possible.
49
+ */
50
+ disabled: Boolean,
51
+ /**
52
+ * If set to `true` the text area's label is hidden.
53
+ */
54
+ hideLabel: Boolean,
55
+ /**
56
+ * This is called when the text area reveices focus.
57
+ */
58
+ onFocus: Function,
59
+ /**
60
+ * This is called when the text area looses focus.
61
+ */
62
+ onBlur: Function,
63
+ ...nToolTipPropsForImplementor
64
+ });
65
+ export const nTextAreaProps = createProps({
66
+ ...nTextAreaBaseProps,
67
+ ...validationProps
68
+ });
69
+ export default createComponent('NTextArea', nTextAreaProps, (props, context) => {
70
+ const textAreaRef = ref();
71
+ const exposed = {
72
+ focus: () => textAreaRef.value?.focus()
73
+ };
74
+ context.expose(exposed);
75
+ return () => _createVNode(NValInput, _mergeProps(props, {
76
+ "input": ({
77
+ error,
78
+ onBlur,
79
+ onUpdateValue
80
+ }) => _createVNode(NTextAreaBase, _mergeProps({
81
+ "ref": textAreaRef
82
+ }, {
83
+ ...props,
84
+ error,
85
+ onBlur,
86
+ onUpdateValue
87
+ }), null)
88
+ }), null);
89
+ });
90
+ /**
91
+ * The `NTextArea` wraps the html text area with all the features from {@link NInput} and {@link NValInput}.
92
+ */
93
+ const NTextAreaBase = createComponent('NTextAreaBase', nTextAreaBaseProps, (props, context) => {
94
+ const textAreaRef = ref();
95
+ const exposed = {
96
+ focus: () => textAreaRef.value?.focus()
97
+ };
98
+ context.expose(exposed);
99
+ return () => _createVNode("div", null, [props.name && !props.hideLabel && _createVNode("label", {
100
+ "for": props.name,
101
+ "class": ['block text-sm font-medium mb-1', props.disabled ? 'text-default-300' : 'text-default-700']
102
+ }, [props.name]), _createVNode(NTooltip, _mergeProps({
103
+ "block": true
104
+ }, mapTooltipProps(props)), {
105
+ default: () => [_createVNode("div", {
106
+ "class": "relative"
107
+ }, [_createVNode("textarea", {
108
+ "ref": textAreaRef,
109
+ "name": props.name,
110
+ "value": props.value,
111
+ "onInput": event => props.onUpdateValue?.(event.target.value),
112
+ "placeholder": props.placeholder,
113
+ "autocomplete": props.autocomplete,
114
+ "disabled": props.disabled,
115
+ "rows": props.rows,
116
+ "maxlength": props.maxLength,
117
+ "onFocus": () => props.onFocus?.(),
118
+ "onBlur": () => props.onBlur?.(),
119
+ "onInvalid": event => event.preventDefault(),
120
+ "class": ['block w-full rounded-md border focus:outline-none focus:ring-1 ', props.disabled ? 'text-default-500 placeholder-default-300 bg-default-50' : 'text-default-900 placeholder-default-400 ', props.error ? 'border-red-500 focus:border-red-500 focus:ring-red-500 pr-10' : 'border-default-300 focus:border-primary-500 focus:ring-primary-500', props.resizable ? 'resize-y' : 'resize-none']
121
+ }, null), _withDirectives(_createVNode("div", {
122
+ "class": "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none"
123
+ }, [_createVNode(ExclamationCircleIcon, {
124
+ "class": "h-5 w-5 text-red-700",
125
+ "aria-hidden": "true"
126
+ }, null)]), [[_vShow, props.error]])])]
127
+ })]);
128
+ });
@@ -0,0 +1,178 @@
1
+ import { withDirectives as _withDirectives, vShow as _vShow, Fragment as _Fragment, createVNode as _createVNode } from "vue";
2
+ import { createComponent, createProps } from '../utils/component';
3
+ import { uniqueId } from '../utils/utils';
4
+ import { computed, onMounted, ref, watch, onUnmounted, Transition } from 'vue';
5
+ import { createPopper } from '@popperjs/core';
6
+ import { watchRef } from '../utils/vue';
7
+ import './NTooltip.css';
8
+ export const nTooltipProps = createProps({
9
+ /**
10
+ * The text content of the tooltip.
11
+ */
12
+ text: String,
13
+ /**
14
+ * A slot to replace the content of the tooltip. This will override the `text` prop.
15
+ */
16
+ content: Function,
17
+ /**
18
+ * If set to `true` the tooltip is shown constantly.
19
+ */
20
+ show: Boolean,
21
+ /**
22
+ * If set to `true` the tooltip is hidden constantly.
23
+ */
24
+ hide: Boolean,
25
+ /**
26
+ * If set to `true` the `block` class is applied to the tooltip.
27
+ * This should be set if the content in the default slot is also block.
28
+ */
29
+ block: Boolean,
30
+ /**
31
+ * The placement of the tooltip.
32
+ */
33
+ placement: {
34
+ type: String,
35
+ default: 'auto'
36
+ },
37
+ /**
38
+ * The maximum width of the tooltip.
39
+ */
40
+ maxWidth: {
41
+ type: String,
42
+ default: 'max-w-xs'
43
+ }
44
+ });
45
+ /**
46
+ * These props are made to use on a component which implements the tooltip
47
+ * and wants it to be controllable via the own props.
48
+ * e.g. `text` is now called `tooltipText`.
49
+ * They can be mapped to the normal tooltip props with {@link mapTooltipProps}
50
+ */
51
+ export const nToolTipPropsForImplementor = {
52
+ /**
53
+ * Adds a tooltip to the component with the specified text.
54
+ * @see {@link nTooltipProps.text}
55
+ */
56
+ tooltipText: nTooltipProps.text,
57
+ /**
58
+ * A slot for the tooltip of this component.
59
+ * If the slot is set, the tooltip with the specified content is added to the component.
60
+ * @see {@link nTooltipProps.content}
61
+ */
62
+ tooltipContent: nTooltipProps.content,
63
+ /**
64
+ * @see {@link nTooltipProps.hide}
65
+ */
66
+ tooltipHide: nTooltipProps.hide,
67
+ /**
68
+ * @see {@link nTooltipProps.show}
69
+ */
70
+ tooltipShow: nTooltipProps.show,
71
+ /**
72
+ * @see {@link nTooltipProps.placement}
73
+ */
74
+ tooltipPlacement: nTooltipProps.placement,
75
+ /**
76
+ * @see {@link nTooltipProps.maxWidth}
77
+ */
78
+ tooltipMaxWidth: nTooltipProps.maxWidth
79
+ };
80
+ /**
81
+ * Maps the {@link nToolTipPropsForImplementor} props to normal tooltip props
82
+ * @returns an object containing the normal tooltip props.
83
+ */
84
+ export function mapTooltipProps(props) {
85
+ return {
86
+ text: props.tooltipText,
87
+ content: props.tooltipContent,
88
+ hide: props.tooltipHide,
89
+ show: props.tooltipShow,
90
+ placement: props.tooltipPlacement,
91
+ maxWidth: props.tooltipMaxWidth
92
+ };
93
+ }
94
+ /**
95
+ * The `NTooltip` is a wrapper for any component which adds a tooltip to it.
96
+ * Any component can just be passed in the default slot and a tooltip will be added to it.
97
+ * Note that this component disappears when neither the `text` nor the `content`
98
+ * prop is passed as the tooltip would then be empty.
99
+ * If this is the case, the default slot will just be rendered inside a div.
100
+ * @example
101
+ * <NTooltip text="Hello">
102
+ * <NButton />
103
+ * </NTooltip>
104
+ */
105
+ export default createComponent('NTooltip', nTooltipProps, (props, {
106
+ slots
107
+ }) => {
108
+ return () => _createVNode("div", {
109
+ "class": props.block ? 'block' : 'inline-block'
110
+ }, [props.content || props.text ? _createVNode(NTooltipBase, props, {
111
+ default: () => [slots.default?.()]
112
+ }) : slots.default?.()]);
113
+ });
114
+ const NTooltipBase = createComponent('NTooltipBase', nTooltipProps, (props, {
115
+ slots
116
+ }) => {
117
+ let popperInstance = null;
118
+ const contentId = `content-${uniqueId()}`;
119
+ const tooltipId = `tooltip-${uniqueId()}`;
120
+ function createTooltip() {
121
+ const content = document.getElementById(contentId);
122
+ const tooltip = document.getElementById(tooltipId);
123
+ if (content && tooltip) {
124
+ popperInstance = createPopper(content, tooltip, {
125
+ placement: props.placement,
126
+ modifiers: [{
127
+ name: 'offset',
128
+ options: {
129
+ offset: [0, 8]
130
+ }
131
+ }]
132
+ });
133
+ } else {
134
+ console.error('Could not create tooltip. HTML elements for content or tooltip were not found.');
135
+ }
136
+ }
137
+ function destroyTooltip() {
138
+ popperInstance?.destroy();
139
+ popperInstance = null;
140
+ }
141
+ onMounted(createTooltip);
142
+ onUnmounted(destroyTooltip);
143
+ watch(() => props.placement, newPlacement => popperInstance?.setOptions({
144
+ placement: newPlacement
145
+ }));
146
+ const isHoveringContent = ref(false);
147
+ const isHoveringTooltip = ref(false);
148
+ const isHovering = computed(() => isHoveringContent.value || isHoveringTooltip.value);
149
+ const showTooltip = computed(() => props.show || !props.hide && isHovering.value);
150
+ watchRef(showTooltip, () => popperInstance?.update());
151
+ return () => _createVNode(_Fragment, null, [_createVNode("div", {
152
+ "class": "p-[10px] -m-[10px]",
153
+ "onMouseleave": () => setTimeout(() => isHoveringContent.value = false, 10)
154
+ }, [_createVNode("div", {
155
+ "id": contentId,
156
+ "onMouseenter": () => isHoveringContent.value = true
157
+ }, [slots.default?.()])]), _createVNode(Transition, {
158
+ "enterActiveClass": "transition-opacity ease-out duration-100",
159
+ "enterFromClass": "opacity-0",
160
+ "enterToClass": "opacity-100",
161
+ "leaveActiveClass": "transition-opacity ease-in duration-75",
162
+ "leaveFromClass": "opacity-100",
163
+ "leaveToClass": "opacity-0"
164
+ }, {
165
+ default: () => [_withDirectives(_createVNode("div", {
166
+ "id": tooltipId,
167
+ "role": "tooltip",
168
+ "onMouseenter": () => isHoveringTooltip.value = true,
169
+ "onMouseleave": () => isHoveringTooltip.value = false,
170
+ "class": [isHovering.value ? 'z-20' : 'z-10', props.maxWidth, 'tooltip']
171
+ }, [_createVNode("div", {
172
+ "class": "bg-white rounded-md py-2 px-4 shadow-lg border-default-200 border text-sm font-normal text-default-700"
173
+ }, [props.content?.() || props.text]), _createVNode("div", {
174
+ "data-popper-arrow": true,
175
+ "class": "arrow"
176
+ }, null)]), [[_vShow, showTooltip.value]])]
177
+ })]);
178
+ });
@@ -287,10 +287,10 @@ declare const _default: import("vue").DefineComponent<{
287
287
  tooltipMaxWidth: import("../utils/tailwind").TWMaxWidth;
288
288
  disabled: boolean;
289
289
  error: boolean;
290
- rules: ValidationRule | ValidationRule[];
291
290
  autocomplete: string;
292
291
  hideLabel: boolean;
293
292
  optional: boolean;
293
+ rules: ValidationRule | ValidationRule[];
294
294
  hideErrorMessage: boolean;
295
295
  disableBlurValidation: boolean;
296
296
  }>;
@@ -0,0 +1,104 @@
1
+ import { createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
2
+ import { createComponent, createProps } from '../utils/component';
3
+ import { computed } from 'vue';
4
+ import { ref, reactive, watch } from 'vue';
5
+ import NInput, { nInputProps } from './NInput';
6
+ import { validate, required } from '../utils/validation';
7
+ export const validationProps = createProps({
8
+ /**
9
+ * If set to `true` this input is always valid when its value is empty.
10
+ * If set to `false` the input receives the {@link required} rule. Default is `false`.
11
+ */
12
+ optional: Boolean,
13
+ /**
14
+ * The rules which this input is checked with.
15
+ * The rules are checked sequentially and the error of the first failed rule is displayed.
16
+ * If `optional` is set to false, the rule {@link required} will be checked first.
17
+ */
18
+ rules: {
19
+ type: [Function, Array],
20
+ default: () => []
21
+ },
22
+ /**
23
+ * The form, which this input will be added to.
24
+ * On initialization, this input will call {@link ValidatedForm.addInput} passing itself to the form.
25
+ */
26
+ form: Object,
27
+ /**
28
+ * Overrides the internal error state. If set to true, it will always display an error.
29
+ */
30
+ error: Boolean,
31
+ /**
32
+ * Overrides the internal error message. If set, this message is always displayed.
33
+ */
34
+ errorMessage: String,
35
+ /**
36
+ * If set to `true` the error message is not shown.
37
+ * However, the input is still marked red if it is in an error state.
38
+ */
39
+ hideErrorMessage: Boolean,
40
+ /**
41
+ * Disables the validation on blur. Should only be used in special occasions.
42
+ */
43
+ disableBlurValidation: Boolean
44
+ });
45
+ export const nValInputProps = createProps({
46
+ ...nInputProps,
47
+ ...validationProps,
48
+ /**
49
+ * A slot to replace the input.
50
+ */
51
+ input: Function
52
+ });
53
+ /**
54
+ * The `NValInput` is a `NInput` with custom validation.
55
+ */
56
+ export default createComponent('NValInput', nValInputProps, (props, context) => {
57
+ const rules = computed(() => {
58
+ const otherRules = Array.isArray(props.rules) ? props.rules : [props.rules];
59
+ return props.optional ? otherRules : [required, ...otherRules];
60
+ });
61
+ const validationResult = ref();
62
+ const validateRules = input => {
63
+ const result = validate(input, rules.value);
64
+ validationResult.value = result;
65
+ return result;
66
+ };
67
+ const showError = computed(() => props.error || validationResult.value != null && !validationResult.value.isValid);
68
+ const showErrorMessage = computed(() => !props.hideErrorMessage && showError.value);
69
+ const errorMessage = computed(() => props.errorMessage || validationResult.value?.errorMessage);
70
+ const validateIfError = (value = props.value) => {
71
+ if (showError.value) validateRules(value);
72
+ };
73
+ watch(() => props.value, () => validateIfError());
74
+ watch(() => rules.value, () => validateIfError());
75
+ const onBlur = () => {
76
+ if (!props.disableBlurValidation) validateRules(props.value);
77
+ props.onBlur?.();
78
+ };
79
+ const onUpdateValue = newValue => {
80
+ validateIfError(newValue);
81
+ props.onUpdateValue?.(newValue);
82
+ };
83
+ const inputSlotProps = reactive({
84
+ onBlur,
85
+ onUpdateValue,
86
+ error: showError
87
+ });
88
+ const inputRef = ref();
89
+ const expose = {
90
+ validate: () => validateRules(props.value),
91
+ reset: () => validationResult.value = undefined,
92
+ focus: () => inputRef.value?.focus()
93
+ };
94
+ context.expose(expose);
95
+ props.form?.addInput(expose);
96
+ return () => _createVNode("div", null, [props.input?.(inputSlotProps) || _createVNode(NInput, _mergeProps({
97
+ "ref": inputRef
98
+ }, {
99
+ ...props,
100
+ ...inputSlotProps
101
+ }), null), showErrorMessage.value && _createVNode("p", {
102
+ "class": "text-red-500 text-xs mt-1"
103
+ }, [errorMessage.value])]);
104
+ });
@@ -3,23 +3,23 @@
3
3
  * @returns the instance of the new form.
4
4
  */
5
5
  export function createValidatedForm() {
6
- return new ValidatedFormImpl();
6
+ return new ValidatedFormImpl();
7
7
  }
8
8
  class ValidatedFormImpl {
9
- inputs = [];
10
- addInput(input) {
11
- this.inputs.push(input);
12
- }
13
- validate() {
14
- const results = this.inputs.map(input => input.validate());
15
- // return first invalid result
16
- for (const result of results)
17
- if (result && !result.isValid)
18
- return result;
19
- // else return valid result
20
- return { isValid: true };
21
- }
22
- reset() {
23
- this.inputs.forEach(input => input.reset());
24
- }
25
- }
9
+ inputs = [];
10
+ addInput(input) {
11
+ this.inputs.push(input);
12
+ }
13
+ validate() {
14
+ const results = this.inputs.map(input => input.validate());
15
+ // return first invalid result
16
+ for (const result of results) if (result && !result.isValid) return result;
17
+ // else return valid result
18
+ return {
19
+ isValid: true
20
+ };
21
+ }
22
+ reset() {
23
+ this.inputs.forEach(input => input.reset());
24
+ }
25
+ }
package/i18n/index.d.ts CHANGED
@@ -1,3 +1,26 @@
1
+ import type { Nullish } from '../utils/utils';
2
+ /**
3
+ * @see {@link trsl}
4
+ */
5
+ export type TranslationFunction = typeof trsl;
6
+ /**
7
+ * @see {@link trslc}
8
+ */
9
+ export type TranslationCountFunction = typeof trslc;
10
+ /**
11
+ * A `TranslationProvider` has to implement the two functions `trsl` and `trslc`.
12
+ */
13
+ export type TranslationProvider = {
14
+ trsl: TranslationFunction;
15
+ trslc: TranslationCountFunction;
16
+ };
17
+ /**
18
+ * Registeres a new translation provider for vue-collection.
19
+ * The translation provider should contain all vue-collection
20
+ * texts located under `i18n/<lang>/vue-collection.json`.
21
+ * @param newProvider
22
+ */
23
+ export declare function registerTranslationProvider(newProvider: TranslationProvider): void;
1
24
  /**
2
25
  * Translates the specified key with the according message.
3
26
  * @param key the key to translate.
@@ -14,4 +37,4 @@ export declare function trsl(key: string, params?: Record<string, unknown>): str
14
37
  * @returns the translated message.
15
38
  * @see trsl
16
39
  */
17
- export declare function trslc(key: string, count: number | null | undefined, params?: Record<string, unknown>): string;
40
+ export declare function trslc(key: string, count: Nullish<number>, params?: Record<string, unknown>): string;