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>