From 3a97253f49bac1be10a6f2761641679eda38edb5 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 19:33:44 -0300 Subject: [PATCH 01/31] feat(b-calendar, b-form-datepicker): add optional decade navigation buttons --- src/utils/date.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/utils/date.js b/src/utils/date.js index 64774d66e15..0eb7448a697 100644 --- a/src/utils/date.js +++ b/src/utils/date.js @@ -111,6 +111,26 @@ export const oneYearAhead = date => { return date } +export const oneDecadeAgo = date => { + date = createDate(date) + const month = date.getMonth() + date.setFullYear(date.getFullYear() - 10) + if (date.getMonth() !== month) { + date.setDate(0) + } + return date +} + +export const oneDecadeAhead = date => { + date = createDate(date) + const month = date.getMonth() + date.setFullYear(date.getFullYear() + 10) + if (date.getMonth() !== month) { + date.setDate(0) + } + return date +} + // Helper function to constrain a date between two values // Always returns a `Date` object or `null` if no date passed export const constrainDate = (date, min = null, max = null) => { From 8c00ff96fc21c00d062c240650ecb4de6cad812e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 19:39:46 -0300 Subject: [PATCH 02/31] Update date.spec.js --- src/utils/date.spec.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/utils/date.spec.js b/src/utils/date.spec.js index be014e1c406..5c799de738c 100644 --- a/src/utils/date.spec.js +++ b/src/utils/date.spec.js @@ -8,6 +8,8 @@ import { oneMonthAhead, oneYearAgo, oneYearAhead, + oneDecadeAgo, + oneDecadeAhead, constrainDate } from './date' @@ -96,7 +98,27 @@ describe('utils/date', () => { expect(formatYMD(oneYearAhead(parseYMD('2020-12-31')))).toEqual('2021-12-31') }) - it('costrainDate works', async () => { + it('oneDecadeAgo works', async () => { + // February 2020 was a leap year + expect(formatYMD(oneDecadeAgo(parseYMD('2020-02-29')))).toEqual('2010-02-28') + expect(formatYMD(oneDecadeAgo(parseYMD('2020-02-28')))).toEqual('2010-02-28') + expect(formatYMD(oneDecadeAgo(parseYMD('2020-01-31')))).toEqual('2010-01-31') + expect(formatYMD(oneDecadeAgo(parseYMD('2020-11-01')))).toEqual('2010-11-01') + expect(formatYMD(oneDecadeAgo(parseYMD('2020-11-30')))).toEqual('2010-11-30') + expect(formatYMD(oneDecadeAgo(parseYMD('2020-12-31')))).toEqual('2010-12-31') + }) + + it('oneDecadeAhead works', async () => { + // February 2020 was a leap year + expect(formatYMD(oneDecadeAhead(parseYMD('2020-02-29')))).toEqual('2030-02-28') + expect(formatYMD(oneDecadeAhead(parseYMD('2020-02-28')))).toEqual('2030-02-28') + expect(formatYMD(oneDecadeAhead(parseYMD('2020-01-31')))).toEqual('2030-01-31') + expect(formatYMD(oneDecadeAhead(parseYMD('2020-11-01')))).toEqual('2030-11-01') + expect(formatYMD(oneDecadeAhead(parseYMD('2020-11-30')))).toEqual('2030-11-30') + expect(formatYMD(oneDecadeAhead(parseYMD('2020-12-31')))).toEqual('2030-12-31') + }) + + it('costrainDate works', async () => { const min = parseYMD('2020-01-05') const max = parseYMD('2020-01-15') const date1 = parseYMD('2020-01-10') From a9a7eac033a95447354ee96750938af74169ee04 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 19:45:03 -0300 Subject: [PATCH 03/31] lint --- src/utils/date.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/date.spec.js b/src/utils/date.spec.js index 5c799de738c..0fb1f0d6658 100644 --- a/src/utils/date.spec.js +++ b/src/utils/date.spec.js @@ -108,7 +108,7 @@ describe('utils/date', () => { expect(formatYMD(oneDecadeAgo(parseYMD('2020-12-31')))).toEqual('2010-12-31') }) - it('oneDecadeAhead works', async () => { + it('oneDecadeAhead works', async () => { // February 2020 was a leap year expect(formatYMD(oneDecadeAhead(parseYMD('2020-02-29')))).toEqual('2030-02-28') expect(formatYMD(oneDecadeAhead(parseYMD('2020-02-28')))).toEqual('2030-02-28') @@ -118,7 +118,7 @@ describe('utils/date', () => { expect(formatYMD(oneDecadeAhead(parseYMD('2020-12-31')))).toEqual('2030-12-31') }) - it('costrainDate works', async () => { + it('costrainDate works', async () => { const min = parseYMD('2020-01-05') const max = parseYMD('2020-01-15') const date1 = parseYMD('2020-01-10') From f41ce8b71b1fd4a83fbbae4342a2585356a8a6d2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 19:48:22 -0300 Subject: [PATCH 04/31] Update config-defaults.js --- src/utils/config-defaults.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/config-defaults.js b/src/utils/config-defaults.js index f4423a4a76f..dc062e5b246 100644 --- a/src/utils/config-defaults.js +++ b/src/utils/config-defaults.js @@ -69,11 +69,13 @@ export default deepFreeze({ }, BCalendar: { // BFormDate will choose these first if not provided in BFormDate section + labelPrevDecade: 'Previous decade', labelPrevYear: 'Previous year', labelPrevMonth: 'Previous month', labelCurrentMonth: 'Current month', labelNextMonth: 'Next month', labelNextYear: 'Next year', + labelNextDecade: 'Next decade', labelToday: 'Today', labelSelected: 'Selected date', labelNoDateSelected: 'No date selected', From 5ba9436777f8815c6114719f3fe9b7693222a429 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:04:29 -0300 Subject: [PATCH 05/31] Update calendar.js --- src/components/calendar/calendar.js | 61 +++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index a5a2e5254f3..b1036e9b670 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -16,6 +16,8 @@ import { oneMonthAhead, oneYearAgo, oneYearAhead, + oneDecadeAgo, + oneDecadeAhead, parseYMD, resolveLocale } from '../../utils/date' @@ -26,7 +28,12 @@ import { toInteger } from '../../utils/number' import { toString } from '../../utils/string' import idMixin from '../../mixins/id' import normalizeSlotMixin from '../../mixins/normalize-slot' -import { BIconChevronLeft, BIconChevronDoubleLeft, BIconCircleFill } from '../../icons/icons' +import { + BIconChevronLeft, + BIconChevronDoubleLeft, + BIconChevronBarLeft, + BIconCircleFill +} from '../../icons/icons' // --- Constants --- @@ -141,6 +148,11 @@ export const BCalendar = Vue.extend({ type: Boolean, default: false }, + showDecadeNav: { + // When `true` enabled thedecade navigation buttons + type: Boolean, + default: false + }, hidden: { // When `true`, renders a comment node, but keeps the component instance active // Mainly for , so that we can get the component's value and locale @@ -158,6 +170,10 @@ export const BCalendar = Vue.extend({ // default: null }, // Labels for buttons and keyboard shortcuts + labelPrevDecade: { + type: String, + default: () => getComponentConfig(NAME, 'labelPrevDecade') + }, labelPrevYear: { type: String, default: () => getComponentConfig(NAME, 'labelPrevYear') @@ -178,6 +194,10 @@ export const BCalendar = Vue.extend({ type: String, default: () => getComponentConfig(NAME, 'labelNextYear') }, + labelNextDecade: { + type: String, + default: () => getComponentConfig(NAME, 'labelNextDecade') + }, labelToday: { type: String, default: () => getComponentConfig(NAME, 'labelToday') @@ -397,6 +417,10 @@ export const BCalendar = Vue.extend({ return createDateFormatter(this.calendarLocale, { day: 'numeric', calendar: 'gregory' }) }, // Disabled states for the nav buttons + prevDecadeDisabled() { + const min = this.computedMin + return this.disabled || (min && lastDateOfMonth(oneDecadeAgo(this.activeDate)) < min) + }, prevYearDisabled() { const min = this.computedMin return this.disabled || (min && lastDateOfMonth(oneYearAgo(this.activeDate)) < min) @@ -417,7 +441,11 @@ export const BCalendar = Vue.extend({ const max = this.computedMax return this.disabled || (max && firstDateOfMonth(oneYearAhead(this.activeDate)) > max) }, - // Calendar generation + nextDecadeDisabled() { + const max = this.computedMax + return this.disabled || (max && firstDateOfMonth(oneDecadeAhead(this.activeDate)) > max) + }, + // Calendar dates generation calendar() { const matrix = [] const firstDay = this.calendarFirstDay @@ -670,6 +698,9 @@ export const BCalendar = Vue.extend({ this.focus() } }, + gotoPrevDecade() { + this.activeYMD = formatYMD(this.constrainDate(oneDecadeAgo(this.activeDate))) + }, gotoPrevYear() { this.activeYMD = formatYMD(this.constrainDate(oneYearAgo(this.activeDate))) }, @@ -686,6 +717,9 @@ export const BCalendar = Vue.extend({ gotoNextYear() { this.activeYMD = formatYMD(this.constrainDate(oneYearAhead(this.activeDate))) }, + gotoNextDecade() { + this.activeYMD = formatYMD(this.constrainDate(oneDecadeAhead(this.activeDate))) + }, onHeaderClick() { if (!this.disabled) { this.activeYMD = this.selectedYMD || formatYMD(this.getToday()) @@ -700,6 +734,7 @@ export const BCalendar = Vue.extend({ } const isRTL = this.isRTL + const hideDecadeNav = !this.showDecadeNav const todayYMD = formatYMD(this.getToday()) const selectedYMD = this.selectedYMD const activeYMD = this.activeYMD @@ -762,11 +797,13 @@ export const BCalendar = Vue.extend({ ) // Content for the date navigation buttons + const $prevDecadeIcon = h(BIconChevronBarLeft, { props: { shiftV: 0.5, flipH: isRTL } }) const $prevYearIcon = h(BIconChevronDoubleLeft, { props: { shiftV: 0.5, flipH: isRTL } }) const $prevMonthIcon = h(BIconChevronLeft, { props: { shiftV: 0.5, flipH: isRTL } }) const $thisMonthIcon = h(BIconCircleFill, { props: { shiftV: 0.5 } }) const $nextMonthIcon = h(BIconChevronLeft, { props: { shiftV: 0.5, flipH: !isRTL } }) const $nextYearIcon = h(BIconChevronDoubleLeft, { props: { shiftV: 0.5, flipH: !isRTL } }) + const $nextDecadeIcon = h(BIconChevronBarLeft, { props: { shiftV: 0.5, flipH: !isRTL } }) // Utility to create the date navigation buttons const makeNavBtn = (content, label, handler, btnDisabled, shortcut) => { @@ -802,6 +839,15 @@ export const BCalendar = Vue.extend({ } }, [ + hideDecadeNav + ? h() + : makeNavBtn( + $prevDecadeIcon, + this.labelPrevDecade, + this.gotoPrevDecade, + this.prevDecadeDisabled, + null + ), makeNavBtn( $prevYearIcon, this.labelPrevYear, @@ -836,7 +882,16 @@ export const BCalendar = Vue.extend({ this.gotoNextYear, this.nextYearDisabled, 'Alt+PageUp' - ) + ), + hideDecadeNav + ? h() + : makeNavBtn( + $nextDecadeIcon, + this.labelNextDecade, + this.gotoNextDecade, + this.nextDecadeDisabled, + null + ) ] ) From 3efa17550f234b9fabd4c3b371d7a19d2aa957a6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:07:38 -0300 Subject: [PATCH 06/31] Update config-defaults.js --- src/utils/config-defaults.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/config-defaults.js b/src/utils/config-defaults.js index dc062e5b246..49ed2b8cb1a 100644 --- a/src/utils/config-defaults.js +++ b/src/utils/config-defaults.js @@ -101,11 +101,13 @@ export default deepFreeze({ }, BFormDatepicker: { // BFormDatepicker will choose from BCalendar first if not provided here + labelPrevDecade: undefined, labelPrevYear: undefined, labelPrevMonth: undefined, labelCurrentMonth: undefined, labelNextMonth: undefined, labelNextYear: undefined, + labelNextDecade: undefined, labelToday: undefined, labelSelected: undefined, labelNoDateSelected: undefined, From d4f6c16892e41903b5b070f7c075a2290d0a8ece Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:18:25 -0300 Subject: [PATCH 07/31] Update calendar.spec.js --- src/components/calendar/calendar.spec.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/components/calendar/calendar.spec.js b/src/components/calendar/calendar.spec.js index a462d49a0f8..b1a674ed1fb 100644 --- a/src/components/calendar/calendar.spec.js +++ b/src/components/calendar/calendar.spec.js @@ -126,6 +126,7 @@ describe('calendar', () => { const wrapper = mount(BCalendar, { attachToDocument: true, propsData: { + showDecadeNav: true, value: '2020-02-15' // Leap year } }) @@ -142,25 +143,37 @@ describe('calendar', () => { expect($navBtns.length).toBe(5) // Prev Month - $navBtns.at(1).trigger('click') + $navBtns.at(2).trigger('click') await waitNT(wrapper.vm) await waitRAF() expect($grid.attributes('data-month')).toBe('2020-01') // Next Month - $navBtns.at(3).trigger('click') + $navBtns.at(4).trigger('click') await waitNT(wrapper.vm) await waitRAF() expect($grid.attributes('data-month')).toBe('2020-02') // Prev Year - $navBtns.at(0).trigger('click') + $navBtns.at(1).trigger('click') await waitNT(wrapper.vm) await waitRAF() expect($grid.attributes('data-month')).toBe('2019-02') // Next Year - $navBtns.at(4).trigger('click') + $navBtns.at(5).trigger('click') + await waitNT(wrapper.vm) + await waitRAF() + expect($grid.attributes('data-month')).toBe('2020-02') + + // Prev Decade + $navBtns.at(0).trigger('click') + await waitNT(wrapper.vm) + await waitRAF() + expect($grid.attributes('data-month')).toBe('2010-02') + + // Next Decade + $navBtns.at(6).trigger('click') await waitNT(wrapper.vm) await waitRAF() expect($grid.attributes('data-month')).toBe('2020-02') From 922f032001b4b86ba49546972710be9e4e672dbf Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:20:17 -0300 Subject: [PATCH 08/31] Update calendar.spec.js --- src/components/calendar/calendar.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/calendar/calendar.spec.js b/src/components/calendar/calendar.spec.js index b1a674ed1fb..e7ce877424e 100644 --- a/src/components/calendar/calendar.spec.js +++ b/src/components/calendar/calendar.spec.js @@ -140,7 +140,7 @@ describe('calendar', () => { expect($grid.attributes('data-month')).toBe('2020-02') const $navBtns = wrapper.findAll('.b-calendar-nav button') - expect($navBtns.length).toBe(5) + expect($navBtns.length).toBe(7) // Prev Month $navBtns.at(2).trigger('click') From 217aa6ac7809a1af60ba9b7ddf5f8f07b99a2b34 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:25:56 -0300 Subject: [PATCH 09/31] Update calendar.spec.js --- src/components/calendar/calendar.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/calendar/calendar.spec.js b/src/components/calendar/calendar.spec.js index e7ce877424e..45834f0949f 100644 --- a/src/components/calendar/calendar.spec.js +++ b/src/components/calendar/calendar.spec.js @@ -182,7 +182,7 @@ describe('calendar', () => { // Handle the rare case this test is run right at midnight where // the current month rolled over at midnight when clicked const thisMonth1 = formatYMD(new Date()).slice(0, -3) - $navBtns.at(2).trigger('click') + $navBtns.at(3).trigger('click') await waitNT(wrapper.vm) await waitRAF() const thisMonth2 = formatYMD(new Date()).slice(0, -3) From 6eaa3f3b38fa406356368928e46caac8ee70207e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:42:10 -0300 Subject: [PATCH 10/31] Update form-datepicker.js --- .../form-datepicker/form-datepicker.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/form-datepicker/form-datepicker.js b/src/components/form-datepicker/form-datepicker.js index a4e6d9e7112..44f2ea80089 100644 --- a/src/components/form-datepicker/form-datepicker.js +++ b/src/components/form-datepicker/form-datepicker.js @@ -94,6 +94,11 @@ const propsMixin = { type: Boolean, default: false }, + showDecadeNav: { + // When `true` enables the decade navigation buttons + type: Boolean, + default: false + }, locale: { type: [String, Array] // default: null @@ -175,6 +180,10 @@ const propsMixin = { }, // Labels for buttons and keyboard shortcuts // These pick BCalendar global config if no BFormDate global config + labelPrevDecade: { + type: String, + default: () => getConfigFallback('labelPrevDecade') + }, labelPrevYear: { type: String, default: () => getConfigFallback('labelPrevYear') @@ -195,6 +204,10 @@ const propsMixin = { type: String, default: () => getConfigFallback('labelNextYear') }, + labelNextDecade: { + type: String, + default: () => getConfigFallback('labelNextDecade') + }, labelToday: { type: String, default: () => getConfigFallback('labelToday') @@ -293,11 +306,14 @@ export const BFormDatepicker = /*#__PURE__*/ Vue.extend({ selectedVariant: self.selectedVariant, todayVariant: self.todayVariant, hideHeader: self.hideHeader, + showDecadeNav: self.showDecadeNav, + labelPrevDecade: self.labelPrevDecade, labelPrevYear: self.labelPrevYear, labelPrevMonth: self.labelPrevMonth, labelCurrentMonth: self.labelCurrentMonth, labelNextMonth: self.labelNextMonth, labelNextYear: self.labelNextYear, + labelNextDecade: self.labelNextDecade, labelToday: self.labelToday, labelSelected: self.labelSelected, labelNoDateSelected: self.labelNoDateSelected, From e7ad4276d2d0b626d3794ebff0d7870a306cf002 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:42:50 -0300 Subject: [PATCH 11/31] Update calendar.js --- src/components/calendar/calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index b1036e9b670..8a17b186456 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -149,7 +149,7 @@ export const BCalendar = Vue.extend({ default: false }, showDecadeNav: { - // When `true` enabled thedecade navigation buttons + // When `true` enables the decade navigation buttons type: Boolean, default: false }, From 893deaa01b7743a6953b220b255a617a9525c90f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:56:44 -0300 Subject: [PATCH 12/31] Update package.json --- src/components/calendar/package.json | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/calendar/package.json b/src/components/calendar/package.json index b9ef9d8fd4c..9796ccd18f7 100644 --- a/src/components/calendar/package.json +++ b/src/components/calendar/package.json @@ -87,7 +87,12 @@ }, { "prop": "hideHeader", - "description": "When set, visually hides the selected date header" + "description": "When `true`, visually hides the selected date header" + }, + { + "prop": "showDecadeNav", + "version": "2.11.0", + "description": "When `true`, shows the +/- decade navigation buttons" }, { "prop": "roleDescription", @@ -95,7 +100,13 @@ }, { "prop": "hidden", - "description": "When set, renders a comment node instead of the calendar widget while keeping the Vue instance active. Mainly used when implementing a custom date picker" + "description": "When `true`, renders a comment node instead of the calendar widget while keeping the Vue instance active. Mainly used when implementing a custom date picker" + }, + { + "prop": "labelPrevDecade", + "version": "2.11.0", + "settings": true, + "description": "Value of the `aria-label` and `title` attributes on the optional `Previous Decade` navigation button" }, { "prop": "labelPrevYear", @@ -122,6 +133,12 @@ "settings": true, "description": "Value of the `aria-label` and `title` attributes on the `Next Year` navigation button" }, + { + "prop": "labelNextDecade", + "version": "2.11.0", + "settings": true, + "description": "Value of the `aria-label` and `title` attributes on the optional `Next Decade` navigation button" + }, { "prop": "labelSelected", "settings": true, From 4a67035709501d7ba9c06a9c72c4ee4b8347c69a Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 20:58:52 -0300 Subject: [PATCH 13/31] Update package.json --- src/components/form-datepicker/package.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/form-datepicker/package.json b/src/components/form-datepicker/package.json index 40816a62cbc..33a6c9c183f 100644 --- a/src/components/form-datepicker/package.json +++ b/src/components/form-datepicker/package.json @@ -98,7 +98,12 @@ }, { "prop": "hideHeader", - "description": "When set, visually hides the selected date header" + "description": "When `true`, visually hides the selected date header" + }, + { + "prop": "showDecadeNav", + "version": "2.11.0", + "description": "When `true`, shows the +/- decade navigation buttons" }, { "prop": "menuClass", @@ -173,6 +178,12 @@ "prop": "placeholder", "description": "Text so show in the form control when no date is selected. Defaults to the `label-no-date-selected` prop value" }, + { + "prop": "labelPrevDecade", + "version": "2.11.0", + "settings": true, + "description": "Value of the `aria-label` and `title` attributes on the optional `Previous Decade` navigation button" + }, { "prop": "labelPrevYear", "settings": true, @@ -198,6 +209,12 @@ "settings": true, "description": "Value of the `aria-label` and `title` attributes on the `Next Year` navigation button" }, + { + "prop": "labelNextDecade", + "version": "2.11.0", + "settings": true, + "description": "Value of the `aria-label` and `title` attributes on the optional `Next Decade` navigation button" + }, { "prop": "labelSelected", "settings": true, From a257a17ec4789ccdb97ad841f5721bf8aacb44a0 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 21:12:21 -0300 Subject: [PATCH 14/31] Update README.md --- src/components/calendar/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/calendar/README.md b/src/components/calendar/README.md index 18155a8f2ca..cc4f71f0845 100644 --- a/src/components/calendar/README.md +++ b/src/components/calendar/README.md @@ -290,6 +290,14 @@ formatted in the locale's language. You can hide this header via the `hide-header` prop. Note this only _visually hides_ the selected date, while keeping it available to screen reader users as an `aria-live` region. +### Optional decade navigation buttons + +Set the prop `show-decade-nav` to enable the previous and next decade buttons in the calendar's date +navigation toolbar. + +The props `label-prev-decade` and `label-next-decade` props can be used to provide custom label text +for the decade buttons. + ### Border and padding Fancy a calendar with a border with padding? Use Bootstrap's From 113ea407b386a633af21d762837b716d326d29ec Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 21:13:33 -0300 Subject: [PATCH 15/31] Update README.md --- src/components/form-datepicker/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/form-datepicker/README.md b/src/components/form-datepicker/README.md index 522de6225d6..97224dae68e 100644 --- a/src/components/form-datepicker/README.md +++ b/src/components/form-datepicker/README.md @@ -315,6 +315,14 @@ component's value. Want a fancy popup with a dark background instead of a light background? Set the `dark` prop to `true` to enable the dark background. +### Optional decade navigation buttons + +Set the prop `show-decade-nav` to enable the previous and next decade buttons in the datepicker's +date navigation toolbar. + +The props `label-prev-decade` and `label-next-decade` props can be used to provide custom label text +for the decade buttons. + ### Button only mode v2.7.0+ From a665e92b0c87e04975994de79caa4f1083b14341 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 21:37:28 -0300 Subject: [PATCH 16/31] Update README.md --- src/components/calendar/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/calendar/README.md b/src/components/calendar/README.md index cc4f71f0845..04831d43eab 100644 --- a/src/components/calendar/README.md +++ b/src/components/calendar/README.md @@ -499,6 +499,7 @@ the same locale as requested, depending on the supported locales of `Intl`). + Show decade navigation buttons @@ -523,6 +525,7 @@ the same locale as requested, depending on the supported locales of `Intl`). return { value: '', context: null, + showDecadeNav: false, locale: 'en-US', locales: [ { value: 'en-US', text: 'English US (en-US)' }, @@ -538,11 +541,13 @@ the same locale as requested, depending on the supported locales of `Intl`). ], labels: { de: { + labelPrevDecade: 'Vorheriges Jahrzehnt', labelPrevYear: 'Vorheriges Jahr', labelPrevMonth: 'Vorheriger Monat', labelCurrentMonth: 'Aktueller Monat', labelNextMonth: 'Nächster Monat', labelNextYear: 'Nächstes Jahr', + labelNextDecade: 'Nächstes Jahrzehnt', labelToday: 'Heute', labelSelected: 'Ausgewähltes Datum', labelNoDateSelected: 'Kein Datum gewählt', @@ -551,11 +556,13 @@ the same locale as requested, depending on the supported locales of `Intl`). labelHelp: 'Mit den Pfeiltasten durch den Kalender navigieren' }, 'ar-EG': { + labelPrevDecade: 'العقد السابق', labelPrevYear: 'العام السابق', labelPrevMonth: 'الشهر السابق', labelCurrentMonth: 'الشهر الحالي', labelNextMonth: 'الشهر المقبل', labelNextYear: 'العام المقبل', + labelNextDecade: 'العقد القادم', labelToday: 'اليوم', labelSelected: 'التاريخ المحدد', labelNoDateSelected: 'لم يتم اختيار تاريخ', @@ -564,11 +571,13 @@ the same locale as requested, depending on the supported locales of `Intl`). labelHelp: 'استخدم مفاتيح المؤشر للتنقل في التواريخ' }, zh: { + labelPrevDecade: '过去十年', labelPrevYear: '上一年', labelPrevMonth: '上个月', labelCurrentMonth: '当前月份', labelNextMonth: '下个月', labelNextYear: '明年', + labelNextDecade: '下一个十年', labelToday: '今天', labelSelected: '选定日期', labelNoDateSelected: '未选择日期', From 53ae87cbbdb4f2f1a86afd40b206f1ff594f04f0 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 21:50:41 -0300 Subject: [PATCH 17/31] Update README.md --- src/components/calendar/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/calendar/README.md b/src/components/calendar/README.md index 04831d43eab..0a80a6ebb61 100644 --- a/src/components/calendar/README.md +++ b/src/components/calendar/README.md @@ -499,7 +499,12 @@ the same locale as requested, depending on the supported locales of `Intl`). - Show decade navigation buttons + + Show decade navigation buttons + + + Hide the date header + @@ -526,6 +532,7 @@ the same locale as requested, depending on the supported locales of `Intl`). value: '', context: null, showDecadeNav: false, + hideHeader: false, locale: 'en-US', locales: [ { value: 'en-US', text: 'English US (en-US)' }, From e0f7efd5f0e242edba30e667d7e2cf8a1be8b132 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 21:52:36 -0300 Subject: [PATCH 18/31] Update README.md --- src/components/calendar/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/calendar/README.md b/src/components/calendar/README.md index 0a80a6ebb61..b03cba9facb 100644 --- a/src/components/calendar/README.md +++ b/src/components/calendar/README.md @@ -497,7 +497,7 @@ the same locale as requested, depending on the supported locales of `Intl`). - + Show decade navigation buttons From baa953a101d64377c7e1a826e982495a5491da43 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Mon, 6 Apr 2020 23:35:49 -0300 Subject: [PATCH 19/31] Update date.js --- src/utils/date.js | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/src/utils/date.js b/src/utils/date.js index 0eb7448a697..18ebcb73767 100644 --- a/src/utils/date.js +++ b/src/utils/date.js @@ -71,10 +71,22 @@ export const lastDateOfMonth = date => { return date } +export const addYears = (date, numberOfYears) => { + date = createDate(date) + const month = date.getMonth() + date.setFullYear(date.getFullYear() + numberOfYears) + // Handle Feb 29th for leap years + if (date.getMonth() !== month) { + date.setDate(0) + } + return date +} + export const oneMonthAgo = date => { date = createDate(date) const month = date.getMonth() date.setMonth(month - 1) + // Handle when days in month are different if (date.getMonth() === month) { date.setDate(0) } @@ -85,6 +97,7 @@ export const oneMonthAhead = date => { date = createDate(date) const month = date.getMonth() date.setMonth(month + 1) + // Handle when days in month are different if (date.getMonth() === (month + 2) % 12) { date.setDate(0) } @@ -92,43 +105,19 @@ export const oneMonthAhead = date => { } export const oneYearAgo = date => { - date = createDate(date) - const month = date.getMonth() - date.setMonth(month - 12) - if (date.getMonth() !== month) { - date.setDate(0) - } - return date + return addYears(date, -1) } export const oneYearAhead = date => { - date = createDate(date) - const month = date.getMonth() - date.setMonth(month + 12) - if (date.getMonth() !== month) { - date.setDate(0) - } - return date + return addYears(date, 1) } export const oneDecadeAgo = date => { - date = createDate(date) - const month = date.getMonth() - date.setFullYear(date.getFullYear() - 10) - if (date.getMonth() !== month) { - date.setDate(0) - } - return date + return addYears(date, -10) } export const oneDecadeAhead = date => { - date = createDate(date) - const month = date.getMonth() - date.setFullYear(date.getFullYear() + 10) - if (date.getMonth() !== month) { - date.setDate(0) - } - return date + return addYears(date, 10) } // Helper function to constrain a date between two values From 3f405d76dbe90ea5323191e6c64ad976131800f1 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Tue, 7 Apr 2020 02:17:28 -0300 Subject: [PATCH 20/31] Update README.md --- src/components/calendar/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/calendar/README.md b/src/components/calendar/README.md index b03cba9facb..ad7cfafb726 100644 --- a/src/components/calendar/README.md +++ b/src/components/calendar/README.md @@ -290,6 +290,8 @@ formatted in the locale's language. You can hide this header via the `hide-header` prop. Note this only _visually hides_ the selected date, while keeping it available to screen reader users as an `aria-live` region. +For example usage, refer to the [Internationalization section](#internationalization) below. + ### Optional decade navigation buttons Set the prop `show-decade-nav` to enable the previous and next decade buttons in the calendar's date @@ -298,6 +300,8 @@ navigation toolbar. The props `label-prev-decade` and `label-next-decade` props can be used to provide custom label text for the decade buttons. +For example usage, refer to the [Internationalization section](#internationalization) below. + ### Border and padding Fancy a calendar with a border with padding? Use Bootstrap's From baa7a9641c0d2f21e84de41a65f7cfb6d94adc8b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Tue, 7 Apr 2020 02:20:43 -0300 Subject: [PATCH 21/31] Update README.md --- src/components/form-datepicker/README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/form-datepicker/README.md b/src/components/form-datepicker/README.md index ffcfd109952..11d68bffd2f 100644 --- a/src/components/form-datepicker/README.md +++ b/src/components/form-datepicker/README.md @@ -323,6 +323,8 @@ date navigation toolbar. The props `label-prev-decade` and `label-next-decade` props can be used to provide custom label text for the decade buttons. +For example usage, refer to the [Internationalization section](#internationalization) below. + ### Button only mode v2.7.0+ @@ -462,13 +464,15 @@ Saturday. - - Show decade navigation buttons - +
+ + Show decade navigation buttons + - - Hide calendar header - + + Hide calendar header + +
Date: Tue, 7 Apr 2020 02:43:36 -0300 Subject: [PATCH 22/31] Update calendar.js --- src/components/calendar/calendar.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index 8a17b186456..201b7bd2dc7 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -601,6 +601,7 @@ export const BCalendar = Vue.extend({ // Focuses grid after updating const keyCode = evt.keyCode const altKey = evt.altKey + const ctrlKey = evt.ctrlKey if (!arrayIncludes([PAGEUP, PAGEDOWN, END, HOME, LEFT, UP, RIGHT, DOWN], keyCode)) { /* istanbul ignore next */ return @@ -614,13 +615,13 @@ export const BCalendar = Vue.extend({ const isRTL = this.isRTL if (keyCode === PAGEUP) { // PAGEUP - Previous month/year - activeDate = (altKey ? oneYearAgo : oneMonthAgo)(activeDate) + activeDate = (altKey ? ctrlKey ? oneDecadeAgo : oneYearAgo : oneMonthAgo)(activeDate) // We check the first day of month to be in rage checkDate = createDate(activeDate) checkDate.setDate(1) } else if (keyCode === PAGEDOWN) { // PAGEDOWN - Next month/year - activeDate = (altKey ? oneYearAhead : oneMonthAhead)(activeDate) + activeDate = (altKey ? ctrlKey ? oneDecadeAhead : oneYearAhead : oneMonthAhead)(activeDate) // We check the last day of month to be in rage checkDate = createDate(activeDate) checkDate.setMonth(checkDate.getMonth() + 1) @@ -846,7 +847,7 @@ export const BCalendar = Vue.extend({ this.labelPrevDecade, this.gotoPrevDecade, this.prevDecadeDisabled, - null + 'Ctrl+Alt+PageDown' ), makeNavBtn( $prevYearIcon, @@ -890,7 +891,7 @@ export const BCalendar = Vue.extend({ this.labelNextDecade, this.gotoNextDecade, this.nextDecadeDisabled, - null + 'Ctrl+Alt+PageUp' ) ] ) From f00712d7955f471ca0c361271f4aa4e03aaa940f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Tue, 7 Apr 2020 02:45:00 -0300 Subject: [PATCH 23/31] Update README.md --- src/components/calendar/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/calendar/README.md b/src/components/calendar/README.md index ad7cfafb726..01ac3c73ebc 100644 --- a/src/components/calendar/README.md +++ b/src/components/calendar/README.md @@ -638,6 +638,8 @@ Keyboard navigation: - PageDown moves to the same day in the next month - Alt+PageUp moves to the same day and month in the previous year - Alt+PageDown moves to the same day and month in the next year +- Ctrl+Alt+PageUp moves to the same day and month in the previous decade +- Ctrl+Alt+PageDown moves to the same day and month in the next decade - Home moves to today's date - End moves to the current selected date, or today if no selected date - Enter or Space selects the currently highlighted (focused) day From 2096f7474d1eda2633ec65fef51bcb8dc8f259d3 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Tue, 7 Apr 2020 02:48:54 -0300 Subject: [PATCH 24/31] lint --- src/components/calendar/calendar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index 201b7bd2dc7..e7389daf219 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -615,13 +615,13 @@ export const BCalendar = Vue.extend({ const isRTL = this.isRTL if (keyCode === PAGEUP) { // PAGEUP - Previous month/year - activeDate = (altKey ? ctrlKey ? oneDecadeAgo : oneYearAgo : oneMonthAgo)(activeDate) + activeDate = (altKey ? (ctrlKey ? oneDecadeAgo : oneYearAgo) : oneMonthAgo)(activeDate) // We check the first day of month to be in rage checkDate = createDate(activeDate) checkDate.setDate(1) } else if (keyCode === PAGEDOWN) { // PAGEDOWN - Next month/year - activeDate = (altKey ? ctrlKey ? oneDecadeAhead : oneYearAhead : oneMonthAhead)(activeDate) + activeDate = (altKey ? (ctrlKey ? oneDecadeAhead : oneYearAhead) : oneMonthAhead)(activeDate) // We check the last day of month to be in rage checkDate = createDate(activeDate) checkDate.setMonth(checkDate.getMonth() + 1) From 37e9217525f804996a3dec0fff9d9f1933b146c0 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Tue, 7 Apr 2020 02:58:08 -0300 Subject: [PATCH 25/31] lint --- src/components/calendar/calendar.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index e7389daf219..604ddfec8d2 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -621,7 +621,9 @@ export const BCalendar = Vue.extend({ checkDate.setDate(1) } else if (keyCode === PAGEDOWN) { // PAGEDOWN - Next month/year - activeDate = (altKey ? (ctrlKey ? oneDecadeAhead : oneYearAhead) : oneMonthAhead)(activeDate) + activeDate = (altKey ? (ctrlKey ? oneDecadeAhead : oneYearAhead) : oneMonthAhead)( + activeDate + ) // We check the last day of month to be in rage checkDate = createDate(activeDate) checkDate.setMonth(checkDate.getMonth() + 1) From e6e50ef6c80c8235f0c36eb3735a7347443bc601 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Tue, 7 Apr 2020 03:18:31 -0300 Subject: [PATCH 26/31] Update README.md --- src/components/form-datepicker/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/form-datepicker/README.md b/src/components/form-datepicker/README.md index 11d68bffd2f..e4df9fb0cab 100644 --- a/src/components/form-datepicker/README.md +++ b/src/components/form-datepicker/README.md @@ -591,7 +591,8 @@ details. [``](/docs/components/dropdown). `` uses Bootstrap's margin, padding, border, and flex utility classes, along with -button (`btn-*`) classes, and the `form-control*` (plus validation) classes. +button (`btn-*`) classes, dropdonw (`dropdown*`) classes, and the `form-control*` (plus validation) +classes. BootstrapVue's Custom SCSS/CSS is also required for proper styling of the date picker and calendar. From f80f52262fa4b18c345b7e820ade465f0350b036 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Tue, 7 Apr 2020 03:19:45 -0300 Subject: [PATCH 27/31] Update README.md --- src/components/form-timepicker/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/form-timepicker/README.md b/src/components/form-timepicker/README.md index e78bb6030a1..1d410706451 100644 --- a/src/components/form-timepicker/README.md +++ b/src/components/form-timepicker/README.md @@ -427,7 +427,8 @@ Refer to the [``](/docs/components/time#accessibility) documentation for [``](/docs/components/dropdown). `` uses Bootstrap's margin, padding, border, and flex utility classes, along with -button (`btn-*`) classes, and the `form-control*` (plus validation) classes. +button (`btn-*`) classes, dropdown (`dropdown*`) classes, and the `form-control*` (plus validation) +classes. BootstrapVue's Custom SCSS/CSS is also required for proper styling of the time picker and popup. From ebcc5e81c39de19fd0f3ee91f01bcee5139f7534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 7 Apr 2020 09:07:06 +0200 Subject: [PATCH 28/31] Update calendar.js --- src/components/calendar/calendar.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index 604ddfec8d2..98b469027a2 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -599,9 +599,7 @@ export const BCalendar = Vue.extend({ // Calendar keyboard navigation // Handles PAGEUP/PAGEDOWN/END/HOME/LEFT/UP/RIGHT/DOWN // Focuses grid after updating - const keyCode = evt.keyCode - const altKey = evt.altKey - const ctrlKey = evt.ctrlKey + const { altKey, ctrlKey, keyCode } = evt if (!arrayIncludes([PAGEUP, PAGEDOWN, END, HOME, LEFT, UP, RIGHT, DOWN], keyCode)) { /* istanbul ignore next */ return From 787e69360e0d49426ff320a39a521af303a9a223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 7 Apr 2020 09:07:25 +0200 Subject: [PATCH 29/31] Prettify --- src/components/avatar/README.md | 2 +- src/components/button/README.md | 2 +- src/components/calendar/README.md | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/avatar/README.md b/src/components/avatar/README.md index 87923df9d9c..d7e915f75f3 100644 --- a/src/components/avatar/README.md +++ b/src/components/avatar/README.md @@ -330,4 +330,4 @@ Avatars are based upon `` and `` components, and as such, rel `badge-*` and `btn-*` variant classes, as well as the `rounded-*` [utility classes](/docs/reference/utility-classes). -`` also requires BootstrapVue's custom CSS for proper styling. \ No newline at end of file +`` also requires BootstrapVue's custom CSS for proper styling. diff --git a/src/components/button/README.md b/src/components/button/README.md index fc1c43e25e0..64dbce58277 100644 --- a/src/components/button/README.md +++ b/src/components/button/README.md @@ -5,7 +5,7 @@ ## Overview -BootstrapVue's `` component generates either a `