@ditojs/ui 3.2.0 → 3.3.0
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/dist/dito-ui.css +1 -1
- package/dist/dito-ui.es.js +1350 -3294
- package/dist/dito-ui.umd.js +1 -7
- package/package.json +15 -9
- package/src/components/DitoButton.vue +91 -16
- package/src/components/DitoCalendar.vue +318 -248
- package/src/components/DitoDatePicker.vue +36 -147
- package/src/components/DitoDateTimePicker.vue +75 -181
- package/src/components/DitoInput.vue +5 -3
- package/src/components/DitoMenuButton.vue +226 -0
- package/src/components/DitoPagination.vue +9 -3
- package/src/components/DitoPickerInput.vue +285 -0
- package/src/components/DitoSwitch.vue +4 -6
- package/src/components/DitoTimePanel.vue +264 -0
- package/src/components/DitoTimePicker.vue +47 -454
- package/src/components/DitoTrigger.vue +293 -97
- package/src/components/index.js +1 -0
- package/src/styles/components/_button.scss +7 -9
- package/src/styles/components/_resize.scss +8 -0
- package/src/styles/mixins/_input.scss +6 -0
- package/src/utils/button.js +24 -0
- package/src/utils/date.js +18 -0
- package/src/utils/event.js +18 -9
- package/src/utils/index.js +1 -0
- package/src/utils/time.js +107 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
<template lang="pug">
|
|
2
|
+
.dito-time-picker-popup(
|
|
3
|
+
role="dialog"
|
|
4
|
+
aria-label="Choose time"
|
|
5
|
+
)
|
|
6
|
+
.dito-time-picker-panel(
|
|
7
|
+
v-for="column in columns"
|
|
8
|
+
:key="column.name"
|
|
9
|
+
)
|
|
10
|
+
ul(
|
|
11
|
+
:ref="element => setListElement(column.name, element)"
|
|
12
|
+
:class="`dito-time-picker-${column.name}`"
|
|
13
|
+
role="listbox"
|
|
14
|
+
:aria-label="column.label"
|
|
15
|
+
)
|
|
16
|
+
li(
|
|
17
|
+
v-for="value in column.values"
|
|
18
|
+
:key="value"
|
|
19
|
+
:ref="element => setOptionElement(column.name, value, element)"
|
|
20
|
+
role="option"
|
|
21
|
+
:aria-label="leftPad(value)"
|
|
22
|
+
:aria-selected="value === column.value"
|
|
23
|
+
:class="{ selected: value === column.value }"
|
|
24
|
+
@click="setTime({ [column.name]: value })"
|
|
25
|
+
) {{ leftPad(value) }}
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script>
|
|
29
|
+
import { alterDate } from '../utils/date.js'
|
|
30
|
+
import { getKeyNavigation } from '../utils/event.js'
|
|
31
|
+
import {
|
|
32
|
+
getEnabledValues,
|
|
33
|
+
getEnabledValue,
|
|
34
|
+
stepEnabledValue
|
|
35
|
+
} from '../utils/time.js'
|
|
36
|
+
|
|
37
|
+
// The panel of `DitoTimePicker` and `DitoDateTimePicker`, with a list of the
|
|
38
|
+
// enabled values for each of the hour, minute and second. The picker's input
|
|
39
|
+
// navigates it through `handleKey()`.
|
|
40
|
+
export default {
|
|
41
|
+
emits: ['update:modelValue', 'select'],
|
|
42
|
+
|
|
43
|
+
props: {
|
|
44
|
+
modelValue: { type: Date, default: null },
|
|
45
|
+
disabledHour: { type: Function, default: () => false },
|
|
46
|
+
disabledMinute: { type: Function, default: () => false },
|
|
47
|
+
disabledSecond: { type: Function, default: () => false }
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
computed: {
|
|
51
|
+
currentDate() {
|
|
52
|
+
return (
|
|
53
|
+
this.modelValue ||
|
|
54
|
+
// Create a new Date() object with the time set to 0, to be used when
|
|
55
|
+
// first setting any of the times, for meaningful dates in case the
|
|
56
|
+
// object is shared with a calendar, e.g. in DateTimePicker.
|
|
57
|
+
alterDate(new Date(), { hour: 0, minute: 0, second: 0, millisecond: 0 })
|
|
58
|
+
)
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// The enabled values of each column, apart from the current date, so they
|
|
62
|
+
// aren't collected again with each change of the time.
|
|
63
|
+
enabledValues() {
|
|
64
|
+
return {
|
|
65
|
+
hour: getEnabledValues(24, this.disabledHour),
|
|
66
|
+
minute: getEnabledValues(60, this.disabledMinute),
|
|
67
|
+
second: getEnabledValues(60, this.disabledSecond)
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
columns() {
|
|
72
|
+
const { currentDate, enabledValues } = this
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
name: 'hour',
|
|
76
|
+
label: 'Hour',
|
|
77
|
+
value: currentDate.getHours(),
|
|
78
|
+
values: enabledValues.hour
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'minute',
|
|
82
|
+
label: 'Minute',
|
|
83
|
+
value: currentDate.getMinutes(),
|
|
84
|
+
values: enabledValues.minute
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'second',
|
|
88
|
+
label: 'Second',
|
|
89
|
+
value: currentDate.getSeconds(),
|
|
90
|
+
values: enabledValues.second
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
watch: {
|
|
97
|
+
modelValue(to, from) {
|
|
98
|
+
if (+to !== +from) {
|
|
99
|
+
this.scrollToSelectedOptions({ smooth: true })
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
created() {
|
|
105
|
+
// The rendered lists and their options, by column name and value.
|
|
106
|
+
this.listElements = {}
|
|
107
|
+
this.optionElements = { hour: {}, minute: {}, second: {} }
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
mounted() {
|
|
111
|
+
this.scrollToSelectedOptions({ smooth: false })
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
methods: {
|
|
115
|
+
leftPad(value) {
|
|
116
|
+
return ('0' + value).slice(-2)
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
setListElement(name, element) {
|
|
120
|
+
this.listElements[name] = element
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
setOptionElement(name, value, element) {
|
|
124
|
+
if (element) {
|
|
125
|
+
this.optionElements[name][value] = element
|
|
126
|
+
} else {
|
|
127
|
+
delete this.optionElements[name][value]
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// All changes of the time go through here, so disabled times are never
|
|
132
|
+
// set: The parts that aren't overridden move on to their next enabled
|
|
133
|
+
// value, so that any part can be picked first.
|
|
134
|
+
setTime(overrides) {
|
|
135
|
+
const time = { millisecond: 0 }
|
|
136
|
+
for (const { name, value, values } of this.columns) {
|
|
137
|
+
time[name] = getEnabledValue(values, overrides[name] ?? value)
|
|
138
|
+
if (time[name] === null) return
|
|
139
|
+
}
|
|
140
|
+
this.$emit('update:modelValue', alterDate(this.currentDate, time))
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// Handles a keydown event of the picker's input, with the caret inside the
|
|
144
|
+
// date part `part`, and returns whether it was handled.
|
|
145
|
+
handleKey(event, part) {
|
|
146
|
+
const { ver: step, enter } = getKeyNavigation(event)
|
|
147
|
+
if (enter) {
|
|
148
|
+
this.$emit('select')
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
const column = (
|
|
152
|
+
step &&
|
|
153
|
+
this.columns.find(({ name }) => name === part?.name)
|
|
154
|
+
)
|
|
155
|
+
if (column) {
|
|
156
|
+
const value = stepEnabledValue(column.values, column.value, step)
|
|
157
|
+
if (value !== null) {
|
|
158
|
+
this.setTime({ [column.name]: value })
|
|
159
|
+
}
|
|
160
|
+
return true
|
|
161
|
+
}
|
|
162
|
+
return false
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
scrollToSelectedOptions({ smooth }) {
|
|
166
|
+
this.$nextTick(() => {
|
|
167
|
+
for (const { name, value } of this.columns) {
|
|
168
|
+
const list = this.listElements[name]
|
|
169
|
+
const option = this.optionElements[name][value]
|
|
170
|
+
if (list && option) {
|
|
171
|
+
// Center the selected option in the list.
|
|
172
|
+
const top = Math.round(
|
|
173
|
+
option.offsetTop -
|
|
174
|
+
(list.clientHeight - option.offsetHeight) / 2
|
|
175
|
+
)
|
|
176
|
+
const distance = Math.abs(list.scrollTop - top)
|
|
177
|
+
list.scrollTo({
|
|
178
|
+
top,
|
|
179
|
+
behavior: smooth && distance < 100 ? 'smooth' : 'auto'
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
</script>
|
|
188
|
+
|
|
189
|
+
<style lang="scss">
|
|
190
|
+
@import '../styles/_imports';
|
|
191
|
+
|
|
192
|
+
$time-picker-line-height: 24px;
|
|
193
|
+
|
|
194
|
+
.dito-time-picker-popup {
|
|
195
|
+
max-width: 160px;
|
|
196
|
+
margin: $popup-margin;
|
|
197
|
+
list-style: none;
|
|
198
|
+
background: $color-white;
|
|
199
|
+
border: $border-style;
|
|
200
|
+
border-radius: $border-radius;
|
|
201
|
+
box-shadow: $shadow-window;
|
|
202
|
+
overflow: hidden;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.dito-time-picker-popup .dito-time-picker-panel {
|
|
206
|
+
float: left;
|
|
207
|
+
border: $border-style;
|
|
208
|
+
border-width: 0 1px 0;
|
|
209
|
+
margin-left: -1px;
|
|
210
|
+
box-sizing: border-box;
|
|
211
|
+
width: calc(100% / 3 + 1px);
|
|
212
|
+
overflow: hidden;
|
|
213
|
+
|
|
214
|
+
&:last-child {
|
|
215
|
+
border-right: 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
ul {
|
|
219
|
+
// The offset parent of the options, see `scrollToSelectedOptions()`.
|
|
220
|
+
position: relative;
|
|
221
|
+
overflow-x: hidden;
|
|
222
|
+
overflow-y: auto;
|
|
223
|
+
list-style: none;
|
|
224
|
+
width: 100%;
|
|
225
|
+
margin: 0;
|
|
226
|
+
// Hide scrollbar:
|
|
227
|
+
box-sizing: content-box;
|
|
228
|
+
padding: 0 17px 0 0;
|
|
229
|
+
height: 7 * $time-picker-line-height;
|
|
230
|
+
|
|
231
|
+
& > li {
|
|
232
|
+
box-sizing: content-box;
|
|
233
|
+
background: $color-white;
|
|
234
|
+
width: 100%;
|
|
235
|
+
height: $time-picker-line-height;
|
|
236
|
+
line-height: $time-picker-line-height;
|
|
237
|
+
text-align: center;
|
|
238
|
+
font-variant-numeric: tabular-nums;
|
|
239
|
+
cursor: pointer;
|
|
240
|
+
white-space: nowrap;
|
|
241
|
+
overflow: hidden;
|
|
242
|
+
@include user-select(none);
|
|
243
|
+
|
|
244
|
+
&:first-child {
|
|
245
|
+
margin-top: 3 * $time-picker-line-height;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
&:last-child {
|
|
249
|
+
margin-bottom: 3 * $time-picker-line-height;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
&:hover {
|
|
253
|
+
background: $color-highlight;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
&.selected,
|
|
257
|
+
&.selected:hover {
|
|
258
|
+
color: $color-text-inverted;
|
|
259
|
+
background: $color-active;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
</style>
|