Update Node_modules

This commit is contained in:
2019-07-02 16:05:15 +02:00
parent 95a0ff3913
commit 9dab5e2dbc
3495 changed files with 39728 additions and 465154 deletions
+13
View File
@@ -11,6 +11,7 @@
:icon-pack="iconPack"
:maxlength="maxlength"
:autocomplete="newAutocomplete"
:use-html5-validation="useHtml5Validation"
v-bind="$attrs"
@input="onInput"
@focus="focused"
@@ -55,6 +56,11 @@
class="dropdown-item is-disabled">
<slot name="empty"/>
</div>
<div
v-if="hasFooterSlot"
class="dropdown-item">
<slot name="footer"/>
</div>
</div>
</div>
</transition>
@@ -140,6 +146,13 @@
*/
hasHeaderSlot() {
return !!this.$slots.header
},
/**
* Check if exists "footer" slot
*/
hasFooterSlot() {
return !!this.$slots.footer
}
},
watch: {
+7 -1
View File
@@ -2,6 +2,7 @@
<component
:is="tag"
class="button"
v-bind="$attrs"
:type="nativeType"
:class="[size, type, {
'is-rounded': rounded,
@@ -75,7 +76,12 @@
return [
'button',
'a',
'input'
'input',
'router-link',
'nuxt-link',
'n-link',
'NuxtLink',
'NLink'
].indexOf(value) >= 0
}
}
+17 -15
View File
@@ -11,7 +11,7 @@
ref="input"
slot="trigger"
autocomplete="off"
:value="formatValue(dateSelected)"
:value="formatValue(computedValue)"
:placeholder="placeholder"
:size="size"
:icon="icon"
@@ -21,9 +21,12 @@
:readonly="!editable"
:rounded="rounded"
v-bind="$attrs"
@change.native="onChange($event.target.value)"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event) && checkHtml5Validity()"/>
:use-html5-validation="useHtml5Validation"
@click.native.stop="toggle(true)"
@keyup.native.enter="toggle(true)"
@change.native="onChangeNativePicker"
@focus="handleOnFocus"
@blur="onBlur() && checkHtml5Validity()"/>
<div
class="card"
@@ -83,7 +86,7 @@
:min="minFaceValue"
:max="maxFaceValue"
:face-numbers="isSelectingHour ? hours : minutes"
:disabled-values="faceDisabledValues()"
:disabled-values="faceDisabledValues"
:double="isSelectingHour && isHourFormat24"
:value="isSelectingHour ? hoursSelected : minutesSelected"
@input="onClockInput"
@@ -102,7 +105,7 @@
ref="input"
type="time"
autocomplete="off"
:value="formatHHMMSS(value)"
:value="formatHHMMSS(computedValue)"
:placeholder="placeholder"
:size="size"
:icon="icon"
@@ -113,16 +116,18 @@
:disabled="disabled"
:readonly="false"
v-bind="$attrs"
:use-html5-validation="useHtml5Validation"
@click.native.stop="toggle(true)"
@keyup.native.enter="toggle(true)"
@change.native="onChangeNativePicker"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event) && checkHtml5Validity()"/>
@focus="handleOnFocus"
@blur="onBlur() && checkHtml5Validity()"/>
</div>
</template>
<script>
import TimepickerMixin from '../../utils/TimepickerMixin'
import Dropdown from '../dropdown/Dropdown'
import DropdownItem from '../dropdown/DropdownItem'
import Input from '../input/Input'
@@ -205,6 +210,9 @@
},
faceSize() {
return this.pickerSize - (outerPadding * 2)
},
faceDisabledValues() {
return this.isSelectingHour ? this.isHourDisabled : this.isMinuteDisabled
}
},
methods: {
@@ -227,13 +235,7 @@
this.meridienSelected = value
this.onMeridienChange(value)
}
},
faceDisabledValues() {
return this.isSelectingHour ? this.isHourDisabled : this.isMinuteDisabled
}
},
created() {
this.incrementMinutes = 5
}
}
</script>
+2 -1
View File
@@ -46,9 +46,10 @@ describe('BDatepicker', () => {
expect($dropdown.isVisible()).toBeTruthy()
})
it('call toggle method', () => {
it('call toggle method', async () => {
wrapper.vm.$refs.dropdown.isActive = false
wrapper.vm.toggle()
await wrapper.vm.$nextTick()
expect(wrapper.vm.$refs.dropdown.isActive).toBeTruthy()
expect($dropdown.isVisible()).toBeTruthy()
+196 -80
View File
@@ -11,7 +11,7 @@
ref="input"
slot="trigger"
autocomplete="off"
:value="formatValue(dateSelected)"
:value="formatValue(computedValue)"
:placeholder="placeholder"
:size="size"
:icon="icon"
@@ -21,8 +21,9 @@
:disabled="disabled"
:readonly="!editable"
v-bind="$attrs"
@click.native.stop="toggle(true)"
@keyup.native.enter="toggle(true)"
:use-html5-validation="useHtml5Validation"
@click.native="onInputClick"
@keyup.native.enter="togglePicker(true)"
@change.native="onChange($event.target.value)"
@focus="handleOnFocus"
@blur="onBlur" />
@@ -37,14 +38,14 @@
class="pagination field is-centered"
:class="size">
<a
v-show="!isFirstMonth && !disabled"
v-show="!showPrev && !disabled"
class="pagination-previous"
role="button"
href="#"
:disabled="disabled"
@click.prevent="decrementMonth"
@keydown.enter.prevent="decrementMonth"
@keydown.space.prevent="decrementMonth">
@click.prevent="prev"
@keydown.enter.prevent="prev"
@keydown.space.prevent="prev">
<b-icon
icon="chevron-left"
@@ -53,14 +54,14 @@
type="is-primary is-clickable"/>
</a>
<a
v-show="!isLastMonth && !disabled"
v-show="!showNext && !disabled"
class="pagination-next"
role="button"
href="#"
:disabled="disabled"
@click.prevent="incrementMonth"
@keydown.enter.prevent="incrementMonth"
@keydown.space.prevent="incrementMonth">
@click.prevent="next"
@keydown.enter.prevent="next"
@keydown.space.prevent="next">
<b-icon
icon="chevron-right"
@@ -71,6 +72,7 @@
<div class="pagination-list">
<b-field>
<b-select
v-if="!isTypeMonth"
v-model="focusedDateData.month"
:disabled="disabled"
:size="size">
@@ -97,9 +99,11 @@
</div>
</header>
<div class="datepicker-content">
<div
v-if="!isTypeMonth"
class="datepicker-content">
<b-datepicker-table
v-model="dateSelected"
v-model="computedValue"
:day-names="dayNames"
:month-names="monthNames"
:first-day-of-week="firstDayOfWeek"
@@ -113,7 +117,26 @@
:events="events"
:indicators="indicators"
:date-creator="dateCreator"
@close="toggle(false)"/>
:type-month="isTypeMonth"
:nearby-month-days="nearbyMonthDays"
:nearby-selectable-month-days="nearbySelectableMonthDays"
@close="togglePicker(false)"/>
</div>
<div v-else>
<b-datepicker-month
v-model="computedValue"
:month-names="monthNames"
:min-date="minDate"
:max-date="maxDate"
:focused="focusedDateData"
:disabled="disabled"
:unselectable-dates="unselectableDates"
:unselectable-days-of-week="unselectableDaysOfWeek"
:selectable-dates="selectableDates"
:events="events"
:indicators="indicators"
:date-creator="dateCreator"
@close="togglePicker(false)"/>
</div>
<footer
@@ -127,22 +150,21 @@
<b-input
v-else
ref="input"
type="date"
:type="!isTypeMonth ? 'date' : 'month'"
autocomplete="off"
:value="formatYYYYMMDD(value)"
:value="formatNative(computedValue)"
:placeholder="placeholder"
:size="size"
:icon="icon"
:icon-pack="iconPack"
:loading="loading"
:max="formatYYYYMMDD(maxDate)"
:min="formatYYYYMMDD(minDate)"
:max="formatNative(maxDate)"
:min="formatNative(minDate)"
:disabled="disabled"
:readonly="false"
v-bind="$attrs"
:use-html5-validation="useHtml5Validation"
@change.native="onChangeNativePicker"
@click.native.stop="toggle(true)"
@keyup.native.enter="toggle(true)"
@focus="handleOnFocus"
@blur="onBlur"/>
</div>
@@ -159,12 +181,37 @@
import Field from '../field/Field'
import Select from '../select/Select'
import Icon from '../icon/Icon'
import DatepickerTable from './DatepickerTable'
import DatepickerMonth from './DatepickerMonth'
const defaultDateFormatter = (date, vm) => {
const yyyyMMdd = date.getFullYear() +
'/' + (date.getMonth() + 1) +
'/' + date.getDate()
const d = new Date(yyyyMMdd)
return !vm.isTypeMonth ? d.toLocaleDateString()
: d.toLocaleDateString(undefined, { year: 'numeric', month: '2-digit' })
}
const defaultDateParser = (date, vm) => {
if (!vm.isTypeMonth) return new Date(Date.parse(date))
if (date) {
const s = date.split('/')
const year = s[0].length === 4 ? s[0] : s[1]
const month = s[0].length === 2 ? s[0] : s[1]
if (year && month) {
return new Date(parseInt(year, 10), parseInt(month - 1, 10), 1, 0, 0, 0, 0)
}
}
return null
}
export default {
name: 'BDatepicker',
components: {
[DatepickerTable.name]: DatepickerTable,
[DatepickerMonth.name]: DatepickerMonth,
[Input.name]: Input,
[Field.name]: Field,
[Select.name]: Select,
@@ -242,25 +289,21 @@
selectableDates: Array,
dateFormatter: {
type: Function,
default: (date) => {
default: (date, vm) => {
if (typeof config.defaultDateFormatter === 'function') {
return config.defaultDateFormatter(date)
} else {
const yyyyMMdd = date.getFullYear() +
'/' + (date.getMonth() + 1) +
'/' + date.getDate()
const d = new Date(yyyyMMdd)
return d.toLocaleDateString()
return defaultDateFormatter(date, vm)
}
}
},
dateParser: {
type: Function,
default: (date) => {
default: (date, vm) => {
if (typeof config.defaultDateParser === 'function') {
return config.defaultDateParser(date)
} else {
return new Date(Date.parse(date))
return defaultDateParser(date, vm)
}
}
},
@@ -286,7 +329,29 @@
type: String,
default: 'dots'
},
openOnFocus: Boolean
openOnFocus: Boolean,
yearsRange: {
type: Array,
default: () => {
return config.defaultDatepickerYearsRange
}
},
type: {
type: String,
validator: (value) => {
return [
'month'
].indexOf(value) >= 0
}
},
nearbyMonthDays: {
type: Boolean,
default: () => config.defaultDatepickerNearbyMonthDays
},
nearbySelectableMonthDays: {
type: Boolean,
default: () => config.defaultDatepickerNearbySelectableMonthDays
}
},
data() {
const focusedDate = this.value || this.focusedDate || this.dateCreator()
@@ -302,17 +367,27 @@
}
},
computed: {
computedValue: {
get() {
return this.dateSelected
},
set(value) {
this.updateInternalState(value)
this.togglePicker(false)
this.$emit('input', value)
}
},
/*
* Returns an array of years for the year dropdown. If earliest/latest
* dates are set by props, range of years will fall within those dates.
*/
listOfYears() {
let latestYear = this.focusedDateData.year + 3
let latestYear = this.focusedDateData.year + this.yearsRange[1]
if (this.maxDate && this.maxDate.getFullYear() < latestYear) {
latestYear = this.maxDate.getFullYear()
}
let earliestYear = (latestYear - 100) + 3
let earliestYear = this.focusedDateData.year + this.yearsRange[0]
if (this.minDate && this.minDate.getFullYear() > earliestYear) {
earliestYear = this.minDate.getFullYear()
}
@@ -325,15 +400,21 @@
return arrayOfYears.reverse()
},
isFirstMonth() {
showPrev() {
if (!this.minDate) return false
if (this.isTypeMonth) {
return this.focusedDateData.year <= this.minDate.getFullYear()
}
const dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month)
const date = new Date(this.minDate.getFullYear(), this.minDate.getMonth())
return (dateToCheck <= date)
},
isLastMonth() {
showNext() {
if (!this.maxDate) return false
if (this.isTypeMonth) {
return this.focusedDateData.year >= this.maxDate.getFullYear()
}
const dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month)
const date = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth())
return (dateToCheck >= date)
@@ -341,31 +422,21 @@
isMobile() {
return this.mobileNative && isMobile.any()
},
isTypeMonth() {
return this.type === 'month'
}
},
watch: {
/*
* Emit input event with selected date as payload, set isActive to false.
* Update internal focusedDateData
*/
dateSelected(value) {
const currentDate = !value ? this.dateCreator() : value
this.focusedDateData = {
month: currentDate.getMonth(),
year: currentDate.getFullYear()
}
this.$emit('input', value)
this.toggle()
},
/**
* When v-model is changed:
* 1. Update internal value.
* 2. If it's invalid, validate again.
*/
value(value) {
this.dateSelected = value
this.updateInternalState(value)
this.togglePicker(false)
!this.isValid && this.$refs.input.checkHtml5Validity()
},
@@ -389,24 +460,17 @@
}
},
methods: {
/*
* Emit input event with selected date as payload for v-model in parent
*/
updateSelectedDate(date) {
this.dateSelected = date
},
/*
* Parse string into date
*/
onChange(value) {
const date = this.dateParser(value)
const date = this.dateParser(value, this)
if (date && !isNaN(date)) {
this.dateSelected = date
this.computedValue = date
} else {
// Force refresh input value when not valid date
this.dateSelected = null
this.$refs.input.newValue = this.dateSelected
this.computedValue = null
this.$refs.input.newValue = this.computedValue
}
},
@@ -415,7 +479,7 @@
*/
formatValue(value) {
if (value && !isNaN(value)) {
return this.dateFormatter(value)
return this.dateFormatter(value, this)
} else {
return null
}
@@ -423,34 +487,47 @@
/*
* Either decrement month by 1 if not January or decrement year by 1
* and set month to 11 (December)
* and set month to 11 (December) or decrement year when 'month'
*/
decrementMonth() {
prev() {
if (this.disabled) return
if (this.focusedDateData.month > 0) {
this.focusedDateData.month -= 1
} else {
this.focusedDateData.month = 11
if (this.isTypeMonth) {
this.focusedDateData.year -= 1
} else {
if (this.focusedDateData.month > 0) {
this.focusedDateData.month -= 1
} else {
this.focusedDateData.month = 11
this.focusedDateData.year -= 1
}
}
},
/*
* Either increment month by 1 if not December or increment year by 1
* and set month to 0 (January)
* and set month to 0 (January) or increment year when 'month'
*/
incrementMonth() {
next() {
if (this.disabled) return
if (this.focusedDateData.month < 11) {
this.focusedDateData.month += 1
} else {
this.focusedDateData.month = 0
if (this.isTypeMonth) {
this.focusedDateData.year += 1
} else {
if (this.focusedDateData.month < 11) {
this.focusedDateData.month += 1
} else {
this.focusedDateData.month = 0
this.focusedDateData.year += 1
}
}
},
formatNative(value) {
return this.isTypeMonth
? this.formatYYYYMM(value) : this.formatYYYYMMDD(value)
},
/*
* Format date into string 'YYYY-MM-DD'
*/
@@ -467,18 +544,41 @@
return ''
},
/*
* Format date into string 'YYYY-MM'
*/
formatYYYYMM(value) {
const date = new Date(value)
if (value && !isNaN(date)) {
const year = date.getFullYear()
const month = date.getMonth() + 1
return year + '-' +
((month < 10 ? '0' : '') + month)
}
return ''
},
/*
* Parse date from string
*/
onChangeNativePicker(event) {
const date = event.target.value
this.dateSelected = date ? new Date(date.replace(/-/g, '/')) : null
this.computedValue = date ? new Date(date.replace(/-/g, '/')) : null
},
updateInternalState(value) {
const currentDate = !value ? this.dateCreator() : value
this.focusedDateData = {
month: currentDate.getMonth(),
year: currentDate.getFullYear()
}
this.dateSelected = value
},
/*
* Toggle datepicker
*/
toggle(active) {
togglePicker(active) {
if (this.$refs.dropdown) {
this.$refs.dropdown.isActive = typeof active === 'boolean'
? active
@@ -489,10 +589,26 @@
/*
* Call default onFocus method and show datepicker
*/
handleOnFocus() {
this.onFocus()
handleOnFocus(event) {
this.onFocus(event)
if (this.openOnFocus) {
this.toggle(true)
this.togglePicker(true)
}
},
/*
* Toggle dropdown
*/
toggle() {
this.$refs.dropdown.toggle()
},
/*
* Avoid dropdown toggle when is already visible
*/
onInputClick(event) {
if (this.$refs.dropdown.isActive) {
event.stopPropagation()
}
},
@@ -501,8 +617,8 @@
*/
keyPress(event) {
// Esc key
if (this.$refs.dropdown.isActive && event.keyCode === 27) {
this.toggle(false)
if (this.$refs.dropdown && this.$refs.dropdown.isActive && event.keyCode === 27) {
this.togglePicker(false)
}
}
},
+33
View File
@@ -0,0 +1,33 @@
import { shallowMount } from '@vue/test-utils'
import BDatepickerMonth from '@components/datepicker/DatepickerMonth'
import config, {setOptions} from '@utils/config'
describe('BDatepickerMonth', () => {
beforeEach(() => {
setOptions(Object.assign(config, {
defaultMonthNames: [
'January', 'February', 'March', 'April', 'May', 'June', 'July',
'August', 'September', 'October', 'November', 'December'
],
focusedDate: new Date('2018-07')
}))
})
const dateCreator = () => new Date()
it('is called', () => {
const wrapper = shallowMount(BDatepickerMonth, {
propsData: {
monthNames: config.defaultMonthNames,
focused: {
month: config.focusedDate.getMonth(),
year: config.focusedDate.getFullYear()
},
dateCreator
}
})
expect(wrapper.name()).toBe('BDatepickerMonth')
expect(wrapper.isVueInstance()).toBeTruthy()
})
})
+197
View File
@@ -0,0 +1,197 @@
<template>
<section class="datepicker-table">
<div class="datepicker-body" :class="{'has-events':hasEvents}">
<div class="datepicker-months">
<template v-for="(date, index) in monthDates">
<a
v-if="selectableDate(date) && !disabled"
:key="index"
:class="[
classObject(date),
{'has-event': eventsDateMatch(date)},
indicators
]"
class="datepicker-cell"
role="button"
href="#"
:disabled="disabled"
@click.prevent="emitChosenDate(date)"
@keydown.enter.prevent="emitChosenDate(date)"
@keydown.space.prevent="emitChosenDate(date)">
{{ monthNames[date.getMonth()] }}
<div class="events" v-if="eventsDateMatch(date)">
<div
class="event"
:class="event.type"
v-for="(event, index) in eventsDateMatch(date)"
:key="index"/>
</div>
</a>
<div
v-else
:key="index"
:class="classObject(date)"
class="datepicker-cell">
{{ monthNames[date.getMonth()] }}
</div>
</template>
</div>
</div>
</section>
</template>
<script>
export default {
name: 'BDatepickerMonth',
props: {
value: Date,
monthNames: Array,
events: Array,
indicators: String,
minDate: Date,
maxDate: Date,
focused: Object,
disabled: Boolean,
dateCreator: Function,
unselectableDates: Array,
unselectableDaysOfWeek: Array,
selectableDates: Array
},
computed: {
hasEvents() {
return this.events && this.events.length
},
/*
* Return array of all events in the specified month
*/
eventsInThisYear() {
if (!this.events) return []
const yearEvents = []
for (let i = 0; i < this.events.length; i++) {
let event = this.events[i]
if (!event.hasOwnProperty('date')) {
event = { date: event }
}
if (!event.hasOwnProperty('type')) {
event.type = 'is-primary'
}
if (
event.date.getFullYear() === this.focused.year
) {
yearEvents.push(event)
}
}
return yearEvents
},
monthDates() {
const year = this.focused.year
const months = []
for (let i = 0; i < 12; i++) {
const d = new Date(year, i, 1)
d.setHours(0, 0, 0, 0)
months.push(d)
}
return months
}
},
methods: {
selectableDate(day) {
const validity = []
if (this.minDate) {
validity.push(day >= this.minDate)
}
if (this.maxDate) {
validity.push(day <= this.maxDate)
}
validity.push(day.getFullYear() === this.focused.year)
if (this.selectableDates) {
for (let i = 0; i < this.selectableDates.length; i++) {
const enabledDate = this.selectableDates[i]
if (day.getFullYear() === enabledDate.getFullYear() &&
day.getMonth() === enabledDate.getMonth()) {
return true
} else {
validity.push(false)
}
}
}
if (this.unselectableDates) {
for (let i = 0; i < this.unselectableDates.length; i++) {
const disabledDate = this.unselectableDates[i]
validity.push(
day.getFullYear() !== disabledDate.getFullYear() ||
day.getMonth() !== disabledDate.getMonth()
)
}
}
if (this.unselectableDaysOfWeek) {
for (let i = 0; i < this.unselectableDaysOfWeek.length; i++) {
const dayOfWeek = this.unselectableDaysOfWeek[i]
validity.push(day.getDay() !== dayOfWeek)
}
}
return validity.indexOf(false) < 0
},
eventsDateMatch(day) {
if (!this.eventsInThisYear.length) return false
const monthEvents = []
for (let i = 0; i < this.eventsInThisYear.length; i++) {
if (this.eventsInThisYear[i].date.getMonth() === day.getMonth()) {
monthEvents.push(this.events[i])
}
}
if (!monthEvents.length) {
return false
}
return monthEvents
},
/*
* Build classObject for cell using validations
*/
classObject(day) {
function dateMatch(dateOne, dateTwo) {
// if either date is null or undefined, return false
if (!dateOne || !dateTwo) {
return false
}
return (dateOne.getFullYear() === dateTwo.getFullYear() &&
dateOne.getMonth() === dateTwo.getMonth())
}
return {
'is-selected': dateMatch(day, this.value),
'is-today': dateMatch(day, this.dateCreator()),
'is-selectable': this.selectableDate(day) && !this.disabled,
'is-unselectable': !this.selectableDate(day) || this.disabled
}
},
/*
* Emit select event with chosen date as payload
*/
emitChosenDate(day) {
if (this.disabled) return
if (this.selectableDate(day)) {
this.$emit('input', day)
}
}
}
}
</script>
+5 -1
View File
@@ -24,6 +24,8 @@
:events="eventsInThisWeek(week)"
:indicators="indicators"
:date-creator="dateCreator"
:nearby-month-days="nearbyMonthDays"
:nearby-selectable-month-days="nearbySelectableMonthDays"
@select="updateSelectedDate"/>
</div>
</section>
@@ -51,7 +53,9 @@
dateCreator: Function,
unselectableDates: Array,
unselectableDaysOfWeek: Array,
selectableDates: Array
selectableDates: Array,
nearbyMonthDays: Boolean,
nearbySelectableMonthDays: Boolean
},
computed: {
visibleDayNames() {
+11 -7
View File
@@ -4,7 +4,7 @@
<a
v-if="selectableDate(day) && !disabled"
:key="index"
:class="[classObject(day), {'has-event':eventsDateMatch(day)}, indicators]"
:class="[classObject(day), {'has-event': eventsDateMatch(day)}, indicators]"
class="datepicker-cell"
role="button"
href="#"
@@ -13,7 +13,6 @@
@keydown.enter.prevent="emitChosenDate(day)"
@keydown.space.prevent="emitChosenDate(day)">
{{ day.getDate() }}
<div class="events" v-if="eventsDateMatch(day)">
<div
class="event"
@@ -21,7 +20,6 @@
v-for="(event, index) in eventsDateMatch(day)"
:key="index"/>
</div>
</a>
<div
v-else
@@ -55,7 +53,9 @@
selectableDates: Array,
events: Array,
indicators: String,
dateCreator: Function
dateCreator: Function,
nearbyMonthDays: Boolean,
nearbySelectableMonthDays: Boolean
},
methods: {
/*
@@ -73,7 +73,9 @@
validity.push(day <= this.maxDate)
}
validity.push(day.getMonth() === this.month)
if (this.nearbyMonthDays && !this.nearbySelectableMonthDays) {
validity.push(day.getMonth() === this.month)
}
if (this.selectableDates) {
for (let i = 0; i < this.selectableDates.length; i++) {
@@ -121,7 +123,7 @@
},
eventsDateMatch(day) {
if (!this.events.length) return false
if (!this.events || !this.events.length) return false
const dayEvents = []
@@ -157,7 +159,9 @@
'is-selected': dateMatch(day, this.selectedDate),
'is-today': dateMatch(day, this.dateCreator()),
'is-selectable': this.selectableDate(day) && !this.disabled,
'is-unselectable': !this.selectableDate(day) || this.disabled
'is-unselectable': !this.selectableDate(day) || this.disabled,
'is-invisible': !this.nearbyMonthDays && day.getMonth() !== this.month,
'is-nearby': this.nearbySelectableMonthDays && day.getMonth() !== this.month
}
}
}
+44 -8
View File
@@ -20,7 +20,7 @@
</transition>
<transition :name="animation">
<div
v-show="(!disabled && (isActive || hoverable)) || inline"
v-show="(!disabled && (isActive || isHoverable)) || inline"
ref="dropdownMenu"
class="dropdown-menu"
:aria-hidden="!isActive">
@@ -35,6 +35,8 @@
</template>
<script>
import config from '../../utils/config'
export default {
name: 'BDropdown',
props: {
@@ -57,7 +59,9 @@
},
mobileModal: {
type: Boolean,
default: true
default: () => {
return config.defaultDropdownMobileModal
}
},
ariaRole: {
type: String,
@@ -66,12 +70,18 @@
animation: {
type: String,
default: 'fade'
},
multiple: Boolean,
closeOnClick: {
type: Boolean,
default: true
}
},
data() {
return {
selected: this.value,
isActive: false,
isHoverable: this.hoverable,
_isDropdown: true // Used internally by DropdownItem
}
},
@@ -115,12 +125,38 @@
* 3. Close the dropdown.
*/
selectItem(value) {
if (this.selected !== value) {
this.$emit('change', value)
this.selected = value
if (this.multiple) {
if (this.selected) {
const index = this.selected.indexOf(value)
if (index === -1) {
this.selected.push(value)
} else {
this.selected.splice(index, 1)
}
} else {
this.selected = [value]
}
this.$emit('change', this.selected)
} else {
if (this.selected !== value) {
this.selected = value
this.$emit('change', this.selected)
}
}
this.$emit('input', this.selected)
if (!this.multiple) {
this.isActive = !this.closeOnClick
/*
* breaking change
if (this.hoverable && this.closeOnClick) {
this.isHoverable = false
// Timeout for the animation complete before destroying
setTimeout(() => {
this.isHoverable = true
}, 250)
}
*/
}
this.$emit('input', value)
this.isActive = false
},
/**
@@ -164,7 +200,7 @@
* Toggle dropdown if it's not disabled.
*/
toggle() {
if (this.disabled || this.hoverable) return
if (this.disabled) return
if (!this.isActive) {
// if not active, toggle after clickOutside event
+7 -2
View File
@@ -42,7 +42,7 @@
return {
'is-disabled': this.$parent.disabled || this.disabled,
'is-paddingless': this.paddingless,
'is-active': this.value !== null && this.value === this.$parent.selected
'is-active': this.isActive
}
},
itemClasses() {
@@ -50,7 +50,7 @@
'dropdown-item': !this.hasLink,
'is-disabled': this.disabled,
'is-paddingless': this.paddingless,
'is-active': this.value !== null && this.value === this.$parent.selected,
'is-active': this.isActive,
'has-link': this.hasLink
}
},
@@ -62,6 +62,11 @@
*/
isClickable() {
return !this.$parent.disabled && !this.separator && !this.disabled && !this.custom
},
isActive() {
if (this.$parent.selected === null) return false
if (this.$parent.multiple) return this.$parent.selected.indexOf(this.value) >= 0
return this.value === this.$parent.selected
}
},
methods: {
+20 -6
View File
@@ -5,20 +5,22 @@
class="field-label"
:class="[customClass, fieldLabelSize]">
<label
v-if="label"
v-if="hasLabel"
:for="labelFor"
:class="customClass"
class="label" >
{{ label }}
<slot v-if="$slots.label" name="label"/>
<template v-else>{{ label }}</template>
</label>
</div>
<template v-else>
<label
v-if="label"
v-if="hasLabel"
:for="labelFor"
:class="customClass"
class="label">
{{ label }}
<slot v-if="$slots.label" name="label"/>
<template v-else>{{ label }}</template>
</label>
</template>
<b-field-body
@@ -40,6 +42,7 @@
</template>
<script>
import config from '../../utils/config'
import FieldBody from './FieldBody'
export default {
@@ -61,7 +64,11 @@
type: Boolean,
default: true
},
customClass: String
customClass: String,
labelPosition: {
type: String,
default: () => { return config.defaultFieldLabelPosition }
}
},
data() {
return {
@@ -76,7 +83,11 @@
return [this.newPosition, {
'is-expanded': this.expanded,
'is-grouped-multiline': this.groupMultiline,
'is-horizontal': this.horizontal
'is-horizontal': this.horizontal,
'is-floating-in-label': this.hasLabel && !this.horizontal &&
this.labelPosition === 'inside',
'is-floating-label': this.hasLabel && !this.horizontal &&
this.labelPosition === 'on-border'
}]
},
/**
@@ -128,6 +139,9 @@
}
return messages.filter((m) => { if (m) return m }).join(' <br> ')
}
},
hasLabel() {
return this.label || this.$slots.label
}
},
watch: {
+3 -3
View File
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BIcon render correctly 1`] = `<span class="icon"><i class="mdi mdi-undefined mdi-24px"></i></span>`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BIcon render correctly 1`] = `<span class="icon"><i class="mdi mdi-undefined mdi-24px"></i></span>`;
+2
View File
@@ -18,6 +18,7 @@ import Pagination from './pagination'
import Radio from './radio'
import Select from './select'
import Snackbar from './snackbar'
import Steps from './steps'
import Switch from './switch'
import Table from './table'
import Tabs from './tabs'
@@ -49,6 +50,7 @@ export {
Radio,
Select,
Snackbar,
Steps,
Switch,
Table,
Tabs,
+14 -6
View File
@@ -1,11 +1,14 @@
<template>
<transition :name="animation">
<div v-if="isActive" class="modal is-active">
<div
v-if="isActive"
class="modal is-active"
:class="{ 'is-full-screen': fullScreen }">
<div class="modal-background" @click="cancel('outside')"/>
<div
class="animation-content"
:class="{ 'modal-content': !hasModalCard }"
:style="{ maxWidth: newWidth }">
:style="customStlye">
<component
v-if="component"
v-bind="props"
@@ -52,8 +55,6 @@
type: [Array, Boolean],
default: () => {
return config.defaultModalCanCancel
? config.defaultModalCanCancel
: ['escape', 'x', 'outside', 'button']
}
},
onCancel: {
@@ -73,7 +74,8 @@
'keep'
].indexOf(value) >= 0
}
}
},
fullScreen: Boolean
},
data() {
return {
@@ -88,12 +90,18 @@
cancelOptions() {
return typeof this.canCancel === 'boolean'
? this.canCancel
? ['escape', 'x', 'outside', 'button']
? config.defaultModalCanCancel
: []
: this.canCancel
},
showX() {
return this.cancelOptions.indexOf('x') >= 0
},
customStlye() {
if (!this.fullScreen) {
return { maxWidth: this.newWidth }
}
return null
}
},
watch: {
+4 -1
View File
@@ -21,7 +21,10 @@
aria-hidden/>
</div>
<div class="media-content">
<p class="text" v-if="message">{{ message }}</p>
<p
class="text"
v-if="message"
v-html="message" />
<slot v-else/>
</div>
</div>
+1 -1
View File
@@ -19,7 +19,7 @@
},
data() {
return {
newDuration: this.duration || config.detaultNotificationDuration
newDuration: this.duration || config.defaultNotificationDuration
}
}
}
+28 -29
View File
@@ -1,17 +1,18 @@
<template>
<div class="b-numberinput field" :class="fieldClasses">
<p class="control">
<p
class="control"
@mouseup="onStopLongPress(false)"
@mouseleave="onStopLongPress(false)"
@touchend="onStopLongPress(false)"
@touchcancel="onStopLongPress(false)">
<button
type="button"
class="button"
:class="buttonClasses"
:disabled="disabled || disabledMin"
@mousedown="onStartLongPress($event, false)"
@mouseup="onStopLongPress(false)"
@mouseleave="onStopLongPress(false)"
@touchstart.prevent="onStartLongPress($event, false)"
@touchend="onStopLongPress(false)"
@touchcancel="onStopLongPress(false)"
@click="onControlClick($event, false)">
<b-icon
icon="minus"
@@ -22,6 +23,7 @@
<b-input
type="number"
ref="input"
v-model.number="computedValue"
v-bind="$attrs"
:step="newStep"
@@ -36,21 +38,23 @@
:icon-pack="iconPack"
:autocomplete="autocomplete"
:expanded="expanded"
:use-html5-validation="useHtml5Validation"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event)" />
<p class="control">
<p
class="control"
@mouseup="onStopLongPress(true)"
@mouseleave="onStopLongPress(true)"
@touchend="onStopLongPress(true)"
@touchcancel="onStopLongPress(true)">
<button
type="button"
class="button"
:class="buttonClasses"
:disabled="disabled || disabledMax"
@mousedown="onStartLongPress($event, true)"
@mouseup="onStopLongPress(true)"
@mouseleave="onStopLongPress(true)"
@touchstart.prevent="onStartLongPress($event, true)"
@touchend="onStopLongPress(true)"
@touchcancel="onStopLongPress(true)"
@click="onControlClick($event, true)">
<b-icon
icon="plus"
@@ -96,7 +100,7 @@
},
data() {
return {
newValue: this.value || parseFloat(this.min) || 0,
newValue: !isNaN(this.value) ? this.value : parseFloat(this.min) || 0,
newStep: this.step || 1,
_elementRef: 'input'
}
@@ -107,9 +111,13 @@
return this.newValue
},
set(value) {
this.newValue = value
this.$emit('input', value)
!this.isValid && this.checkHtml5Validity()
let newValue = value
if (value === '') {
newValue = parseFloat(this.min) || 0
}
this.newValue = newValue
this.$emit('input', newValue)
!this.isValid && this.$refs.input.checkHtml5Validity()
}
},
fieldClasses() {
@@ -177,7 +185,8 @@
}
},
onControlClick(event, inc) {
if (event.detail !== 0) return
// IE 11 -> filter click event
if (event.detail !== 0 || event.type === 'click') return
if (inc) this.increment()
else this.decrement()
},
@@ -185,30 +194,20 @@
if (event.button !== 0 && event.type !== 'touchstart') return
this._$intervalTime = new Date()
clearInterval(this._$intervalRef)
this._$intervalRef = this._$intervalRef = setInterval(() => {
this._$intervalRef = setInterval(() => {
if (inc) this.increment()
else this.decrement()
}, 100)
}, 250)
},
onStopLongPress(inc) {
if (!this._$intervalRef) return
const d = new Date()
if (d - this._$intervalTime < 100) {
if (d - this._$intervalTime < 250) {
if (inc) this.increment()
else this.decrement()
}
clearInterval(this._$intervalRef)
this._$intervalRef = null
},
onChange(event) {
const value = event.target.value
if (value) {
const number = parseFloat(value)
if (number < this.minNumber) {
this.computedValue = this.minNumber
} else if (number > this.maxNumber) {
this.computedValue = this.maxNumber
}
}
}
}
}
+2 -2
View File
@@ -11,7 +11,7 @@
icon="chevron-left"
:pack="iconPack"
both
aria-hidden/>
aria-hidden="true"/>
</a>
<a
role="button"
@@ -24,7 +24,7 @@
icon="chevron-right"
:pack="iconPack"
both
aria-hidden/>
aria-hidden="true"/>
</a>
<ul class="pagination-list" v-if="!simple">
<!--First-->
+1 -1
View File
@@ -6,7 +6,7 @@
v-show="isActive"
class="snackbar"
:class="[type,position]">
<p class="text">{{ message }}</p>
<div class="text" v-html="message"/>
<div
v-if="actionText"
class="action"
+88
View File
@@ -0,0 +1,88 @@
<script>
export default {
name: 'BStepItem',
props: {
label: String,
type: String | Object,
icon: String,
iconPack: String,
clickable: Boolean,
completed: {
type: Boolean,
default: false
},
visible: {
type: Boolean,
default: true
}
},
data() {
return {
isActive: false,
transitionName: null
}
},
methods: {
/**
* Activate step, alter animation name based on the index.
*/
activate(oldIndex, index) {
this.transitionName = index < oldIndex
? 'slide-next'
: 'slide-prev'
this.isActive = true
},
/**
* Deactivate step, alter animation name based on the index.
*/
deactivate(oldIndex, index) {
this.transitionName = index < oldIndex
? 'slide-next'
: 'slide-prev'
this.isActive = false
}
},
created() {
if (!this.$parent.$data._isSteps) {
this.$destroy()
throw new Error('You should wrap bStepItem on a bSteps')
}
this.$parent.stepItems.push(this)
},
beforeDestroy() {
const index = this.$parent.stepItems.indexOf(this)
if (index >= 0) {
this.$parent.stepItems.splice(index, 1)
}
},
render(createElement) {
// if destroy apply v-if
if (this.$parent.destroyOnHide) {
if (!this.isActive || !this.visible) {
return
}
}
const vnode = createElement('div', {
directives: [{
name: 'show',
value: this.isActive && this.visible
}],
attrs: { 'class': 'step-item' }
}, this.$slots.default)
// check animated prop
if (this.$parent.animated) {
return createElement('transition', {
props: {
'name': this.transitionName
},
on: {
'before-enter': () => { this.$parent.isTransitioning = true },
'after-enter': () => { this.$parent.isTransitioning = false }
}
}, [vnode])
}
return vnode
}
}
</script>
+10
View File
@@ -0,0 +1,10 @@
import { mount } from '@vue/test-utils'
import BSteps from '@components/steps/Steps'
describe('BSteps', () => {
it('is called', () => {
const wrapper = mount(BSteps)
expect(wrapper.name()).toBe('BSteps')
expect(wrapper.isVueInstance()).toBeTruthy()
})
})
+236
View File
@@ -0,0 +1,236 @@
<template>
<div class="b-steps">
<nav class="steps" :class="mainClasses">
<ul class="step-items">
<li
v-for="(stepItem, index) in stepItems"
:key="index"
v-show="stepItem.visible"
class="step-item"
:class="[stepItem.type || type, {
'is-active': activeStep === index,
'is-completed': stepItem.completed || activeStep > index
}]">
<a
class="step-link"
:class="{'is-clickable': isItemClickable(stepItem)}"
@click="stepItem.clickable && stepClick(index)">
<div class="step-marker">
<b-icon
v-if="stepItem.icon"
:icon="stepItem.icon"
:pack="stepItem.iconPack"
:size="size"/>
</div>
<div class="step-details">
<span class="step-title">{{ stepItem.label }}</span>
</div>
</a>
</li>
</ul>
</nav>
<section class="step-content" :class="{'is-transitioning': isTransitioning}">
<slot/>
</section>
<nav v-if="hasNavigation" class="step-navigation">
<a
role="button"
href="#"
class="pagination-previous"
:disabled="!hasPrev"
@click.prevent="prev"
:aria-label="ariaPreviousLabel">
<b-icon
icon="chevron-left"
:pack="iconPack"
both
aria-hidden="true"/>
</a>
<a
role="button"
href="#"
class="pagination-next"
:disabled="!hasNext"
@click.prevent="next"
:aria-label="ariaNextLabel">
<b-icon
icon="chevron-right"
:pack="iconPack"
both
aria-hidden="true"/>
</a>
</nav>
</div>
</template>
<script>
import Icon from '../icon/Icon'
import SlotComponent from '../../utils/SlotComponent'
export default {
name: 'BSteps',
components: {
[Icon.name]: Icon,
[SlotComponent.name]: SlotComponent
},
props: {
value: Number,
type: String | Object,
size: String,
animated: {
type: Boolean,
default: true
},
destroyOnHide: {
type: Boolean,
default: false
},
iconPack: String,
hasNavigation: {
type: Boolean,
default: true
},
ariaNextLabel: String,
ariaPreviousLabel: String
},
data() {
return {
activeStep: this.value || 0,
stepItems: [],
contentHeight: 0,
isTransitioning: false,
_isSteps: true // Used internally by StepItem
}
},
computed: {
mainClasses() {
return [
this.type,
this.size
]
},
reversedStepItems() {
return this.stepItems.slice().reverse()
},
/**
* Check the first visible step index.
*/
firstVisibleStepIndex() {
return this.stepItems.findIndex((step, idx) => {
return step.visible
})
},
/**
* Check if previous button is available.
*/
hasPrev() {
return this.firstVisibleStepIndex >= 0 &&
this.activeStep > this.firstVisibleStepIndex
},
/**
* Check the last visible step index.
*/
lastVisibleStepIndex() {
let idx = this.reversedStepItems.findIndex((step, idx) => {
return step.visible
})
if (idx >= 0) {
return this.stepItems.length - 1 - idx
}
return idx
},
/**
* Check if next button is available.
*/
hasNext() {
return this.lastVisibleStepIndex >= 0 &&
this.activeStep < this.lastVisibleStepIndex
}
},
watch: {
/**
* When v-model is changed set the new active step.
*/
value(value) {
this.changeStep(value)
},
/**
* When step-items are updated, set active one.
*/
stepItems() {
if (this.activeStep < this.stepItems.length) {
this.stepItems[this.activeStep].isActive = true
}
}
},
methods: {
/**
* Change the active step and emit change event.
*/
changeStep(newIndex) {
if (this.activeStep === newIndex) return
if (this.activeStep < this.stepItems.length) {
this.stepItems[this.activeStep].deactivate(this.activeStep, newIndex)
}
this.stepItems[newIndex].activate(this.activeStep, newIndex)
this.activeStep = newIndex
this.$emit('change', newIndex)
},
/**
* Return if the step should be clickable or not.
*/
isItemClickable(stepItem) {
if (stepItem.clickable === undefined) {
return stepItem.completed
}
return stepItem.clickable
},
/**
* Step click listener, emit input event and change active step.
*/
stepClick(value) {
this.$emit('input', value)
this.changeStep(value)
},
/**
* Previous button click listener.
*/
prev() {
if (!this.hasPrev) return
let prevItemIdx = this.reversedStepItems.findIndex((step, idx) => {
return this.stepItems.length - 1 - idx < this.activeStep && step.visible
})
if (prevItemIdx >= 0) {
prevItemIdx = this.stepItems.length - 1 - prevItemIdx
}
this.changeStep(prevItemIdx)
},
/**
* Previous button click listener.
*/
next() {
if (!this.hasNext) return
const nextItemIdx = this.stepItems.findIndex((step, idx) => {
return idx > this.activeStep && step.visible
})
this.changeStep(nextItemIdx)
}
},
mounted() {
if (this.activeStep < this.stepItems.length) {
this.stepItems[this.activeStep].isActive = true
}
}
}
</script>
+20
View File
@@ -0,0 +1,20 @@
import Steps from './Steps'
import StepItem from './StepItem'
import { use, registerComponent } from '../../utils/plugins'
const Plugin = {
install(Vue) {
registerComponent(Vue, Steps)
registerComponent(Vue, StepItem)
}
}
use(Plugin)
export default Plugin
export {
Steps,
StepItem
}
+13 -5
View File
@@ -20,10 +20,12 @@
<tr>
<th v-if="showDetailRowIcon" width="40px"/>
<th class="checkbox-cell" v-if="checkable">
<b-checkbox
:value="isAllChecked"
:disabled="isAllUncheckable"
@change.native="checkAll"/>
<template v-if="headerCheckable">
<b-checkbox
:value="isAllChecked"
:disabled="isAllUncheckable"
@change.native="checkAll"/>
</template>
</th>
<th
v-for="(column, index) in visibleColumns"
@@ -32,7 +34,9 @@
'is-current-sort': currentSortColumn === column,
'is-sortable': column.sortable
}"
:style="{ width: column.width + 'px' }"
:style="{
width: column.width === undefined ? null : column.width + 'px'
}"
@click.stop="sort(column)">
<div
class="th-wrap"
@@ -223,6 +227,10 @@
loading: Boolean,
detailed: Boolean,
checkable: Boolean,
headerCheckable: {
type: Boolean,
default: true
},
selected: Object,
focusable: Boolean,
customIsChecked: Function,
+13
View File
@@ -16,6 +16,7 @@
:disabled="disabled"
:ellipsis="ellipsis"
:closable="closable"
:title="ellipsis && getNormalizedTagText(tag)"
@close="removeTag(index)">
{{ getNormalizedTagText(tag) }}
</b-tag>
@@ -36,6 +37,7 @@
:loading="loading"
:autocomplete="nativeAutocomplete"
:keep-first="!allowNew"
:use-html5-validation="useHtml5Validation"
@typing="onTyping"
@focus="onFocus"
@blur="customOnBlur"
@@ -54,6 +56,9 @@
<template :slot="emptySlotName">
<slot name="empty" />
</template>
<template :slot="footerSlotName">
<slot name="footer" />
</template>
</b-autocomplete>
</div>
@@ -176,6 +181,10 @@
return this.hasHeaderSlot ? 'header' : 'dontrender'
},
footerSlotName() {
return this.hasHeaderSlot ? 'footer' : 'dontrender'
},
hasDefaultSlot() {
return !!this.$scopedSlots.default
},
@@ -188,6 +197,10 @@
return !!this.$slots.header
},
hasFooterSlot() {
return !!this.$slots.footer
},
/**
* Show the input field if a maxtags hasn't been set or reached.
*/
+27 -8
View File
@@ -11,7 +11,7 @@
ref="input"
slot="trigger"
autocomplete="off"
:value="formatValue(dateSelected)"
:value="formatValue(computedValue)"
:placeholder="placeholder"
:size="size"
:icon="icon"
@@ -21,9 +21,11 @@
:readonly="!editable"
:rounded="rounded"
v-bind="$attrs"
:use-html5-validation="useHtml5Validation"
@keyup.native.enter="toggle(true)"
@change.native="onChange($event.target.value)"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event) && checkHtml5Validity()"/>
@focus="handleOnFocus"
@blur="onBlur() && checkHtml5Validity()"/>
<b-dropdown-item :disabled="disabled" custom>
<b-field grouped position="is-centered">
@@ -54,6 +56,22 @@
{{ minute.label }}
</option>
</b-select>
<template v-if="enableSeconds">
<span class="control is-colon">:</span>
<b-select
v-model="secondsSelected"
@change.native="onSecondsChange($event.target.value)"
:disabled="disabled"
placeholder="00">
<option
v-for="second in seconds"
:value="second.value"
:key="second.value"
:disabled="isSecondDisabled(second.value)">
{{ second.label }}
</option>
</b-select>
</template>
<b-select
v-model="meridienSelected"
@change.native="onMeridienChange($event.target.value)"
@@ -81,7 +99,7 @@
ref="input"
type="time"
autocomplete="off"
:value="formatHHMMSS(value)"
:value="formatHHMMSS(computedValue)"
:placeholder="placeholder"
:size="size"
:icon="icon"
@@ -92,14 +110,15 @@
:disabled="disabled"
:readonly="false"
v-bind="$attrs"
@change.native="onChangeNativePicker"
@focus="$emit('focus', $event)"
@blur="$emit('blur', $event) && checkHtml5Validity()"/>
:use-html5-validation="useHtml5Validation"
@change.native="onChange($event.target.value)"
@focus="handleOnFocus"
@blur="onBlur() && checkHtml5Validity()"/>
</div>
</template>
<script>
import { default as TimepickerMixin } from '../../utils/TimepickerMixin'
import TimepickerMixin from '../../utils/TimepickerMixin'
import Dropdown from '../dropdown/Dropdown'
import DropdownItem from '../dropdown/DropdownItem'
import Input from '../input/Input'
+1
View File
@@ -27,6 +27,7 @@
:multiple="multiple"
:accept="accept"
:disabled="disabled"
:use-html5-validation="useHtml5Validation"
@change="onFileChange">
</label>
</template>
+1
View File
@@ -17,6 +17,7 @@
@import "components/_pagination";
@import "components/_radio";
@import "components/_select";
@import "components/_steps";
@import "components/_switch";
@import "components/_table";
@import "components/_tabs";
+1 -1
View File
@@ -70,7 +70,7 @@ $checkbox-checkmark-color: $primary-invert !default;
opacity: 0.5;
}
&:hover {
input[type=checkbox] + .check {
input[type=checkbox]:not(:disabled) + .check {
border-color: $checkbox-active-background-color;
@each $name, $pair in $colors {
$color: nth($pair, 1);
+9
View File
@@ -64,6 +64,15 @@
padding-bottom: 0;
}
.card {
border-radius: $radius;
}
.card-header {
border-top-left-radius: $radius;
border-top-right-radius: $radius;
}
.card-content {
padding: $padding-outer;
}
+35 -19
View File
@@ -13,7 +13,7 @@
border-bottom: 1px solid $grey-lighter;
}
.datepicker-content {
height: 16.25rem;
height: 16.25rem;
}
.datepicker-footer {
margin-top: 0.875rem;
@@ -41,27 +41,43 @@
display: table-row-group;
.datepicker-row {
display: table-row;
}
.datepicker-months {
display: inline-flex;
flex-wrap: wrap;
flex-direction: row;
width: 17rem;
.datepicker-cell {
&.is-unselectable {
color: $grey-light;
}
&.is-today {
border: solid 1px rgba($primary, 0.5);
}
&.is-selectable {
color: $grey-dark;
&:hover:not(.is-selected),
&:focus:not(.is-selected) {
background-color: $background;
color: $black;
cursor: pointer;
}
}
&.is-selected {
background-color: $primary;
color: $primary-invert;
display: flex;
align-items: center;
justify-content: center;
width: 33.33%;
height: 2.5rem;
}
}
.datepicker-cell {
&.is-unselectable {
color: $grey-light;
}
&.is-today {
border: solid 1px rgba($primary, 0.5);
}
&.is-selectable {
color: $grey-dark;
&:hover:not(.is-selected),
&:focus:not(.is-selected) {
background-color: $background;
color: $black;
cursor: pointer;
}
}
&.is-selected {
background-color: $primary;
color: $primary-invert;
}
&.is-nearby:not(.is-selected) {
color: $grey-light;
}
}
&.has-events {
.datepicker-cell {
+2 -1
View File
@@ -6,7 +6,7 @@
@include overlay;
position: fixed;
background-color: rgba($black, 0.86);
z-index: 10;
z-index: 40;
cursor: pointer;
@include desktop {
display: none;
@@ -89,6 +89,7 @@
transform: translate3d(-50%, -25%, 0);
white-space: normal;
overflow-y: auto;
z-index: 50;
.dropdown-item {
padding: 1rem 1.5rem;
}
+114 -3
View File
@@ -1,6 +1,4 @@
.label {
font-weight: $weight-semibold
}
$floating-in-height: 3.25em;
.field {
&.is-grouped {
@@ -43,6 +41,119 @@
}
}
.field {
&.is-floating-label {
position: relative;
.label {
position: absolute;
top: -0.775em;
left: 1em;
font-size: calc(#{$size-normal} * 3 / 4);
background-color: transparent;
padding-left: 0.125em;
padding-right: 0.125em;
z-index: 5;
&:before {
content: '';
display: block;
position: absolute;
top: 0.775em;
left: 0;
right: 0;
height: 0.375em;
background-color: #fff;
z-index: -1;
}
&.is-small {
font-size: calc(#{$size-small} * 3 / 4);
}
&.is-medium {
font-size: calc(#{$size-medium} * 3 / 4);
}
&.is-large {
font-size: calc(#{$size-large} * 3 / 4);
}
}
.input,
.textarea,
.select
select {
&:focus {
box-shadow: none;
}
}
.taginput .taginput-container.is-focusable {
padding-top: 0.475em;
&.is-focused {
box-shadow: none;
}
}
}
&.is-floating-in-label {
position: relative;
.label {
position: absolute;
top: 0.25em;
left: 1em;
font-size: calc(#{$size-normal} * 3 / 4);
background-color: transparent;
z-index: 5;
&.is-small {
font-size: calc(#{$size-small} * 3 / 4);
}
&.is-medium {
font-size: calc(#{$size-medium} * 3 / 4);
}
&.is-large {
font-size: calc(#{$size-large} * 3 / 4);
}
}
> .datepicker, > .timepicker {
.input {
padding-top: calc(#{$floating-in-height} / 2 - (#{$size-large} * 3 / 4) / 2);
padding-bottom: 1px;
height: $floating-in-height;
}
}
> :not(.datepicker):not(.timepicker):not(.taginput) {
.input,
.textarea,
select {
padding-top: calc(#{$floating-in-height} / 2 - (#{$size-large} * 3 / 4) / 2);
padding-bottom: 1px;
height: $floating-in-height;
}
.select:not(multiple) {
height: $floating-in-height;
&.is-loading {
&::after {
margin-top: calc(#{$floating-in-height} / 2 - (#{$size-large} * 3 / 4) / 2);
}
}
&::after {
margin-top: 1px;
}
}
}
> :not(.taginput) {
.is-left.icon,
.is-right.icon {
height: $floating-in-height;
}
.is-left.icon {
padding-top: calc(#{$floating-in-height} / 2 - (#{$size-large} * 3 / 4) / 2);
}
}
.control.is-loading::after {
margin-top: calc(#{$floating-in-height} / 2 - (#{$size-large} * 3 / 4) / 2);
}
.taginput .taginput-container.is-focusable {
// 0.275em - 1px _taginput.scss
padding-top: calc(#{$floating-in-height} / 2 - (#{$size-large} * 3 / 4) / 2 + (0.275em - 1px));
}
}
}
.control {
.help.counter {
float: right;
+10
View File
@@ -1,4 +1,14 @@
.modal {
&.is-full-screen {
.animation-content,
.modal-card {
width: 100%;
height: 100%;
max-height: 100vh;
margin: 0;
background-color: #ffffff;
}
}
.animation-content {
margin: 0 20px;
.modal-card {
+4 -6
View File
@@ -2,11 +2,9 @@
&.field {
margin-bottom: 0;
&.is-grouped {
@include mobile {
div.control {
flex-grow: 1;
flex-shrink: 1;
}
div.control {
flex-grow: 1;
flex-shrink: 1;
}
}
}
@@ -26,4 +24,4 @@
padding-right: 0.75em;
}
}
}
}
+21 -14
View File
@@ -1,4 +1,5 @@
$radio-active-background-color: $primary !default;
$radio-size: 1.25em !default;
.b-radio {
&.radio {
@@ -16,23 +17,29 @@ $radio-active-background-color: $primary !default;
outline: none;
z-index: -1;
+ .check {
display: flex;
align-items: center;
justify-content: center;
display: flex;
flex-shrink: 0;
width: 1.25em;
height: 1.25em;
border: 2px solid $grey;
border-radius: 50%;
position: relative;
cursor: pointer;
width: $radio-size;
height: $radio-size;
transition: background $speed-slow $easing;
border-radius: 50%;
border: 2px solid $grey;
&:before {
content: "";
border-radius: 50%;
width: 0.625em;
height: 0.625em;
background: $radio-active-background-color;
display: flex;
position: absolute;
left: 50%;
margin-left: calc(-#{$radio-size}/2);
bottom: 50%;
margin-bottom: calc(-#{$radio-size}/2);
width: $radio-size;
height: $radio-size;
transition: transform $speed-slow $easing;
border-radius: 50%;
transform: scale(0);
transition: transform $speed-slow $easing;
background-color: $radio-active-background-color;
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
@@ -50,7 +57,7 @@ $radio-active-background-color: $primary !default;
}
}
&:before {
transform: scale(1);
transform: scale(.5);
}
}
}
@@ -61,7 +68,7 @@ $radio-active-background-color: $primary !default;
opacity: 0.5;
}
&:hover {
input[type=radio] + .check {
input[type=radio]:not(:disabled) + .check {
border-color: $radio-active-background-color;
@each $name, $pair in $colors {
$color: nth($pair, 1);
+241
View File
@@ -0,0 +1,241 @@
/*
This project is based on
"bulma-steps" (https://github.com/Wikiki/bulma-steps) by
Wikiki (https://github.com/Wikiki) licensed under
MIT (https://github.com/Wikiki/bulma-steps/blob/master/LICENSE)
*/
$steps-maker-default-color: $grey-light !default;
$steps-marker-default-border: .2em solid #fff !default;
$steps-default-color: $grey-lighter !default;
$steps-completed-color: $primary !default;
$steps-active-color: $primary !default;
$steps-divider-height: .2em !default;
@mixin steps-size($size) {
font-size: $size;
min-height: $size * 2;
.step-items {
.step-item {
.step-marker {
height: $size * 2;
width: $size * 2;
position: absolute;
left: calc(50% - #{$size});
.icon {
*, *:before {
font-size: $size;
}
}
}
.step-details {
margin-top: $size * 2;
margin-left: .5em;
margin-right: .5em;
padding-top: .2em;
.step-title {
font-size: $size * 1.2;
font-weight: $weight-semibold;
}
}
&:not(:first-child) {
&::before {
height: $steps-divider-height;
width: 100%;
bottom: 0;
left: -50%;
top: #{$size};
}
}
}
}
}
.b-steps {
.steps {
.step-items {
display: flex;
flex-wrap: wrap;
.step-item {
margin-top: 0;
position: relative;
flex-grow: 1;
flex-basis: 0;
.step-link {
color: $body-color;
&:not(.is-clickable) {
cursor: not-allowed;
}
}
.step-marker {
align-items: center;
display: flex;
border-radius: 50%;
font-weight: $weight-bold;
justify-content: center;
background: $steps-maker-default-color;
color: $white;
border: $steps-marker-default-border;
z-index: 1;
}
// Override marker color per step
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
&::before {
background: linear-gradient(to left, $steps-default-color 50%, $color 50%);
background-size: 200% 100%;
background-position: right bottom;
}
&.is-active {
.step-marker {
background-color: $white;
border-color: $color;
color: $color;
}
&::before {
background-position: left bottom;
}
}
&.is-completed {
.step-marker {
color: $color-invert;
background-color: $color;
}
&::before {
background-position: left bottom;
}
}
}
}
.step-marker{
color: $white;
}
.step-details {
text-align: center;
}
&:not(:first-child) {
flex-basis: 1em;
flex-grow: 1;
flex-shrink: 1;
&::before {
// This will contain the divider
content: " ";
display: block;
position: absolute;
}
}
&::before {
background: linear-gradient(to left, $steps-default-color 50%, $steps-active-color 50%);
background-size: 200% 100%;
background-position: right bottom;
}
&.is-active {
.step-link {
cursor: default;
}
.step-marker {
background-color: $white;
border-color: $steps-active-color;
color: $steps-active-color;
}
&::before {
background-position: left bottom;
}
}
&.is-completed {
.step-marker {
color: $white;
background-color: $steps-completed-color;
}
&::before {
background-position: left bottom;
}
}
}
}
+ .step-content {
position: relative;
overflow: visible;
display: flex;
flex-direction: column;
padding: 1rem;
.step-item {
flex-shrink: 0;
flex-basis: auto;
}
&.is-transitioning {
overflow: hidden;
}
}
@include steps-size($size-normal);
&.is-small {
@include steps-size($size-small);
}
&.is-medium {
@include steps-size($size-medium);
}
&.is-large {
@include steps-size($size-large);
}
}
}
/*.b-tabs {
.tabs {
margin-bottom: 0;
flex-shrink: 0;
li {
&.is-disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.5;
}
}
}
.tab-content {
position: relative;
overflow: visible;
display: flex;
flex-direction: column;
padding: 1rem;
.tab-item {
flex-shrink: 0;
flex-basis: auto;
}
&.is-transitioning {
overflow: hidden;
}
}
&:not(:last-child) {
margin-bottom: 1.5rem;
}
&.is-fullwidth {
width: 100%;
}
}*/
+7 -5
View File
@@ -2,11 +2,13 @@
.tabs {
margin-bottom: 0;
flex-shrink: 0;
}
.is-disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.5;
li {
&.is-disabled {
pointer-events: none;
cursor: not-allowed;
opacity: 0.5;
}
}
}
.tab-content {
position: relative;
+2 -1
View File
@@ -6,6 +6,7 @@ $taginput-height: 1.7em;
@extend .input;
padding-bottom: 0;
padding-top: calc(0.275em - 1px);
padding-left: 0;
align-items: center;
display: flex;
flex-wrap: wrap;
@@ -25,7 +26,7 @@ $taginput-height: 1.7em;
height: $taginput-height;
}
&:not(:last-child) {
margin-right: 0.275rem;
margin-left: 0.275rem;
}
}
+1
View File
@@ -11,3 +11,4 @@ $link-invert: $primary-invert !default;
$link-visited: $grey !default;
$link-focus-border: $primary !default;
$label-weight: $weight-semibold !default;
+3 -2
View File
@@ -14,7 +14,8 @@ export default {
useHtml5Validation: {
type: Boolean,
default: () => config.defaultUseHtml5Validation
}
},
validationMessage: String
},
data() {
return {
@@ -117,7 +118,7 @@ export default {
let isValid = true
if (!el.checkValidity()) {
type = 'is-danger'
message = el.validationMessage
message = this.validationMessage || el.validationMessage
isValid = false
}
this.isValid = isValid
+250 -127
View File
@@ -7,6 +7,62 @@ const PM = 'PM'
const HOUR_FORMAT_24 = '24'
const HOUR_FORMAT_12 = '12'
const defaultTimeFormatter = (date, vm) => {
let hours = date.getHours()
const minutes = date.getMinutes()
const seconds = date.getSeconds()
let period = ''
if (vm.hourFormat === HOUR_FORMAT_12) {
period = ' ' + (hours < 12 ? AM : PM)
if (hours > 12) {
hours -= 12
} else if (hours === 0) {
hours = 12
}
}
return vm.pad(hours) + ':' + vm.pad(minutes) +
(vm.enableSeconds ? (':' + vm.pad(seconds)) : '') + period
}
const defaultTimeParser = (timeString, vm) => {
if (timeString) {
let am = false
if (vm.hourFormat === HOUR_FORMAT_12) {
const dateString12 = timeString.split(' ')
timeString = dateString12[0]
am = dateString12[1] === AM
}
const time = timeString.split(':')
let hours = parseInt(time[0], 10)
const minutes = parseInt(time[1], 10)
const seconds = vm.enableSeconds ? parseInt(time[2], 10) : 0
if (isNaN(hours) || hours < 0 || hours > 23 ||
(vm.hourFormat === HOUR_FORMAT_12 && (hours < 1 || hours > 12)) ||
isNaN(minutes) || minutes < 0 || minutes > 59) {
return null
}
let d = null
if (vm.computedValue && !isNaN(vm.computedValue)) {
d = new Date(vm.computedValue)
} else {
d = new Date()
d.setMilliseconds(0)
}
d.setSeconds(seconds)
d.setMinutes(minutes)
if (this.hourFormat === HOUR_FORMAT_12) {
if (am && hours === 12) {
hours = 0
} else if (!am && hours !== 12) {
hours += 12
}
}
d.setHours(hours)
return new Date(d.geTime())
}
return null
}
export default {
mixins: [FormElementMixin],
inheritAttrs: false,
@@ -29,16 +85,28 @@ export default {
type: Number,
default: 1
},
incrementSeconds: {
type: Number,
default: 1
},
timeFormatter: {
type: Function,
default: (date) => {
this.formatTime(date)
default: (date, vm) => {
if (typeof config.defaultTimeFormatter === 'function') {
return config.defaultTimeFormatter(date)
} else {
return defaultTimeFormatter(date, vm)
}
}
},
timeParser: {
type: Function,
default: (date) => {
this.parseTime(date)
default: (date, vm) => {
if (typeof config.defaultTimeParser === 'function') {
return config.defaultTimeParser(date)
} else {
return defaultTimeParser(date, vm)
}
}
},
mobileNative: {
@@ -48,13 +116,16 @@ export default {
}
},
position: String,
unselectableTimes: Array
unselectableTimes: Array,
openOnFocus: Boolean,
enableSeconds: Boolean
},
data() {
return {
dateSelected: this.value,
hoursSelected: null,
minutesSelected: null,
secondsSelected: null,
meridienSelected: null,
_elementRef: 'input',
AM,
@@ -64,6 +135,15 @@ export default {
}
},
computed: {
computedValue: {
get() {
return this.dateSelected
},
set(value) {
this.dateSelected = value
this.$emit('input', value)
}
},
hours() {
const hours = []
const numberOfHours = this.isHourFormat24 ? 24 : 12
@@ -101,6 +181,18 @@ export default {
}
return minutes
},
seconds() {
const seconds = []
for (let i = 0; i < 60; i += this.incrementSeconds) {
seconds.push({
label: this.formatNumber(i),
value: i
})
}
return seconds
},
meridiens() {
return [AM, PM]
},
@@ -114,30 +206,22 @@ export default {
}
},
watch: {
hourFormat(value) {
hourFormat() {
if (this.hoursSelected !== null) {
this.meridienSelected = this.hoursSelected >= 12 ? PM : AM
}
},
/**
* Emit input event with selected date as payload.
*/
dateSelected(value) {
this.$emit('input', value)
},
},
/**
* When v-model is changed:
* 1. Update internal value.
* 2. If it's invalid, validate again.
*/
value(value) {
this.updateInternalState(value)
this.dateSelected = value
!this.isValid && this.$refs.input.checkHtml5Validity()
value: {
handler(value) {
this.updateInternalState(value)
!this.isValid && this.$refs.input.checkHtml5Validity()
},
immediate: true
}
},
methods: {
@@ -149,13 +233,18 @@ export default {
this.hoursSelected -= 12
}
}
this.updateDateSelected(this.hoursSelected, this.minutesSelected, value)
this.updateDateSelected(
this.hoursSelected,
this.minutesSelected,
this.enableSeconds ? this.secondsSelected : 0,
value)
},
onHoursChange(value) {
this.updateDateSelected(
parseInt(value, 10),
this.minutesSelected,
this.enableSeconds ? this.secondsSelected : 0,
this.meridienSelected
)
},
@@ -164,22 +253,34 @@ export default {
this.updateDateSelected(
this.hoursSelected,
parseInt(value, 10),
this.enableSeconds ? this.secondsSelected : 0,
this.meridienSelected
)
},
onSecondsChange(value) {
this.updateDateSelected(
this.hoursSelected,
this.minutesSelected,
parseInt(value, 10),
this.meridienSelected
)
},
updateDateSelected(hours, minutes, meridiens) {
updateDateSelected(hours, minutes, seconds, meridiens) {
if (hours != null && minutes != null &&
((!this.isHourFormat24 && meridiens !== null) || this.isHourFormat24)) {
if (this.dateSelected && !isNaN(this.dateSelected)) {
this.dateSelected = new Date(this.dateSelected)
let time = null
if (this.computedValue && !isNaN(this.computedValue)) {
time = new Date(this.computedValue)
} else {
this.dateSelected = new Date()
this.dateSelected.setMilliseconds(0)
this.dateSelected.setSeconds(0)
time = new Date()
time.setMilliseconds(0)
}
this.dateSelected.setHours(hours)
this.dateSelected.setMinutes(minutes)
time.setHours(hours)
time.setMinutes(minutes)
time.setSeconds(seconds)
this.computedValue = new Date(time.getTime())
}
},
@@ -187,12 +288,15 @@ export default {
if (value) {
this.hoursSelected = value.getHours()
this.minutesSelected = value.getMinutes()
this.secondsSelected = value.getSeconds()
this.meridienSelected = value.getHours() >= 12 ? PM : AM
} else {
this.hoursSelected = null
this.minutesSelected = null
this.secondsSelected = null
this.meridienSelected = AM
}
this.dateSelected = value
},
isHourDisabled(hour) {
@@ -209,18 +313,19 @@ export default {
}
if (this.unselectableTimes) {
if (!disabled) {
if (this.minutesSelected !== null) {
const unselectable = this.unselectableTimes.filter((time) => {
const unselectable = this.unselectableTimes.filter((time) => {
if (this.enableSeconds && this.secondsSelected !== null) {
return time.getHours() === hour &&
time.getMinutes() === this.minutesSelected &&
time.getSeconds() === this.secondsSelected
} else if (this.minutesSelected !== null) {
return time.getHours() === hour &&
time.getMinutes() === this.minutesSelected
})
disabled = unselectable.length > 0
} else {
const unselectable = this.unselectableTimes.filter((time) => {
} else {
return time.getHours() === hour
})
disabled = unselectable.length === this.minutes.length
}
}
})
disabled = unselectable.length > 0
}
}
return disabled
@@ -240,8 +345,52 @@ export default {
if (this.maxTime) {
if (!disabled) {
const maxHours = this.maxTime.getHours()
const minMinutes = this.maxTime.getMinutes()
disabled = this.hoursSelected === maxHours && minute > minMinutes
const maxMinutes = this.maxTime.getMinutes()
disabled = this.hoursSelected === maxHours && minute > maxMinutes
}
}
}
if (this.unselectableTimes) {
if (!disabled) {
const unselectable = this.unselectableTimes.filter((time) => {
if (this.enableSeconds && this.secondsSelected !== null) {
return time.getHours() === this.hoursSelected &&
time.getMinutes() === minute &&
time.getSeconds() === this.secondsSelected
} else {
return time.getHours() === this.hoursSelected &&
time.getMinutes() === minute
}
})
disabled = unselectable.length > 0
}
}
}
return disabled
},
isSecondDisabled(second) {
let disabled = false
if (this.minutesSelected !== null) {
if (this.isMinuteDisabled(this.minutesSelected)) {
disabled = true
} else {
if (this.minTime) {
const minHours = this.minTime.getHours()
const minMinutes = this.minTime.getMinutes()
const minSeconds = this.minTime.getSeconds()
disabled = this.hoursSelected === minHours &&
this.minutesSelected === minMinutes &&
second < minSeconds
}
if (this.maxTime) {
if (!disabled) {
const maxHours = this.maxTime.getHours()
const maxMinutes = this.maxTime.getMinutes()
const maxSeconds = this.maxTime.getSeconds()
disabled = this.hoursSelected === maxHours &&
this.minutesSelected === maxMinutes &&
second > maxSeconds
}
}
}
@@ -249,7 +398,8 @@ export default {
if (!disabled) {
const unselectable = this.unselectableTimes.filter((time) => {
return time.getHours() === this.hoursSelected &&
time.getMinutes() === minute
time.getMinutes() === this.minutesSelected &&
time.getSeconds() === second
})
disabled = unselectable.length > 0
}
@@ -262,23 +412,42 @@ export default {
* Parse string into date
*/
onChange(value) {
const date = this.parseTime(value)
const date = this.timeParser(value, this)
this.updateInternalState(date)
if (date && !isNaN(date)) {
this.dateSelected = date
this.computedValue = date
} else {
// Force refresh input value when not valid date
this.dateSelected = null
this.$refs.input.newValue = this.dateSelected
this.computedValue = null
this.$refs.input.newValue = this.computedValue
}
},
/*
* Close dropdown time picker
* Toggle timepicker
*/
close() {
toggle(active) {
if (this.$refs.dropdown) {
this.$refs.dropdown.isActive = false
this.$refs.dropdown.isActive = typeof active === 'boolean'
? active
: !this.$refs.dropdown.isActive
}
},
/*
* Close timepicker
*/
close() {
this.toggle(false)
},
/*
* Call default onFocus method and show timepicker
*/
handleOnFocus() {
this.onFocus()
if (this.openOnFocus) {
this.toggle(true)
}
},
@@ -290,8 +459,10 @@ export default {
if (value && !isNaN(date)) {
const hours = date.getHours()
const minutes = date.getMinutes()
const seconds = date.getSeconds()
return this.formatNumber(hours) + ':' +
this.formatNumber(minutes, true) + ':00'
this.formatNumber(minutes, true) + ':' +
this.formatNumber(seconds, true)
}
return ''
},
@@ -302,18 +473,20 @@ export default {
onChangeNativePicker(event) {
const date = event.target.value
if (date) {
if (this.dateSelected && !isNaN(this.dateSelected)) {
this.dateSelected = new Date(this.dateSelected)
let time = null
if (this.computedValue && !isNaN(this.computedValue)) {
time = new Date(this.computedValue)
} else {
this.dateSelected = new Date()
this.dateSelected.setMilliseconds(0)
this.dateSelected.setSeconds(0)
time = new Date()
time.setMilliseconds(0)
}
const time = date.split(':')
this.dateSelected.setHours(parseInt(time[0], 10))
this.dateSelected.setMinutes(parseInt(time[1], 10))
const t = date.split(':')
time.setHours(parseInt(t[0], 10))
time.setMinutes(parseInt(t[1], 10))
time.setSeconds(t[2] ? parseInt(t[2], 10) : 0)
this.computedValue = new Date(time.getTime())
} else {
this.dateSelected = null
this.computedValue = null
}
},
@@ -332,79 +505,29 @@ export default {
*/
formatValue(date) {
if (date && !isNaN(date)) {
return this.formatTime(date)
return this.timeFormatter(date, this)
} else {
return null
}
},
formatTime(date) {
if (typeof config.defaultTimeFormatter === 'function') {
return config.defaultTimeFormatter(date)
} else {
return this.defaultTimeFormatter(date)
}
},
parseTime(date) {
if (typeof config.defaultTimeParser === 'function') {
return config.defaultTimeParser(date)
} else {
return this.defaultTimeParser(date)
}
},
defaultTimeFormatter(date) {
let hours = date.getHours()
const minutes = date.getMinutes()
let period = ''
if (this.hourFormat === HOUR_FORMAT_12) {
period = ' ' + (hours < 12 ? AM : PM)
if (hours > 12) {
hours -= 12
} else if (hours === 0) {
hours = 12
}
}
return this.pad(hours) + ':' + this.pad(minutes) + period
},
defaultTimeParser(timeString) {
if (timeString) {
let am = false
if (this.hourFormat === HOUR_FORMAT_12) {
const dateString12 = timeString.split(' ')
timeString = dateString12[0]
am = dateString12[1] === AM
}
const time = timeString.split(':')
let hours = parseInt(time[0], 10)
const minutes = parseInt(time[1], 10)
if (isNaN(hours) || hours < 0 || hours > 23 ||
(this.hourFormat === HOUR_FORMAT_12 && (hours < 1 || hours > 12)) ||
isNaN(minutes) || minutes < 0 || minutes > 59) {
return null
}
let d = null
if (this.dateSelected && !isNaN(this.dateSelected)) {
d = new Date(this.dateSelected)
} else {
d = new Date()
d.setMilliseconds(0)
d.setSeconds(0)
}
d.setMinutes(minutes)
if (this.hourFormat === HOUR_FORMAT_12) {
if (am && hours === 12) {
hours = 0
} else if (!am && hours !== 12) {
hours += 12
}
}
d.setHours(hours)
return d
}
return null
/**
* Keypress event that is bound to the document.
*/
keyPress(event) {
// Esc key
if (this.$refs.dropdown && this.$refs.dropdown.isActive && event.keyCode === 27) {
this.toggle(false)
}
}
},
mounted() {
this.updateInternalState(this.value)
created() {
if (typeof window !== 'undefined') {
document.addEventListener('keyup', this.keyPress)
}
},
beforeDestroy() {
if (typeof window !== 'undefined') {
document.removeEventListener('keyup', this.keyPress)
}
}
}
+8 -3
View File
@@ -8,7 +8,7 @@ let config = {
defaultSnackbarPosition: null,
defaultToastDuration: 2000,
defaultToastPosition: null,
detaultNotificationDuration: 2000,
defaultNotificationDuration: 2000,
defaultNotificationPosition: null,
defaultTooltipType: 'is-primary',
defaultTooltipAnimated: false,
@@ -22,13 +22,18 @@ let config = {
defaultUnselectableDaysOfWeek: null,
defaultTimeFormatter: null,
defaultTimeParser: null,
defaultModalCanCancel: null,
defaultModalCanCancel: ['escape', 'x', 'outside', 'button'],
defaultModalScroll: null,
defaultDatepickerMobileNative: true,
defaultTimepickerMobileNative: true,
defaultNoticeQueue: true,
defaultInputHasCounter: true,
defaultUseHtml5Validation: true
defaultUseHtml5Validation: true,
defaultDropdownMobileModal: true,
defaultFielLabelPosition: null,
defaultDatepickerYearsRange: [-100, 3],
defaultDatepickerNearbyMonthDays: true,
defaultDatepickerNearbySelectableMonthDays: false
}
export default config