@veritree/ui 0.77.2 → 0.78.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mixins/floating-ui.js +80 -1
- package/mixins/form-control-icon.js +8 -8
- package/nuxt.js +1 -0
- package/package.json +6 -5
- package/src/components/Dialog/VTDialogFooter.vue +9 -1
- package/src/components/Form/VTInput.vue +2 -0
- package/src/components/Form/VTInputDate.vue +352 -27
- package/src/components/Form/VTInputIcon.vue +31 -3
- package/src/components/Form/VTInputNumber.vue +97 -0
- package/src/components/ProgressBar/VTProgressBar.vue +12 -9
- package/src/components/ProgressBar/VTProgressBarIndicator.vue +47 -0
- package/src/components/ScrollShadows/VTScrollShadows.vue +76 -0
- package/src/components/Tooltip/VTTooltip.vue +28 -2
package/mixins/floating-ui.js
CHANGED
|
@@ -3,7 +3,7 @@ import { computePosition, flip, shift, offset, size } from '@floating-ui/dom';
|
|
|
3
3
|
export const floatingUiMixin = {
|
|
4
4
|
props: {
|
|
5
5
|
offset: {
|
|
6
|
-
type: [Number, String],
|
|
6
|
+
type: [Number, String, Object],
|
|
7
7
|
default: 5,
|
|
8
8
|
},
|
|
9
9
|
},
|
|
@@ -39,11 +39,90 @@ export const floatingUiMixin = {
|
|
|
39
39
|
this.active = null;
|
|
40
40
|
},
|
|
41
41
|
|
|
42
|
+
positionContentToTriggerByTopAndLeft(trigger, content, minWidthLimit) {
|
|
43
|
+
/*
|
|
44
|
+
TODO: Try to replace this with the offset object from floating-ui, using mainAxis, crossAxis, and alignmentAxis.
|
|
45
|
+
The problem is that the floating-ui offset values are px values, not percentages, which is the use case I am trying to solve.
|
|
46
|
+
I tried to calculate the percentage based off of the width and height of the trigger element,
|
|
47
|
+
but depending on the placement prop, mainAxis could be x OR y and crossAxis could be x OR y,
|
|
48
|
+
so to calculate the offset based on the width/height of the trigger element
|
|
49
|
+
we would have to calculate it differently for each individual placement preset.
|
|
50
|
+
*/
|
|
51
|
+
const contentRect = content.getBoundingClientRect();
|
|
52
|
+
const contentWidth = contentRect.width;
|
|
53
|
+
const contentHeight = contentRect.height;
|
|
54
|
+
const triggerWidth = trigger.getBoundingClientRect().width;
|
|
55
|
+
|
|
56
|
+
let leftObject = this.leftPosition || {};
|
|
57
|
+
let topObject = this.topPosition || {};
|
|
58
|
+
|
|
59
|
+
let left = leftObject.value || 0;
|
|
60
|
+
let top = topObject.value || 0;
|
|
61
|
+
|
|
62
|
+
if (leftObject.unit === '%') {
|
|
63
|
+
left = (triggerWidth * left) / 100;
|
|
64
|
+
}
|
|
65
|
+
if (topObject.unit === '%') {
|
|
66
|
+
top = (triggerWidth * top) / 100;
|
|
67
|
+
}
|
|
68
|
+
if (leftObject.positionBy === 'center') {
|
|
69
|
+
left = left - contentWidth / 2;
|
|
70
|
+
}
|
|
71
|
+
if (leftObject.positionBy === 'end') {
|
|
72
|
+
left = left - contentWidth;
|
|
73
|
+
}
|
|
74
|
+
if (topObject.positionBy === 'top') {
|
|
75
|
+
top = top + contentHeight;
|
|
76
|
+
}
|
|
77
|
+
if (topObject.positionBy === 'center') {
|
|
78
|
+
top = top + contentHeight / 2;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
computePosition(trigger, content, {
|
|
82
|
+
placement: 'top-start',
|
|
83
|
+
middleware: [
|
|
84
|
+
flip(),
|
|
85
|
+
shift({ padding: 5 }),
|
|
86
|
+
size({
|
|
87
|
+
apply({ rects }) {
|
|
88
|
+
if (!minWidthLimit) return;
|
|
89
|
+
|
|
90
|
+
const width = rects.reference.width;
|
|
91
|
+
const minWidth = width < minWidthLimit ? minWidthLimit : width;
|
|
92
|
+
|
|
93
|
+
Object.assign(content.style, {
|
|
94
|
+
minWidth: `${minWidth}px`,
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
],
|
|
99
|
+
}).then(({ x, y }) => {
|
|
100
|
+
Object.assign(content.style, {
|
|
101
|
+
left: `${x + left}px`,
|
|
102
|
+
top: `${y + top}px`,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
|
|
42
107
|
positionContentToTrigger() {
|
|
43
108
|
const trigger = document.getElementById(this.componentTrigger.id);
|
|
44
109
|
const content = document.getElementById(this.componentContent.id);
|
|
45
110
|
const minWidthLimit = Number(this.floatingUiMinWidth);
|
|
46
111
|
|
|
112
|
+
if (
|
|
113
|
+
this.top !== null ||
|
|
114
|
+
this.top !== undefined ||
|
|
115
|
+
this.left !== null ||
|
|
116
|
+
this.left !== undefined
|
|
117
|
+
) {
|
|
118
|
+
this.positionContentToTriggerByTopAndLeft(
|
|
119
|
+
trigger,
|
|
120
|
+
content,
|
|
121
|
+
minWidthLimit,
|
|
122
|
+
);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
47
126
|
computePosition(trigger, content, {
|
|
48
127
|
placement: this.placement,
|
|
49
128
|
middleware: [
|
|
@@ -30,10 +30,10 @@ export const formControlIconMixin = {
|
|
|
30
30
|
this.headless
|
|
31
31
|
? `form-control-icon--${this.placement}`
|
|
32
32
|
: this.isLeft
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
? '[&_input]:pl-9'
|
|
34
|
+
: this.isRight
|
|
35
|
+
? '[&_input]:pr-9'
|
|
36
|
+
: null,
|
|
37
37
|
];
|
|
38
38
|
},
|
|
39
39
|
|
|
@@ -46,10 +46,10 @@ export const formControlIconMixin = {
|
|
|
46
46
|
this.headless
|
|
47
47
|
? null
|
|
48
48
|
: this.isLeft
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
? 'left-1'
|
|
50
|
+
: this.isRight
|
|
51
|
+
? 'right-1'
|
|
52
|
+
: null,
|
|
53
53
|
];
|
|
54
54
|
},
|
|
55
55
|
},
|
package/nuxt.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veritree/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.78.0-1",
|
|
4
4
|
"description": "veritree ui library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
"release": "np --no-tests --no-2fa"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@floating-ui/dom": "^1.
|
|
18
|
+
"@floating-ui/dom": "^1.6.5",
|
|
19
19
|
"@linusborg/vue-simple-portal": "^0.1.5",
|
|
20
|
-
"@
|
|
21
|
-
"
|
|
20
|
+
"@sum.cumo/vue-datepicker": "^4.0.0",
|
|
21
|
+
"@veritree/icons": "^0.62.0",
|
|
22
|
+
"dayjs": "^1.11.11"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@babel/eslint-parser": "^7.23.10",
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
"stylelint-config-prettier": "^9.0.5",
|
|
34
35
|
"stylelint-config-recommended-vue": "^1.5.0",
|
|
35
36
|
"stylelint-config-standard": "^36.0.0",
|
|
36
|
-
"tailwindcss": "^3.4.
|
|
37
|
+
"tailwindcss": "^3.4.3"
|
|
37
38
|
},
|
|
38
39
|
"engines": {
|
|
39
40
|
"npm": ">=8.0.0",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
: '-mx-4 flex items-center justify-between gap-x-3 p-4 md:-mx-6 md:px-6 md:py-5',
|
|
8
8
|
]"
|
|
9
9
|
>
|
|
10
|
-
<slot></slot>
|
|
10
|
+
<slot :hide="hide"></slot>
|
|
11
11
|
</component>
|
|
12
12
|
</template>
|
|
13
13
|
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
export default {
|
|
16
16
|
name: 'VTDialogFooter',
|
|
17
17
|
|
|
18
|
+
inject: ['apiDialog'],
|
|
19
|
+
|
|
18
20
|
props: {
|
|
19
21
|
headless: {
|
|
20
22
|
type: Boolean,
|
|
@@ -25,5 +27,11 @@ export default {
|
|
|
25
27
|
default: 'footer',
|
|
26
28
|
},
|
|
27
29
|
},
|
|
30
|
+
|
|
31
|
+
methods: {
|
|
32
|
+
hide() {
|
|
33
|
+
this.apiDialog().hide();
|
|
34
|
+
},
|
|
35
|
+
},
|
|
28
36
|
};
|
|
29
37
|
</script>
|
|
@@ -2,30 +2,35 @@
|
|
|
2
2
|
<VTPopover>
|
|
3
3
|
<VTPopoverTrigger ref="trigger">
|
|
4
4
|
<button :class="classComputed" :disabled="disabled" @click.prevent>
|
|
5
|
-
<span v-if="valueModel">{{
|
|
6
|
-
|
|
7
|
-
}}</span>
|
|
8
|
-
<span class="text-gray-500" v-else>yyyy-mm-dd</span>
|
|
5
|
+
<span v-if="valueModel">{{ valueModelFormatted }}</span>
|
|
6
|
+
<span class="text-gray-500" v-else>{{ format }}</span>
|
|
9
7
|
<span
|
|
10
8
|
v-if="!iconless"
|
|
11
9
|
class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2"
|
|
12
|
-
:class="[disabled ? 'bg-gray-
|
|
10
|
+
:class="[disabled ? 'bg-gray-200' : 'bg-white']"
|
|
13
11
|
>
|
|
14
12
|
<IconCalendar class="-z-0 h-5 w-5 scale-90 text-gray-600" />
|
|
15
13
|
</span>
|
|
16
14
|
</button>
|
|
17
15
|
</VTPopoverTrigger>
|
|
18
|
-
<VTPopoverContent @hidden="$emit('hidden')" @shown="
|
|
19
|
-
<
|
|
16
|
+
<VTPopoverContent @hidden="$emit('hidden')" @shown="onShown">
|
|
17
|
+
<Datepicker
|
|
20
18
|
v-model="valueModel"
|
|
21
|
-
:
|
|
22
|
-
:
|
|
23
|
-
:
|
|
24
|
-
:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@
|
|
28
|
-
|
|
19
|
+
:inline="true"
|
|
20
|
+
:initial-view="initialView"
|
|
21
|
+
:minimum-view="minimumView"
|
|
22
|
+
:maximum-view="maximumView"
|
|
23
|
+
:show-edge-dates="showEdgeDates"
|
|
24
|
+
:disabled-dates="disabledDatesComputed"
|
|
25
|
+
@input="onInput"
|
|
26
|
+
>
|
|
27
|
+
<template #prevIntervalBtn>
|
|
28
|
+
<IconChevronLeft />
|
|
29
|
+
</template>
|
|
30
|
+
<template #nextIntervalBtn>
|
|
31
|
+
<IconChevronRight />
|
|
32
|
+
</template>
|
|
33
|
+
</Datepicker>
|
|
29
34
|
</VTPopoverContent>
|
|
30
35
|
</VTPopover>
|
|
31
36
|
</template>
|
|
@@ -38,7 +43,9 @@ import {
|
|
|
38
43
|
import VTPopover from './../Popover/VTPopover.vue';
|
|
39
44
|
import VTPopoverTrigger from './../Popover/VTPopoverTrigger.vue';
|
|
40
45
|
import VTPopoverContent from './../Popover/VTPopoverContent.vue';
|
|
41
|
-
import
|
|
46
|
+
import Datepicker from '@sum.cumo/vue-datepicker';
|
|
47
|
+
import { IconChevronLeft, IconChevronRight } from '@veritree/icons';
|
|
48
|
+
import dayjs from 'dayjs';
|
|
42
49
|
|
|
43
50
|
export default {
|
|
44
51
|
name: 'VTInputDate',
|
|
@@ -46,17 +53,23 @@ export default {
|
|
|
46
53
|
mixins: [formControlMixin, formControlStyleMixin],
|
|
47
54
|
|
|
48
55
|
components: {
|
|
56
|
+
Datepicker,
|
|
57
|
+
IconChevronLeft,
|
|
58
|
+
IconChevronRight,
|
|
49
59
|
VTPopover,
|
|
50
60
|
VTPopoverTrigger,
|
|
51
61
|
VTPopoverContent,
|
|
52
|
-
DatePicker,
|
|
53
62
|
},
|
|
54
63
|
|
|
55
64
|
props: {
|
|
56
|
-
|
|
57
|
-
type: Object,
|
|
65
|
+
disabledDates: {
|
|
66
|
+
type: [Function, Object],
|
|
58
67
|
default: null,
|
|
59
68
|
},
|
|
69
|
+
format: {
|
|
70
|
+
type: String,
|
|
71
|
+
default: 'YYYY-MM-DD',
|
|
72
|
+
},
|
|
60
73
|
min: {
|
|
61
74
|
type: [String, Date],
|
|
62
75
|
default: null,
|
|
@@ -65,17 +78,26 @@ export default {
|
|
|
65
78
|
type: [String, Date],
|
|
66
79
|
default: null,
|
|
67
80
|
},
|
|
81
|
+
initialView: {
|
|
82
|
+
type: String,
|
|
83
|
+
default: 'day',
|
|
84
|
+
},
|
|
85
|
+
minimumView: {
|
|
86
|
+
type: String,
|
|
87
|
+
default: 'day',
|
|
88
|
+
},
|
|
89
|
+
maximumView: {
|
|
90
|
+
type: String,
|
|
91
|
+
default: 'year',
|
|
92
|
+
},
|
|
93
|
+
showEdgeDates: {
|
|
94
|
+
type: Boolean,
|
|
95
|
+
default: true,
|
|
96
|
+
},
|
|
68
97
|
iconless: {
|
|
69
98
|
type: Boolean,
|
|
70
99
|
default: false,
|
|
71
100
|
},
|
|
72
|
-
modelConfig: {
|
|
73
|
-
type: Object,
|
|
74
|
-
default: () => ({
|
|
75
|
-
type: 'string',
|
|
76
|
-
mask: 'YYYY-MM-DD',
|
|
77
|
-
}),
|
|
78
|
-
},
|
|
79
101
|
},
|
|
80
102
|
|
|
81
103
|
computed: {
|
|
@@ -88,12 +110,315 @@ export default {
|
|
|
88
110
|
this.$emit('change', newDate);
|
|
89
111
|
},
|
|
90
112
|
},
|
|
113
|
+
|
|
114
|
+
valueModelFormatted() {
|
|
115
|
+
return dayjs(this.value).format(this.format);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
disabledDatesComputed() {
|
|
119
|
+
if (this.disabledDates) {
|
|
120
|
+
return this.disabledDates;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
to: this.min,
|
|
125
|
+
from: this.max,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
91
128
|
},
|
|
92
129
|
|
|
93
130
|
methods: {
|
|
94
|
-
|
|
131
|
+
onShown() {
|
|
132
|
+
this.$emit('shown');
|
|
133
|
+
this.setFocus();
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Sets the focus on the datepicker element.
|
|
138
|
+
* If there is a selected date, it focuses on that date.
|
|
139
|
+
* If there is no selected date, it focuses on today's date.
|
|
140
|
+
*/
|
|
141
|
+
setFocus() {
|
|
142
|
+
const elDatepicker = document.querySelector('.vdp-datepicker');
|
|
143
|
+
|
|
144
|
+
if (!elDatepicker) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const elSelected = elDatepicker.querySelector('.selected');
|
|
149
|
+
|
|
150
|
+
if (elSelected) {
|
|
151
|
+
elSelected.focus();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const elToday = elDatepicker.querySelector('.today');
|
|
156
|
+
|
|
157
|
+
if (elToday) {
|
|
158
|
+
elToday.focus();
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
onInput() {
|
|
95
163
|
this.$refs.trigger.cancel();
|
|
96
164
|
},
|
|
97
165
|
},
|
|
98
166
|
};
|
|
99
167
|
</script>
|
|
168
|
+
|
|
169
|
+
<style>
|
|
170
|
+
.rtl {
|
|
171
|
+
direction: rtl;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.vdp-datepicker {
|
|
175
|
+
font-size: 14px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.vdp-datepicker__calendar .today {
|
|
179
|
+
background-color: #f2f2f2;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.vdp-datepicker__calendar {
|
|
183
|
+
& button {
|
|
184
|
+
background: inherit;
|
|
185
|
+
text-align: center;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
& button:disabled {
|
|
189
|
+
color: #ddd;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* header */
|
|
194
|
+
.vdp-datepicker__calendar header {
|
|
195
|
+
display: flex;
|
|
196
|
+
|
|
197
|
+
& button {
|
|
198
|
+
border: none;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
& button:hover:not(:disabled) {
|
|
202
|
+
background: #f2f2f2;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
& button.vdp-datepicker__up {
|
|
206
|
+
color: inherit;
|
|
207
|
+
font-weight: 500;
|
|
208
|
+
flex-grow: 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
& .next,
|
|
212
|
+
& .prev {
|
|
213
|
+
border-radius: 50%;
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
justify-content: center;
|
|
217
|
+
flex-shrink: 0;
|
|
218
|
+
height: 2rem;
|
|
219
|
+
aspect-ratio: 1 / 1;
|
|
220
|
+
position: relative;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
& .next .default,
|
|
224
|
+
& .prev .default {
|
|
225
|
+
display: flex;
|
|
226
|
+
text-indent: -10000px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
& .next .default:after,
|
|
230
|
+
& .prev .default:after {
|
|
231
|
+
border: 6px solid transparent;
|
|
232
|
+
content: '';
|
|
233
|
+
left: 50%;
|
|
234
|
+
position: absolute;
|
|
235
|
+
top: 50%;
|
|
236
|
+
transform: translateX(-50%) translateY(-50%);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
& .next.rtl,
|
|
240
|
+
& .prev.rtl {
|
|
241
|
+
transform: rotate(180deg);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
& .prev .default:after {
|
|
245
|
+
border-right: 10px solid #000;
|
|
246
|
+
margin-left: -5px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
& .prev:disabled .default:after {
|
|
250
|
+
border-right: 10px solid #ddd;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
& .next .default:after {
|
|
254
|
+
border-left: 10px solid #000;
|
|
255
|
+
margin-left: 5px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
& .next:disabled .default:after {
|
|
259
|
+
border-left: 10px solid #ddd;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* body (days, months and years) */
|
|
264
|
+
.vdp-datepicker__calendar .picker-cells {
|
|
265
|
+
display: grid;
|
|
266
|
+
list-style: none;
|
|
267
|
+
margin: 0;
|
|
268
|
+
padding: 0;
|
|
269
|
+
|
|
270
|
+
&:has(.cell.day) {
|
|
271
|
+
grid-template-columns: repeat(7, 1fr);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
&:has(.cell.month),
|
|
275
|
+
&:has(.cell.year) {
|
|
276
|
+
min-height: calc(6 * 2rem);
|
|
277
|
+
grid-template-columns: repeat(3, 1fr);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
& .cell {
|
|
281
|
+
border: 1px solid transparent;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
& .cell:not(.selected):focus {
|
|
285
|
+
border-color: #cccccc;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
& .cell.day {
|
|
289
|
+
border-radius: 50%;
|
|
290
|
+
/* height: 2rem; */
|
|
291
|
+
aspect-ratio: 1 / 1;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* cell states */
|
|
296
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day,
|
|
297
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month,
|
|
298
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year {
|
|
299
|
+
cursor: pointer;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day:hover,
|
|
303
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month:hover,
|
|
304
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year:hover {
|
|
305
|
+
border-color: #333;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day:focus,
|
|
309
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month:focus,
|
|
310
|
+
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year:focus {
|
|
311
|
+
z-index: 1;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.vdp-datepicker__calendar .cell.muted {
|
|
315
|
+
color: #ccc;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.vdp-datepicker__calendar .cell.selected {
|
|
319
|
+
background: #333;
|
|
320
|
+
color: #fff;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.vdp-datepicker__calendar .cell.selected.highlighted,
|
|
324
|
+
.vdp-datepicker__calendar .cell.selected:hover {
|
|
325
|
+
background: #333;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.vdp-datepicker__calendar .cell.highlighted {
|
|
329
|
+
background: #cae5ed;
|
|
330
|
+
color: #104756;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.vdp-datepicker__calendar .cell.highlighted.disabled {
|
|
334
|
+
color: #accad2;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.vdp-datepicker__calendar .cell.muted.disabled:not(.selected) {
|
|
338
|
+
color: #ddd;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.vdp-datepicker__calendar .cell.muted.disabled:not(.selected).highlighted {
|
|
342
|
+
color: #accad2;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.vdp-datepicker__calendar .day-header {
|
|
346
|
+
display: grid;
|
|
347
|
+
grid-template-columns: repeat(7, 1fr);
|
|
348
|
+
|
|
349
|
+
& span {
|
|
350
|
+
display: flex;
|
|
351
|
+
align-items: center;
|
|
352
|
+
justify-content: center;
|
|
353
|
+
aspect-ratio: 1 / 1;
|
|
354
|
+
height: 2rem;
|
|
355
|
+
color: #767676;
|
|
356
|
+
font-size: 14px;
|
|
357
|
+
white-space: nowrap;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* .vdp-datepicker__calendar .picker-view {
|
|
362
|
+
width: inherit;
|
|
363
|
+
} */
|
|
364
|
+
|
|
365
|
+
.vdp-datepicker__calendar .picker-view .cells-wrapper {
|
|
366
|
+
overflow: hidden;
|
|
367
|
+
position: relative;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.vdp-datepicker__calendar .picker-view .cells-wrapper .picker-cells {
|
|
371
|
+
transition: all 0.25s ease-in-out;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.vdp-datepicker__calendar .picker-view .slide-right-enter-active {
|
|
375
|
+
top: 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.vdp-datepicker__calendar .picker-view .slide-right-leave-active {
|
|
379
|
+
position: absolute;
|
|
380
|
+
top: 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.vdp-datepicker__calendar .picker-view .slide-right-enter {
|
|
384
|
+
transform: translate(100%);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.vdp-datepicker__calendar .picker-view .slide-right-leave-to {
|
|
388
|
+
transform: translate(-100%);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.vdp-datepicker__calendar .picker-view .slide-left-enter-active {
|
|
392
|
+
top: 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.vdp-datepicker__calendar .picker-view .slide-left-leave-active {
|
|
396
|
+
position: absolute;
|
|
397
|
+
top: 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.vdp-datepicker__calendar .picker-view .slide-left-enter {
|
|
401
|
+
transform: translate(-100%);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.vdp-datepicker__calendar .picker-view .slide-left-leave-to {
|
|
405
|
+
transform: translate(100%);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.view-leave-active {
|
|
409
|
+
position: absolute;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.vdp-datepicker__calendar-button,
|
|
413
|
+
.vdp-datepicker__clear-button {
|
|
414
|
+
border: none;
|
|
415
|
+
font-style: normal;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.vdp-datepicker__calendar-button.input-group-append,
|
|
419
|
+
.vdp-datepicker__calendar-button.input-group-prepend,
|
|
420
|
+
.vdp-datepicker__clear-button.input-group-append,
|
|
421
|
+
.vdp-datepicker__clear-button.input-group-prepend {
|
|
422
|
+
padding: 0;
|
|
423
|
+
}
|
|
424
|
+
</style>
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
v-bind="$attrs"
|
|
8
8
|
v-on="listeners"
|
|
9
9
|
/>
|
|
10
|
-
<div :class="
|
|
11
|
-
<component :is="icon" class="h-5 w-5"
|
|
10
|
+
<div :class="classes">
|
|
11
|
+
<component v-if="icon" :is="icon" class="h-5 w-5"> </component>
|
|
12
|
+
<slot v-else />
|
|
12
13
|
</div>
|
|
13
14
|
</div>
|
|
14
15
|
</template>
|
|
@@ -25,12 +26,39 @@ export default {
|
|
|
25
26
|
icon: {
|
|
26
27
|
type: String,
|
|
27
28
|
default: null,
|
|
28
|
-
required: true,
|
|
29
29
|
},
|
|
30
30
|
iconPlacement: {
|
|
31
31
|
type: String,
|
|
32
32
|
default: 'left',
|
|
33
33
|
},
|
|
34
34
|
},
|
|
35
|
+
|
|
36
|
+
computed: {
|
|
37
|
+
classes() {
|
|
38
|
+
const list = this.classComputedWrapperIcon;
|
|
39
|
+
if (!this.icon) {
|
|
40
|
+
list.push('text-gray-700');
|
|
41
|
+
}
|
|
42
|
+
return list;
|
|
43
|
+
},
|
|
44
|
+
},
|
|
35
45
|
};
|
|
36
46
|
</script>
|
|
47
|
+
|
|
48
|
+
<style scoped>
|
|
49
|
+
/* input[type='date']::-webkit-inner-spin-button,
|
|
50
|
+
input[type='date']::-webkit-calendar-picker-indicator {
|
|
51
|
+
position: absolute;
|
|
52
|
+
opacity: 0;
|
|
53
|
+
} */
|
|
54
|
+
|
|
55
|
+
input[type='number'] {
|
|
56
|
+
appearance: textfield;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
input[type='number']::-webkit-inner-spin-button,
|
|
60
|
+
input[type='number']::-webkit-outer-spin-button {
|
|
61
|
+
appearance: none;
|
|
62
|
+
-webkit-appearance: none;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<input
|
|
3
|
+
v-model="valueFormatted"
|
|
4
|
+
:class="classComputed"
|
|
5
|
+
:disabled="disabled"
|
|
6
|
+
@input="onInput"
|
|
7
|
+
@change="onChange"
|
|
8
|
+
@blur="onBlur"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
import {
|
|
14
|
+
formControlMixin,
|
|
15
|
+
formControlStyleMixin,
|
|
16
|
+
} from '../../../mixins/form-control';
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
name: 'VTInputNumber',
|
|
20
|
+
|
|
21
|
+
mixins: [formControlMixin, formControlStyleMixin],
|
|
22
|
+
|
|
23
|
+
model: {
|
|
24
|
+
prop: 'value',
|
|
25
|
+
event: 'input',
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
props: {
|
|
29
|
+
value: {
|
|
30
|
+
type: [String, Number, Object, Array, Date],
|
|
31
|
+
default: null,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
computed: {
|
|
36
|
+
valueComputed: {
|
|
37
|
+
get() {
|
|
38
|
+
if (this.value === null || this.value === undefined) {
|
|
39
|
+
return '';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return this.value;
|
|
43
|
+
},
|
|
44
|
+
set(value) {
|
|
45
|
+
this.$emit('input', this.unformatNumber(value));
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
valueFormatted() {
|
|
50
|
+
return Number(this.value).toLocaleString();
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
methods: {
|
|
55
|
+
/**
|
|
56
|
+
* Unformats a formatted number string by removing commas and converting it to an integer.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} formattedNumber - The formatted number string.
|
|
59
|
+
* @returns {number|null} - Returns the unformatted number as an integer, or null if the input is invalid.
|
|
60
|
+
*/
|
|
61
|
+
unformatNumber(number) {
|
|
62
|
+
if (!this.isValidNumber(number)) {
|
|
63
|
+
return null; // or any other appropriate value or behavior
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return parseInt(number.replace(/,/g, ''), 10);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Checks if the input is a valid number.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} input - The input string to check.
|
|
73
|
+
* @returns {boolean} - Returns true if the input is a valid number, otherwise false.
|
|
74
|
+
*/
|
|
75
|
+
isValidNumber(input) {
|
|
76
|
+
return (
|
|
77
|
+
input !== null &&
|
|
78
|
+
input !== undefined &&
|
|
79
|
+
input !== '' &&
|
|
80
|
+
!isNaN(Number(input.replace(/,/g, '')))
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
onChange(e) {
|
|
85
|
+
this.$emit('change', this.unformatNumber(e.target.value));
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
onInput(e) {
|
|
89
|
+
this.valueComputed = e.target.value;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
onBlur() {
|
|
93
|
+
this.$emit('blur');
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
</script>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
:class="[
|
|
4
4
|
headless
|
|
5
5
|
? 'progress-bar'
|
|
6
|
-
: 'relative min-h-[12px] w-full overflow-hidden rounded bg-gray-200',
|
|
6
|
+
: 'relative min-h-[12px] w-full flex items-stretch overflow-hidden rounded bg-gray-200',
|
|
7
7
|
]"
|
|
8
8
|
role="progressbar"
|
|
9
9
|
aria-valuemin="0"
|
|
@@ -11,14 +11,13 @@
|
|
|
11
11
|
:aria-valuenow="value"
|
|
12
12
|
:aria-label="label"
|
|
13
13
|
>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
headless
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
></div>
|
|
14
|
+
<slot>
|
|
15
|
+
<VTProgressBarIndicator
|
|
16
|
+
:headless="headless"
|
|
17
|
+
:percentage="percentageComputed"
|
|
18
|
+
:variant="variant"
|
|
19
|
+
/>
|
|
20
|
+
</slot>
|
|
22
21
|
</div>
|
|
23
22
|
</template>
|
|
24
23
|
|
|
@@ -49,6 +48,10 @@ export default {
|
|
|
49
48
|
type: String,
|
|
50
49
|
default: 'rounded-full',
|
|
51
50
|
},
|
|
51
|
+
variant: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: 'default', // default, alert, warning
|
|
54
|
+
},
|
|
52
55
|
},
|
|
53
56
|
|
|
54
57
|
computed: {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="[
|
|
4
|
+
headless
|
|
5
|
+
? 'progress-bar__indicator'
|
|
6
|
+
: `${backgroundComputed} min-h-min transition-all duration-500`,
|
|
7
|
+
]"
|
|
8
|
+
:style="{ width: `${percentage}%` }"
|
|
9
|
+
></div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
export default {
|
|
14
|
+
name: 'VTProgressBarIndicator',
|
|
15
|
+
|
|
16
|
+
props: {
|
|
17
|
+
headless: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: false,
|
|
20
|
+
},
|
|
21
|
+
percentage: {
|
|
22
|
+
type: [String, Number],
|
|
23
|
+
default: null,
|
|
24
|
+
},
|
|
25
|
+
variant: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: 'default', // default, alert, warning
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
computed: {
|
|
32
|
+
backgroundComputed() {
|
|
33
|
+
if (this.variant === 'default') {
|
|
34
|
+
return 'bg-secondary-300';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (this.variant === 'error') {
|
|
38
|
+
return 'bg-error-500';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this.variant === 'warning') {
|
|
42
|
+
return 'bg-warning-500';
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
</script>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component :is="tag" :class="`scroll-shadows-${orientation}`">
|
|
3
|
+
<slot></slot>
|
|
4
|
+
</component>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
export default {
|
|
9
|
+
name: 'VTScrollShadows',
|
|
10
|
+
|
|
11
|
+
props: {
|
|
12
|
+
tag: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: 'div',
|
|
15
|
+
},
|
|
16
|
+
orientation: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: 'vertical', // horizontal
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<style scoped>
|
|
25
|
+
.scroll-shadows-vertical {
|
|
26
|
+
background:
|
|
27
|
+
/* Shadow Cover TOP */
|
|
28
|
+
linear-gradient(white 30%, rgba(255, 255, 255, 0)) center top,
|
|
29
|
+
/* Shadow Cover BOTTOM */ linear-gradient(rgba(255, 255, 255, 0), white 70%)
|
|
30
|
+
center bottom,
|
|
31
|
+
/* Shadow TOP */
|
|
32
|
+
radial-gradient(
|
|
33
|
+
farthest-side at 50% 0,
|
|
34
|
+
rgba(0, 0, 0, 0.1),
|
|
35
|
+
rgba(0, 0, 0, 0)
|
|
36
|
+
)
|
|
37
|
+
center top,
|
|
38
|
+
/* Shadow BOTTOM */
|
|
39
|
+
radial-gradient(
|
|
40
|
+
farthest-side at 50% 100%,
|
|
41
|
+
rgba(0, 0, 0, 0.1),
|
|
42
|
+
rgba(0, 0, 0, 0)
|
|
43
|
+
)
|
|
44
|
+
center bottom;
|
|
45
|
+
|
|
46
|
+
background-repeat: no-repeat;
|
|
47
|
+
background-size:
|
|
48
|
+
100% 30px,
|
|
49
|
+
100% 30px,
|
|
50
|
+
100% 8px,
|
|
51
|
+
100% 8px;
|
|
52
|
+
background-attachment: local, local, scroll, scroll;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.scroll-shadows-horizontal {
|
|
56
|
+
background:
|
|
57
|
+
linear-gradient(90deg, white 30%, rgba(255, 255, 255, 0)),
|
|
58
|
+
linear-gradient(90deg, rgba(255, 255, 255, 0), white 70%) 0 100%,
|
|
59
|
+
radial-gradient(farthest-side at 0% 50%, rgba(0, 0, 0, 0.2), white),
|
|
60
|
+
radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.2), white) 0 100%;
|
|
61
|
+
|
|
62
|
+
background-repeat: no-repeat;
|
|
63
|
+
background-color: white;
|
|
64
|
+
background-position:
|
|
65
|
+
top left,
|
|
66
|
+
top right,
|
|
67
|
+
top left,
|
|
68
|
+
top right;
|
|
69
|
+
background-size:
|
|
70
|
+
10px 100%,
|
|
71
|
+
10px 100%,
|
|
72
|
+
5px 100%,
|
|
73
|
+
5px 100%;
|
|
74
|
+
background-attachment: local, local, scroll, scroll;
|
|
75
|
+
}
|
|
76
|
+
</style>
|
|
@@ -31,8 +31,34 @@ export default {
|
|
|
31
31
|
default: false,
|
|
32
32
|
},
|
|
33
33
|
/**
|
|
34
|
-
* The
|
|
35
|
-
*
|
|
34
|
+
* The position of the tooltip from the top of the trigger. This overrides the placement prop.
|
|
35
|
+
*
|
|
36
|
+
* @type {Object}
|
|
37
|
+
* @property {string} unit - The unit of the top position (% or px).
|
|
38
|
+
* @property {string} positionBy - Whether to position the top, center, or bottom of the tooltip at it's top value. Defaults to bottom.
|
|
39
|
+
* @property {number} value - The value of the top position.
|
|
40
|
+
* @default null
|
|
41
|
+
*/
|
|
42
|
+
topPosition: {
|
|
43
|
+
type: Object,
|
|
44
|
+
default: null,
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* The position of the tooltip from the start (left) of the trigger. This overrides the placement prop.
|
|
48
|
+
*
|
|
49
|
+
* @type {Object}
|
|
50
|
+
* @property {string} unit - The unit of the top position (& or px).
|
|
51
|
+
* @property {string} positionBy - Whether to position the start, center, or end of the tooltip at it's left value. Defaults to start.
|
|
52
|
+
* @property {number} value - The value of the top position.
|
|
53
|
+
* @default null
|
|
54
|
+
*/
|
|
55
|
+
leftPosition: {
|
|
56
|
+
type: Object,
|
|
57
|
+
default: null,
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* The placement of the component relative to its trigger element. If top or bottom are supplied, this value will be ignored.
|
|
61
|
+
* @type {string|number}
|
|
36
62
|
* @values 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end'
|
|
37
63
|
* @default 'bottom-start'
|
|
38
64
|
*/
|