Update Node_modules
This commit is contained in:
+13
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -19,7 +19,7 @@
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newDuration: this.duration || config.detaultNotificationDuration
|
||||
newDuration: this.duration || config.defaultNotificationDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-29
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -27,6 +27,7 @@
|
||||
:multiple="multiple"
|
||||
:accept="accept"
|
||||
:disabled="disabled"
|
||||
:use-html5-validation="useHtml5Validation"
|
||||
@change="onFileChange">
|
||||
</label>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user