UNPKG

bootstrap-vue

Version:

With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extens

309 lines (291 loc) 8.19 kB
import { extend } from '../../vue' import { NAME_FORM_TIMEPICKER } from '../../constants/components' import { EVENT_NAME_CONTEXT, EVENT_NAME_SHOWN, EVENT_NAME_HIDDEN } from '../../constants/events' import { PROP_TYPE_BOOLEAN, PROP_TYPE_DATE_STRING, PROP_TYPE_STRING } from '../../constants/props' import { SLOT_NAME_BUTTON_CONTENT } from '../../constants/slots' import { attemptBlur, attemptFocus } from '../../utils/dom' import { isUndefinedOrNull } from '../../utils/inspect' import { makeModelMixin } from '../../utils/model' import { omit, sortKeys } from '../../utils/object' import { makeProp, makePropsConfigurable, pluckProps } from '../../utils/props' import { idMixin, props as idProps } from '../../mixins/id' import { BIconClock, BIconClockFill } from '../../icons/icons' import { BButton } from '../button/button' import { BVFormBtnLabelControl, props as BVFormBtnLabelControlProps } from '../form-btn-label-control/bv-form-btn-label-control' import { BTime, props as BTimeProps } from '../time/time' // --- Constants --- const { mixin: modelMixin, props: modelProps, prop: MODEL_PROP_NAME, event: MODEL_EVENT_NAME } = makeModelMixin('value', { type: PROP_TYPE_STRING, defaultValue: '' }) // --- Props --- const timeProps = omit(BTimeProps, ['hidden', 'id', 'value']) const formBtnLabelControlProps = omit(BVFormBtnLabelControlProps, [ 'formattedValue', 'id', 'lang', 'rtl', 'value' ]) export const props = makePropsConfigurable( sortKeys({ ...idProps, ...modelProps, ...timeProps, ...formBtnLabelControlProps, closeButtonVariant: makeProp(PROP_TYPE_STRING, 'outline-secondary'), labelCloseButton: makeProp(PROP_TYPE_STRING, 'Close'), labelNowButton: makeProp(PROP_TYPE_STRING, 'Select now'), labelResetButton: makeProp(PROP_TYPE_STRING, 'Reset'), noCloseButton: makeProp(PROP_TYPE_BOOLEAN, false), nowButton: makeProp(PROP_TYPE_BOOLEAN, false), nowButtonVariant: makeProp(PROP_TYPE_STRING, 'outline-primary'), resetButton: makeProp(PROP_TYPE_BOOLEAN, false), resetButtonVariant: makeProp(PROP_TYPE_STRING, 'outline-danger'), resetValue: makeProp(PROP_TYPE_DATE_STRING) }), NAME_FORM_TIMEPICKER ) // --- Main component --- // @vue/component export const BFormTimepicker = /*#__PURE__*/ extend({ name: NAME_FORM_TIMEPICKER, mixins: [idMixin, modelMixin], props, data() { return { // We always use `HH:mm:ss` value internally localHMS: this[MODEL_PROP_NAME] || '', // Context data from BTime localLocale: null, isRTL: false, formattedValue: '', // If the menu is opened isVisible: false } }, computed: { computedLang() { return (this.localLocale || '').replace(/-u-.*$/i, '') || null } }, watch: { [MODEL_PROP_NAME](newValue) { this.localHMS = newValue || '' }, localHMS(newValue) { // We only update the v-model value when the timepicker // is open, to prevent cursor jumps when bound to a // text input in button only mode if (this.isVisible) { this.$emit(MODEL_EVENT_NAME, newValue || '') } } }, methods: { // Public methods focus() { if (!this.disabled) { attemptFocus(this.$refs.control) } }, blur() { if (!this.disabled) { attemptBlur(this.$refs.control) } }, // Private methods setAndClose(value) { this.localHMS = value this.$nextTick(() => { this.$refs.control.hide(true) }) }, onInput(hms) { if (this.localHMS !== hms) { this.localHMS = hms } }, onContext(ctx) { const { isRTL, locale, value, formatted } = ctx this.isRTL = isRTL this.localLocale = locale this.formattedValue = formatted this.localHMS = value || '' // Re-emit the context event this.$emit(EVENT_NAME_CONTEXT, ctx) }, onNowButton() { const now = new Date() const hours = now.getHours() const minutes = now.getMinutes() const seconds = this.showSeconds ? now.getSeconds() : 0 const value = [hours, minutes, seconds].map(v => `00${v || ''}`.slice(-2)).join(':') this.setAndClose(value) }, onResetButton() { this.setAndClose(this.resetValue) }, onCloseButton() { this.$refs.control.hide(true) }, onShow() { this.isVisible = true }, onShown() { this.$nextTick(() => { attemptFocus(this.$refs.time) this.$emit(EVENT_NAME_SHOWN) }) }, onHidden() { this.isVisible = false this.$emit(EVENT_NAME_HIDDEN) }, // Render function helpers defaultButtonFn({ isHovered, hasFocus }) { return this.$createElement(isHovered || hasFocus ? BIconClockFill : BIconClock, { attrs: { 'aria-hidden': 'true' } }) } }, render(h) { const { localHMS, disabled, readonly, $props } = this const placeholder = isUndefinedOrNull(this.placeholder) ? this.labelNoTimeSelected : this.placeholder // Footer buttons let $footer = [] if (this.nowButton) { const label = this.labelNowButton $footer.push( h( BButton, { props: { size: 'sm', disabled: disabled || readonly, variant: this.nowButtonVariant }, attrs: { 'aria-label': label || null }, on: { click: this.onNowButton }, key: 'now-btn' }, label ) ) } if (this.resetButton) { if ($footer.length > 0) { // Add a "spacer" between buttons ('&nbsp;') $footer.push(h('span', '\u00a0')) } const label = this.labelResetButton $footer.push( h( BButton, { props: { size: 'sm', disabled: disabled || readonly, variant: this.resetButtonVariant }, attrs: { 'aria-label': label || null }, on: { click: this.onResetButton }, key: 'reset-btn' }, label ) ) } if (!this.noCloseButton) { // Add a "spacer" between buttons ('&nbsp;') if ($footer.length > 0) { $footer.push(h('span', '\u00a0')) } const label = this.labelCloseButton $footer.push( h( BButton, { props: { size: 'sm', disabled, variant: this.closeButtonVariant }, attrs: { 'aria-label': label || null }, on: { click: this.onCloseButton }, key: 'close-btn' }, label ) ) } if ($footer.length > 0) { $footer = [ h( 'div', { staticClass: 'b-form-date-controls d-flex flex-wrap', class: { 'justify-content-between': $footer.length > 1, 'justify-content-end': $footer.length < 2 } }, $footer ) ] } const $time = h( BTime, { staticClass: 'b-form-time-control', props: { ...pluckProps(timeProps, $props), value: localHMS, hidden: !this.isVisible }, on: { input: this.onInput, context: this.onContext }, ref: 'time' }, $footer ) return h( BVFormBtnLabelControl, { staticClass: 'b-form-timepicker', props: { ...pluckProps(formBtnLabelControlProps, $props), id: this.safeId(), value: localHMS, formattedValue: localHMS ? this.formattedValue : '', placeholder, rtl: this.isRTL, lang: this.computedLang }, on: { show: this.onShow, shown: this.onShown, hidden: this.onHidden }, scopedSlots: { [SLOT_NAME_BUTTON_CONTENT]: this.$scopedSlots[SLOT_NAME_BUTTON_CONTENT] || this.defaultButtonFn }, ref: 'control' }, [$time] ) } })