🌐 AI搜索 & 代理 主页
Skip to content

Commit 417ef8f

Browse files
authored
feat(b-time, b-form-timepicker): new components b-time and b-form-timepicker (#4783)
Co-authored-by: Jacob Müller
1 parent 165f481 commit 417ef8f

38 files changed

+3717
-349
lines changed

docs/components/icons-table.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
key="_bv-icons-table_"
44
class="bv-icons-table notranslate"
55
role="group"
6-
aria-labeledby="bv-icons-table-title"
6+
aria-labelledby="bv-icons-table-title"
77
>
88
<b-row align-v="start">
99
<b-col md="5" lg="6">

docs/markdown/misc/third-party/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ Alternatives to BootstrapVue's [`b-icon-*`](/docs/icons) components:
2626

2727
### Date and Time Pickers
2828

29-
Alternatives to BootstrapVue's [`<b-form-datepicker>`](/docs/components/form-datepicker) and
30-
[`<b-calendar>`](/docs/components/calendar) components:
29+
Alternatives to BootstrapVue's [`<b-form-datepicker>`](/docs/components/form-datepicker),
30+
[`<b-calendar>`](/docs/components/calendar),
31+
[`<b-form-timepicker>`](/docs/components/form-timepicker), and [`<b-time>`](/docs/components/time)
32+
components:
3133

3234
- [Vue AirBnB Style Datepicker](https://mikaeledebro.gitbooks.io/vue-airbnb-style-datepicker/)
3335
- [Vue Datepicker](https://livelybone.github.io/vue/vue-datepicker/) _Note: Not WAI-ARIA compliant_
@@ -56,6 +58,9 @@ Alternatives to BootstrapVue's [`<b-form-datepicker>`](/docs/components/form-dat
5658
- [VeeValidate](https://logaretm.github.io/vee-validate/)
5759
- [Vuelidate](https://github.com/vuelidate/vuelidate/)
5860

61+
For examples of using these validation libraries, refer to our
62+
[Validation reference section](/docs/reference/validation).
63+
5964
## Site generation
6065

6166
- [NuxtJS](https://nuxtjs.org) - Static + SPA + PWA + SSR

src/components/form-datepicker/_form-datepicker.scss renamed to src/_custom-controls.scss

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,60 @@
1-
// Custom CSS for b-form-datepicker
1+
// Special styling for some BootstrapVue custom form controls that do
2+
// not have a native HTML input type root element (or tabindex)
3+
// Used by BFormSpinbutton, BFormDatepicker, BFormTimepicker, BTime, BCalendar
4+
.form-control {
5+
// Adds focus styling to the form-control class (via the focus class)
6+
// Specifically when we are using non focusable elements, or when true focus
7+
// is within the `.form-control` element.
8+
// Mimics the `.form-control:focus` styling
9+
&.focus {
10+
color: $input-focus-color;
11+
background-color: $input-focus-bg;
12+
border-color: $input-focus-border-color;
13+
outline: 0;
14+
@if $enable-shadows {
15+
box-shadow: $input-box-shadow, $input-focus-box-shadow;
16+
} @else {
17+
box-shadow: $input-focus-box-shadow;
18+
}
19+
20+
&.is-valid {
21+
border-color: $form-feedback-valid-color;
22+
box-shadow: 0 0 0 $input-focus-width rgba($form-feedback-valid-color, 0.25);
23+
}
24+
25+
&.is-invalid {
26+
border-color: $form-feedback-invalid-color;
27+
box-shadow: 0 0 0 $input-focus-width rgba($form-feedback-invalid-color, 0.25);
28+
}
29+
}
30+
}
231

3-
.b-form-datepicker.form-control {
32+
// Shared BVFormBtnLabelControl styling
33+
// Currently used by BFormTimepicker and BFormDatepicker
34+
.b-form-btn-label-control {
435
// Remove background validation images from main wrapper
536
background-image: none;
637

38+
@at-root {
39+
// Prevent the button/label from reversing order on in horizontal RTL mode
40+
[dir="rtl"] &,
41+
&[dir="rtl"] {
42+
flex-direction: row-reverse;
43+
44+
> label {
45+
text-align: right;
46+
}
47+
}
48+
}
49+
750
> .btn {
851
line-height: 1;
952
font-size: inherit;
1053
box-shadow: none !important;
1154

12-
> div {
13-
transition: transform 0.15s;
14-
}
15-
1655
&:disabled {
1756
pointer-events: none;
1857
}
19-
20-
&:hover:not(:disabled),
21-
&:focus:not(:disabled) {
22-
> div {
23-
transform: scale(1.125);
24-
}
25-
}
2658
}
2759

2860
&.is-valid > .btn {
@@ -33,31 +65,24 @@
3365
color: $form-feedback-invalid-color;
3466
}
3567

36-
&.focus {
37-
color: $input-focus-color;
38-
background-color: $input-focus-bg;
39-
border-color: $input-focus-border-color;
68+
> label {
69+
// Unfortunately this is not supported by all browsers :(
70+
// text-align: end;
4071
outline: 0;
41-
@if $enable-shadows {
42-
box-shadow: $input-box-shadow, $input-focus-box-shadow;
43-
} @else {
44-
box-shadow: $input-focus-box-shadow;
45-
}
46-
47-
&.is-valid {
48-
border-color: $form-feedback-valid-color;
49-
box-shadow: 0 0 0 $input-focus-width rgba($form-feedback-valid-color, 0.25);
72+
@if $enable-pointer-cursor-for-buttons {
73+
cursor: pointer;
5074
}
75+
// Set a minimum height, as we have height set to auto
76+
// (to allow the content to wrap if needed)
77+
// We subtract off the border, as we have border set to 0
78+
min-height: calc(#{$input-height} - #{$input-height-border});
5179

52-
&.is-invalid {
53-
border-color: $form-feedback-invalid-color;
54-
box-shadow: 0 0 0 $input-focus-width rgba($form-feedback-invalid-color, 0.25);
80+
&.form-control-sm {
81+
min-height: calc(#{$input-height-sm} - #{$input-height-border});
5582
}
56-
}
5783

58-
> label {
59-
@if $enable-pointer-cursor-for-buttons {
60-
cursor: pointer;
84+
&.form-control-lg {
85+
min-height: calc(#{$input-height-lg} - #{$input-height-border});
6186
}
6287
}
6388

src/components/calendar/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,3 +632,5 @@ verbosity and to provide consistency across various screen readers (NVDA, when e
632632
## See also
633633

634634
- [`<b-form-datepicker>` Date picker custom form input](/docs/components/form-datepicker)
635+
- [`<b-form-timepicker>` Time picker custom form input](/docs/comonents/form-timepicker)
636+
- [`<b-time>` Time date selection widget](/docs/components/calendar)

src/components/calendar/_calendar.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
opacity: 1;
1212
}
1313

14+
.form-control[role="application"] {
15+
// Easy rounded corners on contained elements,
16+
// specifically the footer of the calendar grid
17+
overflow: hidden;
18+
}
19+
1420
.b-calendar-grid-body {
1521
.col[data-date] {
1622
// We hard code the sizes in `px` to fit

src/components/calendar/calendar.js

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
} from '../../utils/date'
2121
import { requestAF } from '../../utils/dom'
2222
import { isArray, isFunction, isPlainObject, isString } from '../../utils/inspect'
23+
import { isLocaleRTL } from '../../utils/locale'
2324
import { toInteger } from '../../utils/number'
2425
import { toString } from '../../utils/string'
2526
import idMixin from '../../mixins/id'
@@ -34,37 +35,6 @@ const NAME = 'BCalendar'
3435
// Key Codes
3536
const { UP, DOWN, LEFT, RIGHT, PAGEUP, PAGEDOWN, HOME, END, ENTER, SPACE } = KeyCodes
3637

37-
// Languages that are RTL
38-
const RTL_LANGS = [
39-
'ar',
40-
'az',
41-
'ckb',
42-
'fa',
43-
'he',
44-
'ks',
45-
'lrc',
46-
'mzn',
47-
'ps',
48-
'sd',
49-
'te',
50-
'ug',
51-
'ur',
52-
'yi'
53-
].map(locale => locale.toLowerCase())
54-
55-
// --- Helper utilities ---
56-
57-
export const isLocaleRTL = locale => {
58-
// Determines if the locale is RTL (only single locale supported)
59-
const parts = toString(locale)
60-
.toLowerCase()
61-
.replace(/-u-.+/, '')
62-
.split('-')
63-
const locale1 = parts.slice(0, 2).join('-')
64-
const locale2 = parts[0]
65-
return arrayIncludes(RTL_LANGS, locale1) || arrayIncludes(RTL_LANGS, locale2)
66-
}
67-
6838
// --- BCalendar component ---
6939

7040
// @vue/component
@@ -667,7 +637,6 @@ export const BCalendar = Vue.extend({
667637
},
668638
onClickDay(day) {
669639
// Clicking on a date "button" to select it
670-
// TODO: Change to lookup the `data-data` attribute
671640
const selectedDate = this.selectedDate
672641
const activeDate = this.activeDate
673642
const clickedDate = parseYMD(day.ymd)
@@ -702,6 +671,12 @@ export const BCalendar = Vue.extend({
702671
},
703672
gotoNextYear() {
704673
this.activeYMD = formatYMD(this.constrainDate(oneYearAhead(this.activeDate)))
674+
},
675+
onHeaderClick() {
676+
if (!this.disabled) {
677+
this.activeYMD = this.selectedYMD || formatYMD(this.getToday())
678+
this.focus()
679+
}
705680
}
706681
},
707682
render(h) {
@@ -719,8 +694,9 @@ export const BCalendar = Vue.extend({
719694
// Flag for making the `aria-live` regions live
720695
const isLive = this.isLive
721696
// Pre-compute some IDs
722-
const idWidget = safeId()
723-
const idValue = safeId('_calendar-value_')
697+
// Thes should be computed props
698+
const idValue = safeId()
699+
const idWidget = safeId('_calendar-wrapper_')
724700
const idNav = safeId('_calendar-nav_')
725701
const idGrid = safeId('_calendar-grid_')
726702
const idGridCaption = safeId('_calendar-grid-caption_')
@@ -737,13 +713,20 @@ export const BCalendar = Vue.extend({
737713
id: idValue,
738714
for: idGrid,
739715
role: 'status',
716+
tabindex: this.disabled ? null : '-1',
740717
// Mainly for testing purposes, as we do not know
741718
// the exact format `Intl` will format the date string
742719
'data-selected': toString(selectedYMD),
743720
// We wait until after mount to enable `aria-live`
744721
// to prevent initial announcement on page render
745722
'aria-live': isLive ? 'polite' : 'off',
746723
'aria-atomic': isLive ? 'true' : null
724+
},
725+
on: {
726+
// Transfer focus/click to focus grid
727+
// and focus active date (or today if no selection)
728+
click: this.onHeaderClick,
729+
focus: this.onHeaderClick
747730
}
748731
},
749732
this.selectedDate

src/components/calendar/calendar.spec.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,48 @@ describe('calendar', () => {
212212
wrapper.destroy()
213213
})
214214

215+
it('clicking output header focuses grid', async () => {
216+
const wrapper = mount(BCalendar, {
217+
attachToDocument: true,
218+
propsData: {
219+
value: '2020-02-15' // Leap year
220+
}
221+
})
222+
223+
expect(wrapper.isVueInstance()).toBe(true)
224+
await waitNT(wrapper.vm)
225+
await waitRAF()
226+
227+
const $grid = wrapper.find('[role="application"]')
228+
expect($grid.exists()).toBe(true)
229+
expect($grid.is('div')).toBe(true)
230+
231+
expect(document.activeElement).not.toBe($grid.element)
232+
233+
const $output = wrapper.find('header > output')
234+
expect($output.exists()).toBe(true)
235+
236+
$output.trigger('click')
237+
await waitNT(wrapper.vm)
238+
await waitRAF()
239+
240+
expect(document.activeElement).toBe($grid.element)
241+
242+
wrapper.vm.blur()
243+
await waitNT(wrapper.vm)
244+
await waitRAF()
245+
246+
expect(document.activeElement).not.toBe($grid.element)
247+
248+
$output.trigger('focus')
249+
await waitNT(wrapper.vm)
250+
await waitRAF()
251+
252+
expect(document.activeElement).toBe($grid.element)
253+
254+
wrapper.destroy()
255+
})
256+
215257
it('keyboard navigation works', async () => {
216258
const wrapper = mount(BCalendar, {
217259
attachToDocument: true,

src/components/form-datepicker/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,5 +486,7 @@ BootstrapVue's Custom SCSS/CSS is also required for proper styling of the date p
486486

487487
## See also
488488

489+
- [`<b-form-timepicker>` Time picker custom form input](/docs/components/form-timepicker)
489490
- [`<b-calendar>` Calendar date selection widget](/docs/components/calendar)
491+
- [`<b-time>` Time selection widget](/docs/components/time)
490492
- [`<b-dropdown>` Dropdown component](/docs/components/dropdown)

0 commit comments

Comments
 (0)