Skip to content
Merged
25 changes: 25 additions & 0 deletions src/components/calendar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,31 @@ slot can be used to add buttons such as `Select Today` or `Reset`, etc.
<!-- b-calendar-default-slot.vue -->
```

### Date navigation button slots

<span class="badge badge-info small">2.12.0+</span>

To change the content of the calendar's date navigation buttons, BootstrapVue provides scoped slots
for each button:

- `'nav-prev-decade'`
- `'nav-prev-year'`
- `'nav-prev-month'`
- `'nav-this-month'` (the go to selected/today button)
- `'nav-next-month'`
- `'nav-next-year'`
- `'nav-next-decade'`

All seven slots have the same scoped property available:

| Property | Type | Description |
| -------- | ------- | --------------------------------------------------------------------- |
| `isRTL` | Boolean | Will be `true` when the date navigation bar is rendered right-to-left |

You can use the `isRTL` scoped property to "flip" the prev vs next button content to handle the
left-to-right to right-to-left orientation change &mdash; i.e. the previous year button will be on
the right when `isRTL` is `true`, instead of the left.

### Adding CSS classes to specific dates

If you need to highlight a specific date or dates, set the `date-info-fn` prop to a reference to a
Expand Down
30 changes: 22 additions & 8 deletions src/components/calendar/calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -796,14 +796,28 @@ export const BCalendar = Vue.extend({
)

// Content for the date navigation buttons
// TODO: add slots for the nav button content
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 } })
const navScope = { isRTL }
const navProps = { shiftV: 0.5 }
const navPrevProps = { ...navProps, flipH: isRTL }
const navNextProps = { ...navProps, flipH: !isRTL }
const $prevDecadeIcon =
this.normalizeSlot('nav-prev-decade', navScope) ||
h(BIconChevronBarLeft, { props: navPrevProps })
const $prevYearIcon =
this.normalizeSlot('nav-prev-year', navScope) ||
h(BIconChevronDoubleLeft, { props: navPrevProps })
const $prevMonthIcon =
this.normalizeSlot('nav-prev-month', navScope) || h(BIconChevronLeft, { props: navPrevProps })
const $thisMonthIcon =
this.normalizeSlot('nav-this-month', navScope) || h(BIconCircleFill, { props: navProps })
const $nextMonthIcon =
this.normalizeSlot('nav-next-month', navScope) || h(BIconChevronLeft, { props: navNextProps })
const $nextYearIcon =
this.normalizeSlot('nav-next-year', navScope) ||
h(BIconChevronDoubleLeft, { props: navNextProps })
const $nextDecadeIcon =
this.normalizeSlot('nav-next-decade', navScope) ||
h(BIconChevronBarLeft, { props: navNextProps })

// Utility to create the date navigation buttons
const makeNavBtn = (content, label, handler, btnDisabled, shortcut) => {
Expand Down
84 changes: 84 additions & 0 deletions src/components/calendar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,90 @@
{
"name": "default",
"description": "Used to place custom controls at the bottom of the calendar component"
},
{
"name": "nav-prev-decade",
"version": "2.12.0",
"description": "Used to place custom content in the previous decade navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the nav bar is rendered right to left"
}
]
},
{
"name": "nav-prev-year",
"version": "2.12.0",
"description": "Used to place custom content in the previous year navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-prev-month",
"version": "2.12.0",
"description": "Used to place custom content in the previous month navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-this-month",
"version": "2.12.0",
"description": "Used to place custom content in the this month/day navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-next-month",
"version": "2.12.0",
"description": "Used to place custom content in the next month navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-next-year",
"version": "2.12.0",
"description": "Used to place custom content in the next year navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-next-decade",
"version": "2.12.0",
"description": "Used to place custom content in the next decade navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
}
]
}
Expand Down
25 changes: 25 additions & 0 deletions src/components/form-datepicker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,31 @@ Notes:
- `year`, `month` and `day` will always be shown. If you need to leave out a value, set the property
to `undefined`, although this is highly discouraged for accessibility reasons

### Date navigation button slots

<span class="badge badge-info small">2.12.0+</span>

To change the content of the calendar's date navigation buttons, BootstrapVue provides scoped slots
for each button:

- `'nav-prev-decade'`
- `'nav-prev-year'`
- `'nav-prev-month'`
- `'nav-this-month'` (the go to selected/today button)
- `'nav-next-month'`
- `'nav-next-year'`
- `'nav-next-decade'`

All seven slots have the same scoped property available:

| Property | Type | Description |
| -------- | ------- | --------------------------------------------------------------------- |
| `isRTL` | Boolean | Will be `true` when the date navigation bar is rendered right-to-left |

You can use the `isRTL` scoped property to "flip" the prev vs next button content to handle the
left-to-right to right-to-left orientation change &mdash; i.e. the previous year button will be on
the right when `isRTL` is `true`, instead of the left.

## Internationalization

Internationalization of the date picker's calendar is provided via
Expand Down
15 changes: 13 additions & 2 deletions src/components/form-datepicker/form-datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BVFormBtnLabelControl, dropdownProps } from '../../utils/bv-form-btn-la
import { getComponentConfig } from '../../utils/config'
import { createDate, constrainDate, formatYMD, parseYMD } from '../../utils/date'
import { isUndefinedOrNull } from '../../utils/inspect'
import { pick } from '../../utils/object'
import idMixin from '../../mixins/id'
import { BButton } from '../button/button'
import { BCalendar } from '../calendar/calendar'
Expand Down Expand Up @@ -431,6 +432,7 @@ export const BFormDatepicker = /*#__PURE__*/ Vue.extend({
}
},
render(h) {
const $scopedSlots = this.$scopedSlots
const localYMD = this.localYMD
const disabled = this.disabled
const readonly = this.readonly
Expand Down Expand Up @@ -513,7 +515,16 @@ export const BFormDatepicker = /*#__PURE__*/ Vue.extend({
selected: this.onSelected,
input: this.onInput,
context: this.onContext
}
},
scopedSlots: pick($scopedSlots, [
'nav-prev-decade',
'nav-prev-year',
'nav-prev-month',
'nav-this-month',
'nav-next-month',
'nav-next-year',
'nav-next-decade'
])
},
$footer
)
Expand Down Expand Up @@ -541,7 +552,7 @@ export const BFormDatepicker = /*#__PURE__*/ Vue.extend({
hidden: this.onHidden
},
scopedSlots: {
'button-content': this.$scopedSlots['button-content'] || this.defaultButtonFn
'button-content': $scopedSlots['button-content'] || this.defaultButtonFn
}
},
[$calendar]
Expand Down
84 changes: 84 additions & 0 deletions src/components/form-datepicker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,90 @@
"description": "The visibility state of the popup. `true` if the popup is visible and `false` if not"
}
]
},
{
"name": "nav-prev-decade",
"version": "2.12.0",
"description": "Used to place custom content in the previous decade navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-prev-year",
"version": "2.12.0",
"description": "Used to place custom content in the previous year navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-prev-month",
"version": "2.12.0",
"description": "Used to place custom content in the previous month navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-this-month",
"version": "2.12.0",
"description": "Used to place custom content in the this month/day navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-next-month",
"version": "2.12.0",
"description": "Used to place custom content in the next month navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-next-year",
"version": "2.12.0",
"description": "Used to place custom content in the next year navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
},
{
"name": "nav-next-decade",
"version": "2.12.0",
"description": "Used to place custom content in the next decade navigation button",
"scope": [
{
"prop": "isRTL",
"type": "Boolean",
"description": "Will be `true` if the date navigation bar is rendered right to left"
}
]
}
]
}
Expand Down
15 changes: 12 additions & 3 deletions src/utils/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { isArray } from './array'
export const assign = (...args) => Object.assign(...args)
export const create = (proto, optionalProps) => Object.create(proto, optionalProps)
export const defineProperties = (obj, props) => Object.defineProperties(obj, props)
export const defineProperty = (obj, prop, descr) => Object.defineProperty(obj, prop, descr)
export const defineProperty = (obj, prop, descriptor) =>
Object.defineProperty(obj, prop, descriptor)
export const freeze = obj => Object.freeze(obj)
export const getOwnPropertyNames = obj => Object.getOwnPropertyNames(obj)
export const getOwnPropertyDescriptor = (obj, prop) => Object.getOwnPropertyDescriptor(obj, prop)
Expand Down Expand Up @@ -43,8 +44,16 @@ export const isPlainObject = obj => Object.prototype.toString.call(obj) === '[ob
export const clone = obj => ({ ...obj })

/**
* Return a shallow copy of object with
* the specified properties omitted
* Return a shallow copy of object with the specified properties only
* @link https://gist.github.com/bisubus/2da8af7e801ffd813fab7ac221aa7afc
*/
export const pick = (obj, props) =>
keys(obj)
.filter(key => props.indexOf(key) !== -1)
.reduce((result, key) => ({ ...result, [key]: obj[key] }), {})

/**
* Return a shallow copy of object with the specified properties omitted
* @link https://gist.github.com/bisubus/2da8af7e801ffd813fab7ac221aa7afc
*/
export const omit = (obj, props) =>
Expand Down
19 changes: 19 additions & 0 deletions src/utils/object.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { pick, omit } from './object'

describe('utils/object', () => {
it('pick() works', async () => {
const obj = { a: 1, b: 2, c: 3, d: null, e: [] }

expect(pick(obj, ['a', 'b', 'c'])).toEqual({ a: 1, b: 2, c: 3 })
expect(pick(obj, Object.keys(obj))).toEqual(obj)
expect(pick(obj, [])).toEqual({})
})

it('omit() works', async () => {
const obj = { a: 1, b: 2, c: 3, d: null, e: [] }

expect(omit(obj, ['a', 'b', 'c'])).toEqual({ d: null, e: [] })
expect(omit(obj, Object.keys(obj))).toEqual({})
expect(omit(obj, [])).toEqual(obj)
})
})