mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-29 20:51:09 -04:00
build version 400
This commit is contained in:
157
resources/assets/js/components/GlobalSearch.vue
Normal file
157
resources/assets/js/components/GlobalSearch.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div>
|
||||
<sw-dropdown :is-show="isShow" variant="search-dropdown">
|
||||
<sw-input
|
||||
slot="activator"
|
||||
v-model="name"
|
||||
:placeholder="$t('global_search.search')"
|
||||
variant="search-input"
|
||||
@input="throttledMethod"
|
||||
>
|
||||
<search-icon slot="leftIcon" class="h-5 m-1 text-gray-500" />
|
||||
<loading-icon
|
||||
slot="rightIcon"
|
||||
v-show="isLoading"
|
||||
class="absolute right-0 h-5 m-1 animate-spin text-primary-400"
|
||||
/>
|
||||
</sw-input>
|
||||
|
||||
<div class="w-64 h-40 overflow-y-scroll box">
|
||||
<div v-if="getCustomerList.length > 0 && !isLoading">
|
||||
<label class="text-xs text-gray-400 uppercase">
|
||||
{{ $t('global_search.customers') }}
|
||||
</label>
|
||||
<router-link
|
||||
v-for="d in getCustomerList"
|
||||
:key="d.id"
|
||||
:to="`/admin/customers/${d.id}/view`"
|
||||
>
|
||||
<sw-dropdown-item>
|
||||
<span
|
||||
class="flex items-center justify-center w-8 h-8 mr-4 text-xs font-semibold bg-gray-300 rounded-full text-primary-500"
|
||||
>
|
||||
{{ initGenerator(d.name) }}
|
||||
</span>
|
||||
|
||||
<div v-if="d.contact_name" class="flex flex-col">
|
||||
<span class="text-sm text-black">{{ d.name }}</span>
|
||||
|
||||
<span class="text-xs text-gray-500">{{ d.contact_name }}</span>
|
||||
</div>
|
||||
<div v-else class="flex items-center">
|
||||
<span class="text-sm text-black">{{ d.name }}</span>
|
||||
</div>
|
||||
</sw-dropdown-item>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div v-if="getUserList.length > 0 && !isLoading">
|
||||
<label class="text-xs text-gray-400 uppercase">{{
|
||||
$t('global_search.users')
|
||||
}}</label>
|
||||
<router-link
|
||||
v-for="d in getUserList"
|
||||
:key="d.id"
|
||||
:to="`/admin/users/${d.id}/edit`"
|
||||
>
|
||||
<sw-dropdown-item>
|
||||
<span
|
||||
class="flex items-center justify-center w-8 h-8 mr-4 text-xs font-semibold bg-gray-300 rounded-full text-primary-500"
|
||||
>
|
||||
{{ initGenerator(d.name) }}
|
||||
</span>
|
||||
|
||||
<div class="flex items-center">
|
||||
<span class="text-sm text-black">{{ d.name }}</span>
|
||||
</div>
|
||||
</sw-dropdown-item>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
getUserList.length === 0 &&
|
||||
getCustomerList.length === 0 &&
|
||||
!isLoading
|
||||
"
|
||||
>
|
||||
<span
|
||||
class="flex items-center justify-center text-sm font-normal text-gray-500"
|
||||
>
|
||||
{{ $t('global_search.no_results_found') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</sw-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SearchIcon } from '@vue-hero-icons/solid'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import LoadingIcon from '../components/icon/LoadingIcon'
|
||||
import _ from 'lodash'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SearchIcon,
|
||||
LoadingIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
isLoading: false,
|
||||
name: '',
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters('search', ['getCustomerList', 'getUserList']),
|
||||
},
|
||||
|
||||
created() {
|
||||
this.searchUsers()
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions('search', ['searchUsers']),
|
||||
|
||||
throttledMethod: _.debounce(async function () {
|
||||
this.isLoading = true
|
||||
await this.searchUsers({ search: this.name }).then(() => {
|
||||
this.isShow = true
|
||||
})
|
||||
if (this.name === '') {
|
||||
this.isShow = false
|
||||
}
|
||||
this.isLoading = false
|
||||
}, 500),
|
||||
|
||||
initGenerator(name) {
|
||||
if (name) {
|
||||
let nameSplit = name.split('')
|
||||
let initials =
|
||||
nameSplit[0].charAt(0).toUpperCase() +
|
||||
nameSplit[1].charAt(0).toUpperCase()
|
||||
return initials
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.box::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.box::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
outline: 1px solid white;
|
||||
border-radius: 0.42rem !important;
|
||||
}
|
||||
|
||||
.box::-webkit-scrollbar-thumb {
|
||||
background-color: #e4e6ef;
|
||||
}
|
||||
</style>
|
||||
@ -1,195 +0,0 @@
|
||||
<template>
|
||||
<button
|
||||
:type="type"
|
||||
:class="btnClass"
|
||||
:disabled="disabled || loading"
|
||||
@click="handleClick"
|
||||
>
|
||||
<font-awesome-icon
|
||||
v-if="icon && !loading && !rightIcon"
|
||||
:class="iconClass"
|
||||
:icon="icon"
|
||||
class="vue-icon icon-left"
|
||||
/>
|
||||
<font-awesome-icon
|
||||
v-if="loading"
|
||||
:class="iconClass"
|
||||
icon="spinner"
|
||||
class="fa-spin"
|
||||
/>
|
||||
<slot />
|
||||
<font-awesome-icon
|
||||
v-if="icon && !loading && rightIcon"
|
||||
:class="iconClass"
|
||||
:icon="icon"
|
||||
class="vue-icon icon-right"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
round: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
outline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'default'
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
block: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
iconButton: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
rightIcon: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'button'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
btnClass () {
|
||||
if (this.isCustomStyle) {
|
||||
return ''
|
||||
}
|
||||
|
||||
let btnClass = 'base-button '
|
||||
|
||||
switch (this.color) {
|
||||
case 'success':
|
||||
if (this.outline) {
|
||||
btnClass += `btn btn-outline-success `
|
||||
} else {
|
||||
btnClass += `btn btn-success `
|
||||
}
|
||||
break
|
||||
|
||||
case 'danger':
|
||||
if (this.outline) {
|
||||
btnClass += `btn btn-outline-danger `
|
||||
} else {
|
||||
btnClass += `btn btn-danger `
|
||||
}
|
||||
break
|
||||
|
||||
case 'warning':
|
||||
if (this.outline) {
|
||||
btnClass += `btn btn-outline-warning `
|
||||
} else {
|
||||
btnClass += `btn btn-warning `
|
||||
}
|
||||
break
|
||||
|
||||
case 'info':
|
||||
if (this.outline) {
|
||||
btnClass += `btn btn-outline-info `
|
||||
} else {
|
||||
btnClass += `btn btn-info `
|
||||
}
|
||||
break
|
||||
|
||||
case 'theme':
|
||||
if (this.outline) {
|
||||
btnClass += `btn btn-outline-primary `
|
||||
} else {
|
||||
btnClass += `btn btn-primary `
|
||||
}
|
||||
break
|
||||
|
||||
case 'theme-light':
|
||||
if (this.outline) {
|
||||
btnClass += `btn btn-outline-light `
|
||||
} else {
|
||||
btnClass += `btn btn-light `
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
if (this.outline) {
|
||||
btnClass += `btn btn-outline-dark `
|
||||
} else {
|
||||
btnClass += `btn btn-dark `
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
switch (this.size) {
|
||||
case 'large':
|
||||
btnClass += 'btn-lg '
|
||||
break
|
||||
|
||||
case 'small':
|
||||
btnClass += 'btn-sm '
|
||||
break
|
||||
|
||||
default:
|
||||
btnClass += 'default-size '
|
||||
}
|
||||
|
||||
if (this.block) {
|
||||
btnClass += 'btn-block '
|
||||
}
|
||||
|
||||
if (this.disabled) {
|
||||
btnClass += ' btn-cursor-not-allowed'
|
||||
}
|
||||
|
||||
return btnClass
|
||||
},
|
||||
iconClass () {
|
||||
if (this.loading || !this.iconButton) {
|
||||
if (this.rightIcon) {
|
||||
return 'ml-2'
|
||||
}
|
||||
return 'mr-2'
|
||||
}
|
||||
return 'icon-button'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick (e) {
|
||||
this.$emit('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
302
resources/assets/js/components/base/BaseCustomInput.vue
Normal file
302
resources/assets/js/components/base/BaseCustomInput.vue
Normal file
@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<div class="relative">
|
||||
<div class="absolute bottom-0 right-0 z-10">
|
||||
<sw-dropdown
|
||||
:close-on-select="true"
|
||||
max-height="220"
|
||||
position="bottom-end"
|
||||
class="mb-2"
|
||||
>
|
||||
<sw-button
|
||||
slot="activator"
|
||||
variant="primary-outline"
|
||||
type="button"
|
||||
class="mr-2"
|
||||
>
|
||||
<plus-sm-icon class="h-5 mr-1 -ml-2" />
|
||||
{{ $t('settings.customization.addresses.insert_fields') }}
|
||||
</sw-button>
|
||||
<div class="flex p-2">
|
||||
<ul v-for="(type, index) in fieldList" :key="index" class="list-none">
|
||||
<li class="mb-1 ml-2 text-xs font-semibold text-gray-500 uppercase">
|
||||
{{ type.label }}
|
||||
</li>
|
||||
<li
|
||||
v-for="(field, index) in type.fields"
|
||||
:key="index"
|
||||
class="w-48 text-sm font-normal cursor-pointer hover:bg-gray-200"
|
||||
@click="insertField(field.value)"
|
||||
>
|
||||
<div class="flex">
|
||||
<chevron-double-right-icon class="h-3 mt-1 text-gray-400" />{{
|
||||
field.label
|
||||
}}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</sw-dropdown>
|
||||
</div>
|
||||
<sw-editor
|
||||
v-model="inputValue"
|
||||
:set-editor="inputValue"
|
||||
:disabled="disabled"
|
||||
:invalid="isFieldValid"
|
||||
:placeholder="placeholder"
|
||||
variant="header-editor"
|
||||
input-class="border-none"
|
||||
class="text-area-field"
|
||||
@input="handleInput"
|
||||
@change="handleChange"
|
||||
@keyup="handleKeyupEnter"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PlusSmIcon } from '@vue-hero-icons/outline'
|
||||
import { ChevronDoubleRightIcon } from '@vue-hero-icons/solid'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import customFields from '../../mixins/customFields'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlusSmIcon,
|
||||
ChevronDoubleRightIcon,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number, File],
|
||||
default: '',
|
||||
},
|
||||
types: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rows: {
|
||||
type: String,
|
||||
default: '10',
|
||||
},
|
||||
cols: {
|
||||
type: String,
|
||||
default: '30',
|
||||
},
|
||||
invalid: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fieldList: [],
|
||||
invoiceFields: [],
|
||||
estimateFields: [],
|
||||
paymentFields: [],
|
||||
customerFields: [],
|
||||
position: null,
|
||||
inputValue: this.value,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('customFields', ['getCustomFields']),
|
||||
isFieldValid() {
|
||||
return this.invalid
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.inputValue = this.value
|
||||
},
|
||||
fields() {
|
||||
if (this.fields && this.fields.length > 0) {
|
||||
this.getFields()
|
||||
}
|
||||
},
|
||||
getCustomFields(newValue) {
|
||||
this.invoiceFields = newValue
|
||||
? newValue.filter((field) => field.model_type === 'Invoice')
|
||||
: []
|
||||
this.customerFields = newValue
|
||||
? newValue.filter((field) => field.model_type === 'Customer')
|
||||
: []
|
||||
this.paymentFields = newValue
|
||||
? newValue.filter((field) => field.model_type === 'Payment')
|
||||
: []
|
||||
this.estimateFields = newValue.filter(
|
||||
(field) => field.model_type === 'Estimate'
|
||||
)
|
||||
this.getFields()
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
this.getFields()
|
||||
await this.fetchNoteCustomFields({ limit: 'all' })
|
||||
},
|
||||
methods: {
|
||||
...mapActions('customFields', ['fetchNoteCustomFields']),
|
||||
|
||||
getFields() {
|
||||
this.fieldList = []
|
||||
|
||||
if (this.fields && this.fields.length > 0) {
|
||||
if (this.fields.find((field) => field == 'shipping')) {
|
||||
this.fieldList.push({
|
||||
label: 'Shipping Address',
|
||||
fields: [
|
||||
{ label: 'Address name', value: 'SHIPPING_ADDRESS_NAME' },
|
||||
{ label: 'Country', value: 'SHIPPING_COUNTRY' },
|
||||
{ label: 'State', value: 'SHIPPING_STATE' },
|
||||
{ label: 'City', value: 'SHIPPING_CITY' },
|
||||
{ label: 'Address Street 1', value: 'SHIPPING_ADDRESS_STREET_1' },
|
||||
{ label: 'Address Street 2', value: 'SHIPPING_ADDRESS_STREET_2' },
|
||||
{ label: 'Phone', value: 'SHIPPING_PHONE' },
|
||||
{ label: 'Zip Code', value: 'SHIPPING_ZIP_CODE' },
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
if (this.fields.find((field) => field == 'billing')) {
|
||||
this.fieldList.push({
|
||||
label: 'Billing Address',
|
||||
fields: [
|
||||
{ label: 'Address name', value: 'BILLING_ADDRESS_NAME' },
|
||||
{ label: 'Country', value: 'BILLING_COUNTRY' },
|
||||
{ label: 'State', value: 'BILLING_STATE' },
|
||||
{ label: 'City', value: 'BILLING_CITY' },
|
||||
{ label: 'Address Street 1', value: 'BILLING_ADDRESS_STREET_1' },
|
||||
{ label: 'Address Street 2', value: 'BILLING_ADDRESS_STREET_2' },
|
||||
{ label: 'Phone', value: 'BILLING_PHONE' },
|
||||
{ label: 'Zip Code', value: 'BILLING_ZIP_CODE' },
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
if (this.fields.find((field) => field == 'customer')) {
|
||||
this.fieldList.push({
|
||||
label: 'Customer',
|
||||
fields: [
|
||||
{ label: 'Display Name', value: 'CONTACT_DISPLAY_NAME' },
|
||||
{ label: 'Contact Name', value: 'PRIMARY_CONTACT_NAME' },
|
||||
{ label: 'Email', value: 'CONTACT_EMAIL' },
|
||||
{ label: 'Phone', value: 'CONTACT_PHONE' },
|
||||
{ label: 'Website', value: 'CONTACT_WEBSITE' },
|
||||
...this.customerFields.map((i) => ({
|
||||
label: i.label,
|
||||
value: i.slug,
|
||||
})),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
if (this.fields.find((field) => field == 'invoice')) {
|
||||
this.fieldList.push({
|
||||
label: 'Invoice',
|
||||
fields: [
|
||||
{ label: 'Date', value: 'INVOICE_DATE' },
|
||||
{ label: 'Due Date', value: 'INVOICE_DUE_DATE' },
|
||||
{ label: 'Number', value: 'INVOICE_NUMBER' },
|
||||
{ label: 'Ref Number', value: 'INVOICE_REF_NUMBER' },
|
||||
{ label: 'Invoice Link', value: 'INVOICE_LINK' },
|
||||
...this.invoiceFields.map((i) => ({
|
||||
label: i.label,
|
||||
value: i.slug,
|
||||
})),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
if (this.fields.find((field) => field == 'estimate')) {
|
||||
this.fieldList.push({
|
||||
label: 'Estimate',
|
||||
fields: [
|
||||
{ label: 'Date', value: 'ESTIMATE_DATE' },
|
||||
{ label: 'Expiry Date', value: 'ESTIMATE_EXPIRY_DATE' },
|
||||
{ label: 'Number', value: 'ESTIMATE_NUMBER' },
|
||||
{ label: 'Ref Number', value: 'ESTIMATE_REF_NUMBER' },
|
||||
{ label: 'Estimate Link', value: 'ESTIMATE_LINK' },
|
||||
...this.estimateFields.map((i) => ({
|
||||
label: i.label,
|
||||
value: i.slug,
|
||||
})),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
if (this.fields.find((field) => field == 'payment')) {
|
||||
this.fieldList.push({
|
||||
label: 'Payment',
|
||||
fields: [
|
||||
{ label: 'Date', value: 'PAYMENT_DATE' },
|
||||
{ label: 'Number', value: 'PAYMENT_NUMBER' },
|
||||
{ label: 'Mode', value: 'PAYMENT_MODE' },
|
||||
{ label: 'Amount', value: 'PAYMENT_AMOUNT' },
|
||||
{ label: 'Payment Link', value: 'PAYMENT_LINK' },
|
||||
...this.paymentFields.map((i) => ({
|
||||
label: i.label,
|
||||
value: i.slug,
|
||||
})),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
if (this.fields.find((field) => field == 'company')) {
|
||||
this.fieldList.push({
|
||||
label: 'Company',
|
||||
fields: [
|
||||
{ label: 'Company Name', value: 'COMPANY_NAME' },
|
||||
{ label: 'Country', value: 'COMPANY_COUNTRY' },
|
||||
{ label: 'State', value: 'COMPANY_STATE' },
|
||||
{ label: 'City', value: 'COMPANY_CITY' },
|
||||
{ label: 'Address Street 1', value: 'COMPANY_ADDRESS_STREET_1' },
|
||||
{ label: 'Address Street 2', value: 'COMPANY_ADDRESS_STREET_2' },
|
||||
{ label: 'Phone', value: 'COMPANY_PHONE' },
|
||||
{ label: 'Zip Code', value: 'COMPANY_ZIP_CODE' },
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
insertField(varName) {
|
||||
if (this.inputValue) {
|
||||
this.inputValue += `{${varName}}`
|
||||
} else {
|
||||
this.inputValue = `{${varName}}`
|
||||
}
|
||||
this.$emit('input', this.inputValue)
|
||||
},
|
||||
|
||||
handleInput(e) {
|
||||
this.$emit('input', this.inputValue)
|
||||
},
|
||||
|
||||
handleChange(e) {
|
||||
this.$emit('change', this.inputValue)
|
||||
},
|
||||
|
||||
handleKeyupEnter(e) {
|
||||
this.$emit('keyup', this.inputValue)
|
||||
},
|
||||
|
||||
handleKeyDownEnter(e) {
|
||||
this.$emit('keydown', e, this.inputValue)
|
||||
},
|
||||
|
||||
handleFocusOut(e) {
|
||||
this.$emit('blur', this.inputValue)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="item-selector">
|
||||
<base-select
|
||||
<sw-select
|
||||
ref="baseSelect"
|
||||
v-model="customerSelect"
|
||||
:options="customers"
|
||||
@ -21,40 +21,41 @@
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
customerSelect: null,
|
||||
loading: false
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('customer', [
|
||||
'customers'
|
||||
])
|
||||
...mapGetters('customer', ['customers']),
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fetchCustomers()
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions('customer', [
|
||||
'fetchCustomers'
|
||||
]),
|
||||
async searchCustomers (search) {
|
||||
...mapActions('customer', ['fetchCustomers']),
|
||||
async searchCustomers(search) {
|
||||
this.loading = true
|
||||
|
||||
await this.fetchCustomers({search})
|
||||
await this.fetchCustomers({ search })
|
||||
|
||||
this.loading = false
|
||||
},
|
||||
onTextChange (val) {
|
||||
onTextChange(val) {
|
||||
this.searchCustomers(val)
|
||||
},
|
||||
checkCustomers (val) {
|
||||
checkCustomers(val) {
|
||||
if (!this.customers.length) {
|
||||
this.fetchCustomers()
|
||||
}
|
||||
},
|
||||
deselectCustomer () {
|
||||
deselectCustomer() {
|
||||
this.customerSelect = null
|
||||
this.$emit('deselect')
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
142
resources/assets/js/components/base/BaseDatePicker.vue
Normal file
142
resources/assets/js/components/base/BaseDatePicker.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div>
|
||||
<sw-date-picker
|
||||
ref="BaseDatepicker"
|
||||
v-model="date"
|
||||
:config="config"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:invalid="invalid"
|
||||
:name="name"
|
||||
:tabindex="tabindex"
|
||||
@input="onDateChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import SwDatePicker from '@bytefury/spacewind/src/components/SwDatePicker'
|
||||
import moment from 'moment'
|
||||
const fromMomentDate = (date, format = 'YYYY-MM-DD') =>
|
||||
moment(new Date(date), format)
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SwDatePicker,
|
||||
},
|
||||
props: {
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
invalid: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
enableTime: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
time_24hr: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
value: {
|
||||
type: [String, Date],
|
||||
default: () => moment(new Date()),
|
||||
},
|
||||
tabindex: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
date: null,
|
||||
config: {
|
||||
altInput: true,
|
||||
enableTime: this.enableTime,
|
||||
time_24hr: this.time_24hr,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('company', {
|
||||
carbonFormat: 'getCarbonDateFormat',
|
||||
momentFormat: 'getMomentDateFormat',
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (val && !this.enableTime) {
|
||||
this.date = fromMomentDate(val, 'YYYY-MM-DD').format('YYYY-MM-DD')
|
||||
} else {
|
||||
this.date = fromMomentDate(val, 'YYYY-MM-DD').format('YYYY-MM-DD H:m:s')
|
||||
}
|
||||
},
|
||||
enableTime(val) {
|
||||
this.$set(this.config, 'enableTime', this.enableTime)
|
||||
},
|
||||
carbonFormat() {
|
||||
if (!this.enableTime) {
|
||||
this.$set(
|
||||
this.config,
|
||||
'altFormat',
|
||||
this.carbonFormat ? this.carbonFormat : 'd M Y'
|
||||
)
|
||||
} else {
|
||||
this.$set(
|
||||
this.config,
|
||||
'altFormat',
|
||||
this.carbonFormat ? `${this.carbonFormat} H:i ` : 'd M Y H:i'
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$set(this.config, 'enableTime', this.enableTime)
|
||||
if (!this.enableTime) {
|
||||
this.$set(
|
||||
this.config,
|
||||
'altFormat',
|
||||
this.carbonFormat ? this.carbonFormat : 'd M Y'
|
||||
)
|
||||
} else {
|
||||
this.$set(
|
||||
this.config,
|
||||
'altFormat',
|
||||
this.carbonFormat ? `${this.carbonFormat} H:i ` : 'd M Y H:i'
|
||||
)
|
||||
}
|
||||
if (this.value && !this.enableTime) {
|
||||
this.date = fromMomentDate(this.value, 'YYYY-MM-DD').format('YYYY-MM-DD')
|
||||
return true
|
||||
}
|
||||
if (this.value) {
|
||||
this.date = fromMomentDate(this.value, 'YYYY-MM-DD').format(
|
||||
'YYYY-MM-DD HH:mm:SS'
|
||||
)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDateChange(date) {
|
||||
this.$emit('input', date)
|
||||
this.$emit('change', date)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.flatpickr-calendar.open {
|
||||
z-index: 60 !important;
|
||||
}
|
||||
</style>
|
||||
@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<div class="base-input">
|
||||
<font-awesome-icon v-if="icon && isAlignLeftIcon" :icon="icon" class="left-icon"/>
|
||||
<input
|
||||
ref="baseInput"
|
||||
v-model="inputValue"
|
||||
:type="toggleType"
|
||||
:disabled="disabled"
|
||||
:readonly="readOnly"
|
||||
:name="name"
|
||||
:tabindex="tabIndex"
|
||||
:class="[{ 'input-field-left-icon': icon && isAlignLeftIcon, 'input-field-right-icon': (icon && !isAlignLeftIcon) || isInputGroup, invalid: isFieldValid, disabled: disabled, 'small-input': small}, inputClass]"
|
||||
:placeholder="placeholder"
|
||||
:autocomplete="autocomplete"
|
||||
class="input-field"
|
||||
@input="handleInput"
|
||||
@change="handleChange"
|
||||
@keyup="handleKeyupEnter"
|
||||
@keydown="handleKeyDownEnter"
|
||||
@blur="handleFocusOut"
|
||||
>
|
||||
<div v-if="showPassword && isAlignLeftIcon" style="cursor: pointer" @click="showPass = !showPass" >
|
||||
<font-awesome-icon :icon="!showPass ?'eye': 'eye-slash'" class="right-icon" />
|
||||
</div>
|
||||
<font-awesome-icon v-if="icon && !isAlignLeftIcon" :icon="icon" class="right-icon" />
|
||||
<span v-if="isInputGroup" class="right-input-group-text">
|
||||
{{ inputGroupText }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
tabIndex: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
value: {
|
||||
type: [String, Number, File],
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
invalid: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
inputClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
small: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
alignIcon: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
autocomplete: {
|
||||
type: String,
|
||||
default: 'on'
|
||||
},
|
||||
showPassword: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isInputGroup: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
inputGroupText: {
|
||||
type: String,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
focus: false,
|
||||
showPass: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isFieldValid () {
|
||||
return this.invalid
|
||||
},
|
||||
isAlignLeftIcon () {
|
||||
if (this.alignIcon === 'left') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
toggleType () {
|
||||
if (this.showPass) {
|
||||
return 'text'
|
||||
}
|
||||
return this.type
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value' () {
|
||||
this.inputValue = this.value
|
||||
},
|
||||
focus () {
|
||||
this.focusInput()
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.focusInput()
|
||||
},
|
||||
methods: {
|
||||
focusInput () {
|
||||
if (this.focus) {
|
||||
this.$refs.baseInput.focus()
|
||||
}
|
||||
},
|
||||
handleInput (e) {
|
||||
this.$emit('input', this.inputValue)
|
||||
},
|
||||
handleChange (e) {
|
||||
this.$emit('change', this.inputValue)
|
||||
},
|
||||
handleKeyupEnter (e) {
|
||||
this.$emit('keyup', this.inputValue)
|
||||
},
|
||||
handleKeyDownEnter (e) {
|
||||
this.$emit('keydown', e, this.inputValue)
|
||||
},
|
||||
handleFocusOut (e) {
|
||||
this.$emit('blur', this.inputValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -1,8 +1,12 @@
|
||||
<template>
|
||||
<div class="base-loader">
|
||||
<div class="spinner"/>
|
||||
<div class="overlay">
|
||||
<div class="loader-inner ball-scale-ripple-multiple">
|
||||
<div
|
||||
:class="{ 'bg-gray-400': showBgOverlay }"
|
||||
class="absolute top-0 left-0 z-20 flex items-center justify-center w-full h-full bg-opacity-25 base-loader"
|
||||
>
|
||||
<div class="absolute top-0 left-0 w-full h-full overlay">
|
||||
<div
|
||||
class="absolute flex items-center justify-center ball-scale-ripple-multiple"
|
||||
>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
@ -10,19 +14,110 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
showBgOverlay: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.overlay {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: rgba(255,255,255,0.4);
|
||||
position: absolute;
|
||||
top: 7%;
|
||||
left: 13%;
|
||||
<style lang="scss">
|
||||
// function.scss
|
||||
|
||||
@function delay($interval, $count, $index) {
|
||||
@return ($index * $interval) - ($interval * $count);
|
||||
}
|
||||
|
||||
// mixins.scss
|
||||
|
||||
@mixin global-bg() {
|
||||
background-color: #5851d8;
|
||||
}
|
||||
|
||||
@mixin global-animation() {
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@mixin balls() {
|
||||
@include global-bg();
|
||||
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 100%;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
@mixin lines() {
|
||||
@include global-bg();
|
||||
|
||||
width: $line-width;
|
||||
height: $line-height;
|
||||
border-radius: 2px;
|
||||
margin: $margin;
|
||||
}
|
||||
|
||||
.base-loader {
|
||||
.overlay {
|
||||
@keyframes ball-scale-ripple-multiple {
|
||||
0% {
|
||||
transform: scale(0.1);
|
||||
opacity: 1;
|
||||
}
|
||||
70% {
|
||||
transform: scale(1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ball-scale-ripple-multiple($n: 3, $start: 0) {
|
||||
@for $i from $start through $n {
|
||||
> div:nth-child(#{$i}) {
|
||||
animation-delay: delay(0.2s, $n, $i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.ball-scale-ripple-multiple {
|
||||
transform: translateY(-25px);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
@include ball-scale-ripple-multiple();
|
||||
transform: translateY(-50px / 2);
|
||||
|
||||
> div {
|
||||
@include global-animation();
|
||||
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -26px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 100%;
|
||||
border: 2px solid #5851d8;
|
||||
animation: ball-scale-ripple-multiple 1.25s 0s infinite
|
||||
cubic-bezier(0.21, 0.53, 0.56, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.table-loader .overlay {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
height: calc(100% - 80px);
|
||||
top: 80px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
7
resources/assets/js/components/base/BasePage.vue
Normal file
7
resources/assets/js/components/base/BasePage.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<main
|
||||
class="flex flex-col flex-1 p-8 overflow-y-auto border-b border-gray-300 border-solid"
|
||||
>
|
||||
<slot />
|
||||
</main>
|
||||
</template>
|
||||
@ -1,71 +0,0 @@
|
||||
<template>
|
||||
<div class="base-prefix-input" @click="focusInput">
|
||||
<font-awesome-icon v-if="icon" :icon="icon" class="icon" />
|
||||
<p class="prefix-label"><span class="mr-1">{{ prefix }}</span>-</p>
|
||||
<input
|
||||
ref="basePrefixInput"
|
||||
v-model="inputValue"
|
||||
:type="type"
|
||||
class="prefix-input-field"
|
||||
@input="handleInput"
|
||||
@change="handleChange"
|
||||
@keyup="handleKeyupEnter"
|
||||
@keydown="handleKeyDownEnter"
|
||||
@blur="handleFocusOut"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
prefix: {
|
||||
type: String,
|
||||
default: null,
|
||||
required: true
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
value: {
|
||||
type: [String, Number, File],
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
inputValue: this.value
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value' () {
|
||||
this.inputValue = this.value
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
focusInput () {
|
||||
this.$refs.basePrefixInput.focus()
|
||||
},
|
||||
handleInput (e) {
|
||||
this.$emit('input', this.inputValue)
|
||||
},
|
||||
handleChange (e) {
|
||||
this.$emit('change', this.inputValue)
|
||||
},
|
||||
handleKeyupEnter (e) {
|
||||
this.$emit('keyup', this.inputValue)
|
||||
},
|
||||
handleKeyDownEnter (e) {
|
||||
this.$emit('keydown', e, this.inputValue)
|
||||
},
|
||||
handleFocusOut (e) {
|
||||
this.$emit('blur', this.inputValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -1,66 +0,0 @@
|
||||
<template>
|
||||
<div class="base-switch">
|
||||
<input
|
||||
:id="uniqueId"
|
||||
v-model="checkValue"
|
||||
type="checkbox"
|
||||
@input="handleInput"
|
||||
@change="handleChange"
|
||||
@keyup="handleKeyupEnter"
|
||||
@blur="handleFocusOut"
|
||||
>
|
||||
<label class="switch-label" :for="uniqueId"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
id: null,
|
||||
checkValue: this.value
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
uniqueId () {
|
||||
return '_' + Math.random().toString(36).substr(2, 9)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value' () {
|
||||
this.checkValue = this.value
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput (e) {
|
||||
this.$emit('input', e.target.checked)
|
||||
},
|
||||
handleChange (e) {
|
||||
this.$emit('change', this.checkValue)
|
||||
},
|
||||
handleKeyupEnter (e) {
|
||||
this.$emit('keyup', this.checkValue)
|
||||
},
|
||||
handleFocusOut (e) {
|
||||
this.$emit('blur', this.checkValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* .switch-label {
|
||||
margin-bottom: 3px !important
|
||||
} */
|
||||
</style>
|
||||
@ -1,79 +0,0 @@
|
||||
<template>
|
||||
<textarea
|
||||
v-model="inputValue"
|
||||
:rows="rows"
|
||||
:cols="cols"
|
||||
:disabled="disabled"
|
||||
:class="['base-text-area',{'invalid': isFieldValid, 'disabled': disabled}, inputClass]"
|
||||
:placeholder="placeholder"
|
||||
class="text-area-field"
|
||||
@input="handleInput"
|
||||
@change="handleChange"
|
||||
@keyup="handleKeyupEnter"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
rows: {
|
||||
type: String,
|
||||
default: '4'
|
||||
},
|
||||
cols: {
|
||||
type: String,
|
||||
default: '10'
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
invalid: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
inputClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
inputValue: this.value
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isFieldValid () {
|
||||
return this.invalid
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value' () {
|
||||
this.inputValue = this.value
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleInput (e) {
|
||||
this.$emit('input', this.inputValue)
|
||||
},
|
||||
handleChange (e) {
|
||||
this.$emit('change', this.inputValue)
|
||||
},
|
||||
handleKeyupEnter (e) {
|
||||
this.$emit('keyup', this.inputValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
81
resources/assets/js/components/base/BaseTimePicker.vue
Normal file
81
resources/assets/js/components/base/BaseTimePicker.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<sw-date-picker
|
||||
ref="BaseDatepicker"
|
||||
v-model="time"
|
||||
:config="config"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:invalid="invalid"
|
||||
:name="name"
|
||||
:tabindex="tabindex"
|
||||
@input="onDateChange"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import SwDatePicker from '@bytefury/spacewind/src/components/SwDatePicker'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SwDatePicker,
|
||||
},
|
||||
props: {
|
||||
invalid: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
require: false,
|
||||
},
|
||||
defaultTime: {
|
||||
type: String,
|
||||
default: null,
|
||||
require: false,
|
||||
},
|
||||
hideClearButton: {
|
||||
type: String,
|
||||
default: null,
|
||||
require: false,
|
||||
},
|
||||
value: {
|
||||
type: [String, Object],
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'HH:mm:ss',
|
||||
},
|
||||
tabindex: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
time: this.value,
|
||||
config: {
|
||||
enableTime: true,
|
||||
noCalendar: true,
|
||||
dateFormat: 'H:i',
|
||||
time_24hr: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.time = this.value
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onDateChange(date) {
|
||||
this.$emit('input', date)
|
||||
this.$emit('change', date)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,649 +0,0 @@
|
||||
<template>
|
||||
<div :class="[wrapperClass, isRtl ? 'rtl' : '']" class="base-date-input">
|
||||
<date-input
|
||||
:selected-date="selectedDate"
|
||||
:reset-typed-date="resetTypedDate"
|
||||
:format="customFormatter"
|
||||
:translation="translation"
|
||||
:inline="inline"
|
||||
:id="id"
|
||||
:name="name"
|
||||
:ref-name="refName"
|
||||
:open-date="openDate"
|
||||
:placeholder="placeholder"
|
||||
:input-class="inputClass"
|
||||
:typeable="typeable"
|
||||
:clear-button="clearButton"
|
||||
:clear-button-icon="clearButtonIcon"
|
||||
:calendar-button="calendarButton"
|
||||
:calendar-button-icon="calendarButtonIcon"
|
||||
:calendar-button-icon-content="calendarButtonIconContent"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
:class="{'required-date': invalid}"
|
||||
:bootstrap-styling="bootstrapStyling"
|
||||
:use-utc="useUtc"
|
||||
@showCalendar="showCalendar"
|
||||
@closeCalendar="close"
|
||||
@typedDate="setTypedDate"
|
||||
@clearDate="clearDate">
|
||||
<slot slot="afterDateInput" name="afterDateInput"/>
|
||||
</date-input>
|
||||
|
||||
<!-- Day View -->
|
||||
<picker-day
|
||||
v-if="allowedToShowView('day')"
|
||||
:page-date="pageDate"
|
||||
:selected-date="selectedDate"
|
||||
:show-day-view="showDayView"
|
||||
:full-month-name="fullMonthName"
|
||||
:allowed-to-show-view="allowedToShowView"
|
||||
:disabled-dates="disabledDates"
|
||||
:highlighted="highlighted"
|
||||
:calendar-class="calendarClass"
|
||||
:calendar-style="calendarStyle"
|
||||
:translation="translation"
|
||||
:page-timestamp="pageTimestamp"
|
||||
:is-rtl="isRtl"
|
||||
:monday-first="mondayFirst"
|
||||
:day-cell-content="dayCellContent"
|
||||
:use-utc="useUtc"
|
||||
@changedMonth="handleChangedMonthFromDayPicker"
|
||||
@selectDate="selectDate"
|
||||
@showMonthCalendar="showMonthCalendar"
|
||||
@selectedDisabled="selectDisabledDate">
|
||||
<slot slot="beforeCalendarHeader" name="beforeCalendarHeader"/>
|
||||
</picker-day>
|
||||
|
||||
<!-- Month View -->
|
||||
<picker-month
|
||||
v-if="allowedToShowView('month')"
|
||||
:page-date="pageDate"
|
||||
:selected-date="selectedDate"
|
||||
:show-month-view="showMonthView"
|
||||
:allowed-to-show-view="allowedToShowView"
|
||||
:disabled-dates="disabledDates"
|
||||
:calendar-class="calendarClass"
|
||||
:calendar-style="calendarStyle"
|
||||
:translation="translation"
|
||||
:is-rtl="isRtl"
|
||||
:use-utc="useUtc"
|
||||
@selectMonth="selectMonth"
|
||||
@showYearCalendar="showYearCalendar"
|
||||
@changedYear="setPageDate">
|
||||
<slot slot="beforeCalendarHeader" name="beforeCalendarHeader"/>
|
||||
</picker-month>
|
||||
|
||||
<!-- Year View -->
|
||||
<picker-year
|
||||
v-if="allowedToShowView('year')"
|
||||
:page-date="pageDate"
|
||||
:selected-date="selectedDate"
|
||||
:show-year-view="showYearView"
|
||||
:allowed-to-show-view="allowedToShowView"
|
||||
:disabled-dates="disabledDates"
|
||||
:calendar-class="calendarClass"
|
||||
:calendar-style="calendarStyle"
|
||||
:translation="translation"
|
||||
:is-rtl="isRtl"
|
||||
:use-utc="useUtc"
|
||||
@selectYear="selectYear"
|
||||
@changedDecade="setPageDate">
|
||||
<slot slot="beforeCalendarHeader" name="beforeCalendarHeader"/>
|
||||
</picker-year>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import en from './src/locale/translations/en'
|
||||
import DateInput from './DateInput'
|
||||
import PickerDay from './PickerDay.vue'
|
||||
import PickerMonth from './PickerMonth.vue'
|
||||
import PickerYear from './PickerYear.vue'
|
||||
import utils, { makeDateUtils } from './src/DateUtils'
|
||||
import { mapGetters } from 'vuex'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DateInput,
|
||||
PickerDay,
|
||||
PickerMonth,
|
||||
PickerYear
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
validator: val => utils.validateDateInput(val)
|
||||
},
|
||||
name: String,
|
||||
refName: String,
|
||||
id: String,
|
||||
// format: {
|
||||
// type: [String, Function],
|
||||
// default: 'dd MMM yyyy'
|
||||
// },
|
||||
language: {
|
||||
type: Object,
|
||||
default: () => en
|
||||
},
|
||||
openDate: {
|
||||
validator: val => utils.validateDateInput(val)
|
||||
},
|
||||
dayCellContent: Function,
|
||||
fullMonthName: Boolean,
|
||||
disabledDates: Object,
|
||||
highlighted: Object,
|
||||
placeholder: String,
|
||||
inline: Boolean,
|
||||
calendarClass: [String, Object, Array],
|
||||
inputClass: [String, Object, Array],
|
||||
wrapperClass: [String, Object, Array],
|
||||
mondayFirst: Boolean,
|
||||
clearButton: Boolean,
|
||||
clearButtonIcon: String,
|
||||
calendarButton: Boolean,
|
||||
calendarButtonIcon: String,
|
||||
calendarButtonIconContent: String,
|
||||
bootstrapStyling: Boolean,
|
||||
initialView: String,
|
||||
disabled: Boolean,
|
||||
required: Boolean,
|
||||
invalid: Boolean,
|
||||
typeable: Boolean,
|
||||
useUtc: Boolean,
|
||||
minimumView: {
|
||||
type: String,
|
||||
default: 'day'
|
||||
},
|
||||
maximumView: {
|
||||
type: String,
|
||||
default: 'year'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const startDate = this.openDate ? new Date(this.openDate) : new Date()
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
const pageTimestamp = constructedDateUtils.setDate(startDate, 1)
|
||||
return {
|
||||
/*
|
||||
* Vue cannot observe changes to a Date Object so date must be stored as a timestamp
|
||||
* This represents the first day of the current viewing month
|
||||
* {Number}
|
||||
*/
|
||||
pageTimestamp,
|
||||
/*
|
||||
* Selected Date
|
||||
* {Date}
|
||||
*/
|
||||
selectedDate: null,
|
||||
/*
|
||||
* Flags to show calendar views
|
||||
* {Boolean}
|
||||
*/
|
||||
showDayView: false,
|
||||
showMonthView: false,
|
||||
showYearView: false,
|
||||
/*
|
||||
* Positioning
|
||||
*/
|
||||
calendarHeight: 0,
|
||||
resetTypedDate: new Date(),
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (value) {
|
||||
this.setValue(value)
|
||||
},
|
||||
openDate () {
|
||||
this.setPageDate()
|
||||
},
|
||||
initialView () {
|
||||
this.setInitialView()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('preferences', {
|
||||
'format': 'getMomentDateFormat'
|
||||
}),
|
||||
customFormatter () {
|
||||
let newDate = new Date(this.value)
|
||||
return moment(newDate).format(this.format)
|
||||
},
|
||||
computedInitialView () {
|
||||
if (!this.initialView) {
|
||||
return this.minimumView
|
||||
}
|
||||
|
||||
return this.initialView
|
||||
},
|
||||
pageDate () {
|
||||
return new Date(this.pageTimestamp)
|
||||
},
|
||||
|
||||
translation () {
|
||||
return this.language
|
||||
},
|
||||
|
||||
calendarStyle () {
|
||||
return {
|
||||
position: this.isInline ? 'static' : undefined
|
||||
}
|
||||
},
|
||||
isOpen () {
|
||||
return this.showDayView || this.showMonthView || this.showYearView
|
||||
},
|
||||
isInline () {
|
||||
return !!this.inline
|
||||
},
|
||||
isRtl () {
|
||||
return this.translation.rtl === true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Called in the event that the user navigates to date pages and
|
||||
* closes the picker without selecting a date.
|
||||
*/
|
||||
resetDefaultPageDate () {
|
||||
if (this.selectedDate === null) {
|
||||
this.setPageDate()
|
||||
return
|
||||
}
|
||||
this.setPageDate(this.selectedDate)
|
||||
},
|
||||
/**
|
||||
* Effectively a toggle to show/hide the calendar
|
||||
* @return {mixed}
|
||||
*/
|
||||
showCalendar () {
|
||||
if (this.disabled || this.isInline) {
|
||||
return false
|
||||
}
|
||||
if (this.isOpen) {
|
||||
return this.close(true)
|
||||
}
|
||||
this.setInitialView()
|
||||
},
|
||||
/**
|
||||
* Sets the initial picker page view: day, month or year
|
||||
*/
|
||||
setInitialView () {
|
||||
const initialView = this.computedInitialView
|
||||
if (!this.allowedToShowView(initialView)) {
|
||||
throw new Error(`initialView '${this.initialView}' cannot be rendered based on minimum '${this.minimumView}' and maximum '${this.maximumView}'`)
|
||||
}
|
||||
switch (initialView) {
|
||||
case 'year':
|
||||
this.showYearCalendar()
|
||||
break
|
||||
case 'month':
|
||||
this.showMonthCalendar()
|
||||
break
|
||||
default:
|
||||
this.showDayCalendar()
|
||||
break
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Are we allowed to show a specific picker view?
|
||||
* @param {String} view
|
||||
* @return {Boolean}
|
||||
*/
|
||||
allowedToShowView (view) {
|
||||
const views = ['day', 'month', 'year']
|
||||
const minimumViewIndex = views.indexOf(this.minimumView)
|
||||
const maximumViewIndex = views.indexOf(this.maximumView)
|
||||
const viewIndex = views.indexOf(view)
|
||||
|
||||
return viewIndex >= minimumViewIndex && viewIndex <= maximumViewIndex
|
||||
},
|
||||
/**
|
||||
* Show the day picker
|
||||
* @return {Boolean}
|
||||
*/
|
||||
showDayCalendar () {
|
||||
if (!this.allowedToShowView('day')) {
|
||||
return false
|
||||
}
|
||||
this.close()
|
||||
this.showDayView = true
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* Show the month picker
|
||||
* @return {Boolean}
|
||||
*/
|
||||
showMonthCalendar () {
|
||||
if (!this.allowedToShowView('month')) {
|
||||
return false
|
||||
}
|
||||
this.close()
|
||||
this.showMonthView = true
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* Show the year picker
|
||||
* @return {Boolean}
|
||||
*/
|
||||
showYearCalendar () {
|
||||
if (!this.allowedToShowView('year')) {
|
||||
return false
|
||||
}
|
||||
this.close()
|
||||
this.showYearView = true
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* Set the selected date
|
||||
* @param {Number} timestamp
|
||||
*/
|
||||
setDate (timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
this.selectedDate = date
|
||||
this.setPageDate(date)
|
||||
this.$emit('selected', date)
|
||||
this.$emit('input', date)
|
||||
},
|
||||
/**
|
||||
* Clear the selected date
|
||||
*/
|
||||
clearDate () {
|
||||
this.selectedDate = null
|
||||
this.setPageDate()
|
||||
this.$emit('selected', null)
|
||||
this.$emit('input', null)
|
||||
this.$emit('cleared')
|
||||
},
|
||||
/**
|
||||
* @param {Object} date
|
||||
*/
|
||||
selectDate (date) {
|
||||
this.setDate(date.timestamp)
|
||||
if (!this.isInline) {
|
||||
this.close(true)
|
||||
}
|
||||
this.resetTypedDate = new Date()
|
||||
},
|
||||
/**
|
||||
* @param {Object} date
|
||||
*/
|
||||
selectDisabledDate (date) {
|
||||
this.$emit('selectedDisabled', date)
|
||||
},
|
||||
/**
|
||||
* @param {Object} month
|
||||
*/
|
||||
selectMonth (month) {
|
||||
const date = new Date(month.timestamp)
|
||||
if (this.allowedToShowView('day')) {
|
||||
this.setPageDate(date)
|
||||
this.$emit('changedMonth', month)
|
||||
this.showDayCalendar()
|
||||
} else {
|
||||
this.selectDate(month)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {Object} year
|
||||
*/
|
||||
selectYear (year) {
|
||||
const date = new Date(year.timestamp)
|
||||
if (this.allowedToShowView('month')) {
|
||||
this.setPageDate(date)
|
||||
this.$emit('changedYear', year)
|
||||
this.showMonthCalendar()
|
||||
} else {
|
||||
this.selectDate(year)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Set the datepicker value
|
||||
* @param {Date|String|Number|null} date
|
||||
*/
|
||||
setValue (date) {
|
||||
if (typeof date === 'string' || typeof date === 'number') {
|
||||
let parsed = new Date(date)
|
||||
date = isNaN(parsed.valueOf()) ? null : parsed
|
||||
}
|
||||
if (!date) {
|
||||
this.setPageDate()
|
||||
this.selectedDate = null
|
||||
return
|
||||
}
|
||||
this.selectedDate = date
|
||||
this.setPageDate(date)
|
||||
},
|
||||
/**
|
||||
* Sets the date that the calendar should open on
|
||||
*/
|
||||
setPageDate (date) {
|
||||
if (!date) {
|
||||
if (this.openDate) {
|
||||
date = new Date(this.openDate)
|
||||
} else {
|
||||
date = new Date()
|
||||
}
|
||||
}
|
||||
this.pageTimestamp = this.utils.setDate(new Date(date), 1)
|
||||
},
|
||||
/**
|
||||
* Handles a month change from the day picker
|
||||
*/
|
||||
handleChangedMonthFromDayPicker (date) {
|
||||
this.setPageDate(date)
|
||||
this.$emit('changedMonth', date)
|
||||
},
|
||||
/**
|
||||
* Set the date from a typedDate event
|
||||
*/
|
||||
setTypedDate (date) {
|
||||
this.setDate(date.getTime())
|
||||
},
|
||||
/**
|
||||
* Close all calendar layers
|
||||
* @param {Boolean} emitEvent - emit close event
|
||||
*/
|
||||
close (emitEvent) {
|
||||
this.showDayView = this.showMonthView = this.showYearView = false
|
||||
if (!this.isInline) {
|
||||
if (emitEvent) {
|
||||
this.$emit('closed')
|
||||
}
|
||||
document.removeEventListener('click', this.clickOutside, false)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Initiate the component
|
||||
*/
|
||||
init () {
|
||||
if (this.value) {
|
||||
this.setValue(this.value)
|
||||
}
|
||||
if (this.isInline) {
|
||||
this.setInitialView()
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
<style lang="css">
|
||||
.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.required-date {
|
||||
border: 1px solid #FB7178;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.vdp-datepicker {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.vdp-datepicker * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
width: 300px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header {
|
||||
display: block;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header span {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 71.42857142857143%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .prev,
|
||||
.vdp-datepicker__calendar header .next {
|
||||
width: 14.285714285714286%;
|
||||
float: left;
|
||||
text-indent: -10000px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .prev:after,
|
||||
.vdp-datepicker__calendar header .next:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
border: 6px solid transparent;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .prev:after {
|
||||
border-right: 10px solid #000;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .prev.disabled:after {
|
||||
border-right: 10px solid #ddd;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .next:after {
|
||||
border-left: 10px solid #000;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .next.disabled:after {
|
||||
border-left: 10px solid #ddd;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .prev:not(.disabled),
|
||||
.vdp-datepicker__calendar header .next:not(.disabled),
|
||||
.vdp-datepicker__calendar header .up:not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar header .prev:not(.disabled):hover,
|
||||
.vdp-datepicker__calendar header .next:not(.disabled):hover,
|
||||
.vdp-datepicker__calendar header .up:not(.disabled):hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .disabled {
|
||||
color: #ddd;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .flex-rtl {
|
||||
display: flex;
|
||||
width: inherit;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
width: 14.285714285714286%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day:hover,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month:hover,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year:hover {
|
||||
border: 1px solid #4bd;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.selected {
|
||||
background: #4bd;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.selected:hover {
|
||||
background: #4bd;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.selected.highlighted {
|
||||
background: #4bd;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.highlighted {
|
||||
background: #cae5ed;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.highlighted.disabled {
|
||||
color: #a3a3a3;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.grey {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.grey:hover {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.day-header {
|
||||
font-size: 75%;
|
||||
white-space: nowrap;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .cell.day-header:hover {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.vdp-datepicker__calendar .month,
|
||||
.vdp-datepicker__calendar .year {
|
||||
width: 33.333%;
|
||||
}
|
||||
|
||||
.vdp-datepicker__clear-button,
|
||||
.vdp-datepicker__calendar-button {
|
||||
cursor: pointer;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.vdp-datepicker__clear-button.disabled,
|
||||
.vdp-datepicker__calendar-button.disabled {
|
||||
color: #999;
|
||||
cursor: default;
|
||||
}
|
||||
</style>
|
||||
@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<div :class="{'input-group' : bootstrapStyling}">
|
||||
<!-- Calendar Button -->
|
||||
<span v-if="calendarButton" class="vdp-datepicker__calendar-button" :class="{'input-group-prepend' : bootstrapStyling}" @click="showCalendar" v-bind:style="{'cursor:not-allowed;' : disabled}">
|
||||
<span :class="{'input-group-text' : bootstrapStyling}">
|
||||
<font-awesome-icon :icon="calendarButtonIcon"/>
|
||||
</span>
|
||||
</span>
|
||||
<!-- Input -->
|
||||
<input
|
||||
:type="inline ? 'hidden' : 'text'"
|
||||
:class="[computedInputClass, {'invalid': isFieldValid}]"
|
||||
:name="name"
|
||||
:ref="refName"
|
||||
:id="id"
|
||||
:value="formattedValue"
|
||||
:open-date="openDate"
|
||||
:placeholder="placeholder"
|
||||
:clear-button="clearButton"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
:readonly="!typeable"
|
||||
class="date-field"
|
||||
@click="showCalendar"
|
||||
@keyup="parseTypedDate"
|
||||
@blur="inputBlurred"
|
||||
autocomplete="off">
|
||||
<!-- Clear Button -->
|
||||
<span v-if="clearButton && selectedDate" class="vdp-datepicker__clear-button" :class="{'input-group-append' : bootstrapStyling}" @click="clearDate()">
|
||||
<span :class="{'input-group-text' : bootstrapStyling}">
|
||||
<!-- <i :class="clearButtonIcon">
|
||||
<span v-if="!clearButtonIcon">×</span>
|
||||
</i> -->
|
||||
<font-awesome-icon :icon="clearButtonIcon"/>
|
||||
</span>
|
||||
</span>
|
||||
<slot name="afterDateInput"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from './src/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
selectedDate: Date,
|
||||
resetTypedDate: [Date],
|
||||
format: [String, Function],
|
||||
translation: Object,
|
||||
inline: Boolean,
|
||||
id: String,
|
||||
name: String,
|
||||
refName: String,
|
||||
openDate: Date,
|
||||
placeholder: String,
|
||||
inputClass: [String, Object, Array],
|
||||
clearButton: Boolean,
|
||||
clearButtonIcon: String,
|
||||
calendarButton: Boolean,
|
||||
calendarButtonIcon: String,
|
||||
calendarButtonIconContent: String,
|
||||
disabled: Boolean,
|
||||
required: Boolean,
|
||||
typeable: Boolean,
|
||||
bootstrapStyling: Boolean,
|
||||
useUtc: Boolean,
|
||||
invalid: Boolean
|
||||
},
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
input: null,
|
||||
typedDate: false,
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedValue () {
|
||||
if (!this.selectedDate) {
|
||||
return null
|
||||
}
|
||||
if (this.typedDate) {
|
||||
return this.typedDate
|
||||
}
|
||||
return typeof this.format === 'function'
|
||||
? this.format(this.selectedDate)
|
||||
: this.utils.formatDate(new Date(this.selectedDate), this.format, this.translation)
|
||||
},
|
||||
|
||||
computedInputClass () {
|
||||
if (this.bootstrapStyling) {
|
||||
if (typeof this.inputClass === 'string') {
|
||||
return [this.inputClass, 'form-control'].join(' ')
|
||||
}
|
||||
return {'form-control': true, ...this.inputClass}
|
||||
}
|
||||
return this.inputClass
|
||||
},
|
||||
|
||||
isFieldValid () {
|
||||
return this.invalid
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
resetTypedDate () {
|
||||
this.typedDate = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCalendar () {
|
||||
this.$emit('showCalendar')
|
||||
},
|
||||
/**
|
||||
* Attempt to parse a typed date
|
||||
* @param {Event} event
|
||||
*/
|
||||
parseTypedDate (event) {
|
||||
// close calendar if escape or enter are pressed
|
||||
if ([
|
||||
27, // escape
|
||||
13 // enter
|
||||
].includes(event.keyCode)) {
|
||||
this.input.blur()
|
||||
}
|
||||
|
||||
if (this.typeable) {
|
||||
const typedDate = Date.parse(this.input.value)
|
||||
if (!isNaN(typedDate)) {
|
||||
this.typedDate = this.input.value
|
||||
this.$emit('typedDate', new Date(this.typedDate))
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* nullify the typed date to defer to regular formatting
|
||||
* called once the input is blurred
|
||||
*/
|
||||
inputBlurred () {
|
||||
if (this.typeable && isNaN(Date.parse(this.input.value))) {
|
||||
this.clearDate()
|
||||
this.input.value = null
|
||||
this.typedDate = null
|
||||
}
|
||||
|
||||
this.$emit('closeCalendar')
|
||||
},
|
||||
/**
|
||||
* emit a clearDate event
|
||||
*/
|
||||
clearDate () {
|
||||
this.$emit('clearDate')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.input = this.$el.querySelector('input')
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@ -1,375 +0,0 @@
|
||||
<template>
|
||||
<div :class="[calendarClass, 'vdp-datepicker__calendar']" v-show="showDayView" :style="calendarStyle" @mousedown.prevent>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
<header>
|
||||
<span
|
||||
@click="isRtl ? nextMonth() : previousMonth()"
|
||||
class="prev"
|
||||
:class="{'disabled': isLeftNavDisabled}"><</span>
|
||||
<span class="day__month_btn" @click="showMonthCalendar" :class="allowedToShowView('month') ? 'up' : ''">{{ isYmd ? currYearName : currMonthName }} {{ isYmd ? currMonthName : currYearName }}</span>
|
||||
<span
|
||||
@click="isRtl ? previousMonth() : nextMonth()"
|
||||
class="next"
|
||||
:class="{'disabled': isRightNavDisabled}">></span>
|
||||
</header>
|
||||
<div :class="isRtl ? 'flex-rtl' : ''">
|
||||
<span class="cell day-header" v-for="d in daysOfWeek" :key="d.timestamp">{{ d }}</span>
|
||||
<template v-if="blankDays > 0">
|
||||
<span class="cell day blank" v-for="d in blankDays" :key="d.timestamp"></span>
|
||||
</template><!--
|
||||
--><span class="cell day"
|
||||
v-for="day in days"
|
||||
:key="day.timestamp"
|
||||
:class="dayClasses(day)"
|
||||
v-html="dayCellContent(day)"
|
||||
@click="selectDate(day)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from './src/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
showDayView: Boolean,
|
||||
selectedDate: Date,
|
||||
pageDate: Date,
|
||||
pageTimestamp: Number,
|
||||
fullMonthName: Boolean,
|
||||
allowedToShowView: Function,
|
||||
dayCellContent: {
|
||||
type: Function,
|
||||
default: day => day.date
|
||||
},
|
||||
disabledDates: Object,
|
||||
highlighted: Object,
|
||||
calendarClass: [String, Object, Array],
|
||||
calendarStyle: Object,
|
||||
translation: Object,
|
||||
isRtl: Boolean,
|
||||
mondayFirst: Boolean,
|
||||
useUtc: Boolean
|
||||
},
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Returns an array of day names
|
||||
* @return {String[]}
|
||||
*/
|
||||
daysOfWeek () {
|
||||
if (this.mondayFirst) {
|
||||
const tempDays = this.translation.days.slice()
|
||||
tempDays.push(tempDays.shift())
|
||||
return tempDays
|
||||
}
|
||||
return this.translation.days
|
||||
},
|
||||
/**
|
||||
* Returns the day number of the week less one for the first of the current month
|
||||
* Used to show amount of empty cells before the first in the day calendar layout
|
||||
* @return {Number}
|
||||
*/
|
||||
blankDays () {
|
||||
const d = this.pageDate
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1))
|
||||
: new Date(d.getFullYear(), d.getMonth(), 1, d.getHours(), d.getMinutes())
|
||||
if (this.mondayFirst) {
|
||||
return this.utils.getDay(dObj) > 0 ? this.utils.getDay(dObj) - 1 : 6
|
||||
}
|
||||
return this.utils.getDay(dObj)
|
||||
},
|
||||
/**
|
||||
* @return {Object[]}
|
||||
*/
|
||||
days () {
|
||||
const d = this.pageDate
|
||||
let days = []
|
||||
// set up a new date object to the beginning of the current 'page'
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1))
|
||||
: new Date(d.getFullYear(), d.getMonth(), 1, d.getHours(), d.getMinutes())
|
||||
let daysInMonth = this.utils.daysInMonth(this.utils.getFullYear(dObj), this.utils.getMonth(dObj))
|
||||
for (let i = 0; i < daysInMonth; i++) {
|
||||
days.push({
|
||||
date: this.utils.getDate(dObj),
|
||||
timestamp: dObj.getTime(),
|
||||
isSelected: this.isSelectedDate(dObj),
|
||||
isDisabled: this.isDisabledDate(dObj),
|
||||
isHighlighted: this.isHighlightedDate(dObj),
|
||||
isHighlightStart: this.isHighlightStart(dObj),
|
||||
isHighlightEnd: this.isHighlightEnd(dObj),
|
||||
isToday: this.utils.compareDates(dObj, new Date()),
|
||||
isWeekend: this.utils.getDay(dObj) === 0 || this.utils.getDay(dObj) === 6,
|
||||
isSaturday: this.utils.getDay(dObj) === 6,
|
||||
isSunday: this.utils.getDay(dObj) === 0
|
||||
})
|
||||
this.utils.setDate(dObj, this.utils.getDate(dObj) + 1)
|
||||
}
|
||||
return days
|
||||
},
|
||||
/**
|
||||
* Gets the name of the month the current page is on
|
||||
* @return {String}
|
||||
*/
|
||||
currMonthName () {
|
||||
const monthName = this.fullMonthName ? this.translation.months : this.translation.monthsAbbr
|
||||
return this.utils.getMonthNameAbbr(this.utils.getMonth(this.pageDate), monthName)
|
||||
},
|
||||
/**
|
||||
* Gets the name of the year that current page is on
|
||||
* @return {Number}
|
||||
*/
|
||||
currYearName () {
|
||||
const yearSuffix = this.translation.yearSuffix
|
||||
return `${this.utils.getFullYear(this.pageDate)}${yearSuffix}`
|
||||
},
|
||||
/**
|
||||
* Is this translation using year/month/day format?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isYmd () {
|
||||
return this.translation.ymd && this.translation.ymd === true
|
||||
},
|
||||
/**
|
||||
* Is the left hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isLeftNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isNextMonthDisabled(this.pageTimestamp)
|
||||
: this.isPreviousMonthDisabled(this.pageTimestamp)
|
||||
},
|
||||
/**
|
||||
* Is the right hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isRightNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isPreviousMonthDisabled(this.pageTimestamp)
|
||||
: this.isNextMonthDisabled(this.pageTimestamp)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectDate (date) {
|
||||
if (date.isDisabled) {
|
||||
this.$emit('selectedDisabled', date)
|
||||
return false
|
||||
}
|
||||
this.$emit('selectDate', date)
|
||||
},
|
||||
/**
|
||||
* @return {Number}
|
||||
*/
|
||||
getPageMonth () {
|
||||
return this.utils.getMonth(this.pageDate)
|
||||
},
|
||||
/**
|
||||
* Emit an event to show the month picker
|
||||
*/
|
||||
showMonthCalendar () {
|
||||
this.$emit('showMonthCalendar')
|
||||
},
|
||||
/**
|
||||
* Change the page month
|
||||
* @param {Number} incrementBy
|
||||
*/
|
||||
changeMonth (incrementBy) {
|
||||
let date = this.pageDate
|
||||
this.utils.setMonth(date, this.utils.getMonth(date) + incrementBy)
|
||||
this.$emit('changedMonth', date)
|
||||
},
|
||||
/**
|
||||
* Decrement the page month
|
||||
*/
|
||||
previousMonth () {
|
||||
if (!this.isPreviousMonthDisabled()) {
|
||||
this.changeMonth(-1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Is the previous month disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isPreviousMonthDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.to) {
|
||||
return false
|
||||
}
|
||||
let d = this.pageDate
|
||||
return this.utils.getMonth(this.disabledDates.to) >= this.utils.getMonth(d) &&
|
||||
this.utils.getFullYear(this.disabledDates.to) >= this.utils.getFullYear(d)
|
||||
},
|
||||
/**
|
||||
* Increment the current page month
|
||||
*/
|
||||
nextMonth () {
|
||||
if (!this.isNextMonthDisabled()) {
|
||||
this.changeMonth(+1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Is the next month disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isNextMonthDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.from) {
|
||||
return false
|
||||
}
|
||||
let d = this.pageDate
|
||||
return this.utils.getMonth(this.disabledDates.from) <= this.utils.getMonth(d) &&
|
||||
this.utils.getFullYear(this.disabledDates.from) <= this.utils.getFullYear(d)
|
||||
},
|
||||
/**
|
||||
* Whether a day is selected
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isSelectedDate (dObj) {
|
||||
return this.selectedDate && this.utils.compareDates(this.selectedDate, dObj)
|
||||
},
|
||||
/**
|
||||
* Whether a day is disabled
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDisabledDate (date) {
|
||||
let disabledDates = false
|
||||
|
||||
if (typeof this.disabledDates === 'undefined') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.dates !== 'undefined') {
|
||||
this.disabledDates.dates.forEach((d) => {
|
||||
if (this.utils.compareDates(date, d)) {
|
||||
disabledDates = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
if (typeof this.disabledDates.to !== 'undefined' && this.disabledDates.to && date < this.disabledDates.to) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.from !== 'undefined' && this.disabledDates.from && date > this.disabledDates.from) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.ranges !== 'undefined') {
|
||||
this.disabledDates.ranges.forEach((range) => {
|
||||
if (typeof range.from !== 'undefined' && range.from && typeof range.to !== 'undefined' && range.to) {
|
||||
if (date < range.to && date > range.from) {
|
||||
disabledDates = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (typeof this.disabledDates.days !== 'undefined' && this.disabledDates.days.indexOf(this.utils.getDay(date)) !== -1) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.daysOfMonth !== 'undefined' && this.disabledDates.daysOfMonth.indexOf(this.utils.getDate(date)) !== -1) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.customPredictor === 'function' && this.disabledDates.customPredictor(date)) {
|
||||
disabledDates = true
|
||||
}
|
||||
return disabledDates
|
||||
},
|
||||
/**
|
||||
* Whether a day is highlighted (only if it is not disabled already except when highlighted.includeDisabled is true)
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isHighlightedDate (date) {
|
||||
if (!(this.highlighted && this.highlighted.includeDisabled) && this.isDisabledDate(date)) {
|
||||
return false
|
||||
}
|
||||
|
||||
let highlighted = false
|
||||
|
||||
if (typeof this.highlighted === 'undefined') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.dates !== 'undefined') {
|
||||
this.highlighted.dates.forEach((d) => {
|
||||
if (this.utils.compareDates(date, d)) {
|
||||
highlighted = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isDefined(this.highlighted.from) && this.isDefined(this.highlighted.to)) {
|
||||
highlighted = date >= this.highlighted.from && date <= this.highlighted.to
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.days !== 'undefined' && this.highlighted.days.indexOf(this.utils.getDay(date)) !== -1) {
|
||||
highlighted = true
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.daysOfMonth !== 'undefined' && this.highlighted.daysOfMonth.indexOf(this.utils.getDate(date)) !== -1) {
|
||||
highlighted = true
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.customPredictor === 'function' && this.highlighted.customPredictor(date)) {
|
||||
highlighted = true
|
||||
}
|
||||
|
||||
return highlighted
|
||||
},
|
||||
dayClasses (day) {
|
||||
return {
|
||||
'selected': day.isSelected,
|
||||
'disabled': day.isDisabled,
|
||||
'highlighted': day.isHighlighted,
|
||||
'today': day.isToday,
|
||||
'weekend': day.isWeekend,
|
||||
'sat': day.isSaturday,
|
||||
'sun': day.isSunday,
|
||||
'highlight-start': day.isHighlightStart,
|
||||
'highlight-end': day.isHighlightEnd
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Whether a day is highlighted and it is the first date
|
||||
* in the highlighted range of dates
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isHighlightStart (date) {
|
||||
return this.isHighlightedDate(date) &&
|
||||
(this.highlighted.from instanceof Date) &&
|
||||
(this.utils.getFullYear(this.highlighted.from) === this.utils.getFullYear(date)) &&
|
||||
(this.utils.getMonth(this.highlighted.from) === this.utils.getMonth(date)) &&
|
||||
(this.utils.getDate(this.highlighted.from) === this.utils.getDate(date))
|
||||
},
|
||||
/**
|
||||
* Whether a day is highlighted and it is the first date
|
||||
* in the highlighted range of dates
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isHighlightEnd (date) {
|
||||
return this.isHighlightedDate(date) &&
|
||||
(this.highlighted.to instanceof Date) &&
|
||||
(this.utils.getFullYear(this.highlighted.to) === this.utils.getFullYear(date)) &&
|
||||
(this.utils.getMonth(this.highlighted.to) === this.utils.getMonth(date)) &&
|
||||
(this.utils.getDate(this.highlighted.to) === this.utils.getDate(date))
|
||||
},
|
||||
/**
|
||||
* Helper
|
||||
* @param {mixed} prop
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDefined (prop) {
|
||||
return typeof prop !== 'undefined' && prop
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@ -1,200 +0,0 @@
|
||||
<template>
|
||||
<div :class="[calendarClass, 'vdp-datepicker__calendar']" v-show="showMonthView" :style="calendarStyle" @mousedown.prevent>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
<header>
|
||||
<span
|
||||
@click="isRtl ? nextYear() : previousYear()"
|
||||
class="prev"
|
||||
:class="{'disabled': isLeftNavDisabled}"><</span>
|
||||
<span class="month__year_btn" @click="showYearCalendar" :class="allowedToShowView('year') ? 'up' : ''">{{ pageYearName }}</span>
|
||||
<span
|
||||
@click="isRtl ? previousYear() : nextYear()"
|
||||
class="next"
|
||||
:class="{'disabled': isRightNavDisabled}">></span>
|
||||
</header>
|
||||
<span class="cell month"
|
||||
v-for="month in months"
|
||||
:key="month.timestamp"
|
||||
:class="{'selected': month.isSelected, 'disabled': month.isDisabled}"
|
||||
@click.stop="selectMonth(month)">{{ month.month }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from './src/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
showMonthView: Boolean,
|
||||
selectedDate: Date,
|
||||
pageDate: Date,
|
||||
pageTimestamp: Number,
|
||||
disabledDates: Object,
|
||||
calendarClass: [String, Object, Array],
|
||||
calendarStyle: Object,
|
||||
translation: Object,
|
||||
isRtl: Boolean,
|
||||
allowedToShowView: Function,
|
||||
useUtc: Boolean
|
||||
},
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
months () {
|
||||
const d = this.pageDate
|
||||
let months = []
|
||||
// set up a new date object to the beginning of the current 'page'
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(d.getUTCFullYear(), 0, d.getUTCDate()))
|
||||
: new Date(d.getFullYear(), 0, d.getDate(), d.getHours(), d.getMinutes())
|
||||
for (let i = 0; i < 12; i++) {
|
||||
months.push({
|
||||
month: this.utils.getMonthName(i, this.translation.months),
|
||||
timestamp: dObj.getTime(),
|
||||
isSelected: this.isSelectedMonth(dObj),
|
||||
isDisabled: this.isDisabledMonth(dObj)
|
||||
})
|
||||
this.utils.setMonth(dObj, this.utils.getMonth(dObj) + 1)
|
||||
}
|
||||
return months
|
||||
},
|
||||
/**
|
||||
* Get year name on current page.
|
||||
* @return {String}
|
||||
*/
|
||||
pageYearName () {
|
||||
const yearSuffix = this.translation.yearSuffix
|
||||
return `${this.utils.getFullYear(this.pageDate)}${yearSuffix}`
|
||||
},
|
||||
/**
|
||||
* Is the left hand navigation disabled
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isLeftNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isNextYearDisabled(this.pageTimestamp)
|
||||
: this.isPreviousYearDisabled(this.pageTimestamp)
|
||||
},
|
||||
/**
|
||||
* Is the right hand navigation disabled
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isRightNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isPreviousYearDisabled(this.pageTimestamp)
|
||||
: this.isNextYearDisabled(this.pageTimestamp)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Emits a selectMonth event
|
||||
* @param {Object} month
|
||||
*/
|
||||
selectMonth (month) {
|
||||
if (month.isDisabled) {
|
||||
return false
|
||||
}
|
||||
this.$emit('selectMonth', month)
|
||||
},
|
||||
/**
|
||||
* Changes the year up or down
|
||||
* @param {Number} incrementBy
|
||||
*/
|
||||
changeYear (incrementBy) {
|
||||
let date = this.pageDate
|
||||
this.utils.setFullYear(date, this.utils.getFullYear(date) + incrementBy)
|
||||
this.$emit('changedYear', date)
|
||||
},
|
||||
/**
|
||||
* Decrements the year
|
||||
*/
|
||||
previousYear () {
|
||||
if (!this.isPreviousYearDisabled()) {
|
||||
this.changeYear(-1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if the previous year is disabled or not
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isPreviousYearDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.to) {
|
||||
return false
|
||||
}
|
||||
return this.utils.getFullYear(this.disabledDates.to) >= this.utils.getFullYear(this.pageDate)
|
||||
},
|
||||
/**
|
||||
* Increments the year
|
||||
*/
|
||||
nextYear () {
|
||||
if (!this.isNextYearDisabled()) {
|
||||
this.changeYear(1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if the next year is disabled or not
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isNextYearDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.from) {
|
||||
return false
|
||||
}
|
||||
return this.utils.getFullYear(this.disabledDates.from) <= this.utils.getFullYear(this.pageDate)
|
||||
},
|
||||
/**
|
||||
* Emits an event that shows the year calendar
|
||||
*/
|
||||
showYearCalendar () {
|
||||
this.$emit('showYearCalendar')
|
||||
},
|
||||
/**
|
||||
* Whether the selected date is in this month
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isSelectedMonth (date) {
|
||||
return (this.selectedDate &&
|
||||
this.utils.getFullYear(this.selectedDate) === this.utils.getFullYear(date) &&
|
||||
this.utils.getMonth(this.selectedDate) === this.utils.getMonth(date))
|
||||
},
|
||||
/**
|
||||
* Whether a month is disabled
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDisabledMonth (date) {
|
||||
let disabledDates = false
|
||||
|
||||
if (typeof this.disabledDates === 'undefined') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.to !== 'undefined' && this.disabledDates.to) {
|
||||
if (
|
||||
(this.utils.getMonth(date) < this.utils.getMonth(this.disabledDates.to) && this.utils.getFullYear(date) <= this.utils.getFullYear(this.disabledDates.to)) ||
|
||||
this.utils.getFullYear(date) < this.utils.getFullYear(this.disabledDates.to)
|
||||
) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
if (typeof this.disabledDates.from !== 'undefined' && this.disabledDates.from) {
|
||||
if (
|
||||
(this.utils.getMonth(date) > this.utils.getMonth(this.disabledDates.from) && this.utils.getFullYear(date) >= this.utils.getFullYear(this.disabledDates.from)) ||
|
||||
this.utils.getFullYear(date) > this.utils.getFullYear(this.disabledDates.from)
|
||||
) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.customPredictor === 'function' && this.disabledDates.customPredictor(date)) {
|
||||
disabledDates = true
|
||||
}
|
||||
return disabledDates
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@ -1,174 +0,0 @@
|
||||
<template>
|
||||
<div :class="[calendarClass, 'vdp-datepicker__calendar']" v-show="showYearView" :style="calendarStyle" @mousedown.prevent>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
<header>
|
||||
<span
|
||||
@click="isRtl ? nextDecade() : previousDecade()"
|
||||
class="prev"
|
||||
:class="{'disabled': isLeftNavDisabled}"><</span>
|
||||
<span>{{ getPageDecade }}</span>
|
||||
<span
|
||||
@click="isRtl ? previousDecade() : nextDecade()"
|
||||
class="next"
|
||||
:class="{'disabled': isRightNavDisabled}">></span>
|
||||
</header>
|
||||
<span
|
||||
class="cell year"
|
||||
v-for="year in years"
|
||||
:key="year.timestamp"
|
||||
:class="{ 'selected': year.isSelected, 'disabled': year.isDisabled }"
|
||||
@click.stop="selectYear(year)">{{ year.year }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from './src/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
showYearView: Boolean,
|
||||
selectedDate: Date,
|
||||
pageDate: Date,
|
||||
pageTimestamp: Number,
|
||||
disabledDates: Object,
|
||||
highlighted: Object,
|
||||
calendarClass: [String, Object, Array],
|
||||
calendarStyle: Object,
|
||||
translation: Object,
|
||||
isRtl: Boolean,
|
||||
allowedToShowView: Function,
|
||||
useUtc: Boolean
|
||||
},
|
||||
computed: {
|
||||
years () {
|
||||
const d = this.pageDate
|
||||
let years = []
|
||||
// set up a new date object to the beginning of the current 'page'7
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(Math.floor(d.getUTCFullYear() / 10) * 10, d.getUTCMonth(), d.getUTCDate()))
|
||||
: new Date(Math.floor(d.getFullYear() / 10) * 10, d.getMonth(), d.getDate(), d.getHours(), d.getMinutes())
|
||||
for (let i = 0; i < 10; i++) {
|
||||
years.push({
|
||||
year: this.utils.getFullYear(dObj),
|
||||
timestamp: dObj.getTime(),
|
||||
isSelected: this.isSelectedYear(dObj),
|
||||
isDisabled: this.isDisabledYear(dObj)
|
||||
})
|
||||
this.utils.setFullYear(dObj, this.utils.getFullYear(dObj) + 1)
|
||||
}
|
||||
return years
|
||||
},
|
||||
/**
|
||||
* @return {String}
|
||||
*/
|
||||
getPageDecade () {
|
||||
const decadeStart = Math.floor(this.utils.getFullYear(this.pageDate) / 10) * 10
|
||||
const decadeEnd = decadeStart + 9
|
||||
const yearSuffix = this.translation.yearSuffix
|
||||
return `${decadeStart} - ${decadeEnd}${yearSuffix}`
|
||||
},
|
||||
/**
|
||||
* Is the left hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isLeftNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isNextDecadeDisabled(this.pageTimestamp)
|
||||
: this.isPreviousDecadeDisabled(this.pageTimestamp)
|
||||
},
|
||||
/**
|
||||
* Is the right hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isRightNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isPreviousDecadeDisabled(this.pageTimestamp)
|
||||
: this.isNextDecadeDisabled(this.pageTimestamp)
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectYear (year) {
|
||||
if (year.isDisabled) {
|
||||
return false
|
||||
}
|
||||
this.$emit('selectYear', year)
|
||||
},
|
||||
changeYear (incrementBy) {
|
||||
let date = this.pageDate
|
||||
this.utils.setFullYear(date, this.utils.getFullYear(date) + incrementBy)
|
||||
this.$emit('changedDecade', date)
|
||||
},
|
||||
previousDecade () {
|
||||
if (this.isPreviousDecadeDisabled()) {
|
||||
return false
|
||||
}
|
||||
this.changeYear(-10)
|
||||
},
|
||||
isPreviousDecadeDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.to) {
|
||||
return false
|
||||
}
|
||||
const disabledYear = this.utils.getFullYear(this.disabledDates.to)
|
||||
const lastYearInPreviousPage = Math.floor(this.utils.getFullYear(this.pageDate) / 10) * 10 - 1
|
||||
return disabledYear > lastYearInPreviousPage
|
||||
},
|
||||
nextDecade () {
|
||||
if (this.isNextDecadeDisabled()) {
|
||||
return false
|
||||
}
|
||||
this.changeYear(10)
|
||||
},
|
||||
isNextDecadeDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.from) {
|
||||
return false
|
||||
}
|
||||
const disabledYear = this.utils.getFullYear(this.disabledDates.from)
|
||||
const firstYearInNextPage = Math.ceil(this.utils.getFullYear(this.pageDate) / 10) * 10
|
||||
return disabledYear < firstYearInNextPage
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the selected date is in this year
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isSelectedYear (date) {
|
||||
return this.selectedDate && this.utils.getFullYear(this.selectedDate) === this.utils.getFullYear(date)
|
||||
},
|
||||
/**
|
||||
* Whether a year is disabled
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDisabledYear (date) {
|
||||
let disabledDates = false
|
||||
if (typeof this.disabledDates === 'undefined' || !this.disabledDates) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.to !== 'undefined' && this.disabledDates.to) {
|
||||
if (this.utils.getFullYear(date) < this.utils.getFullYear(this.disabledDates.to)) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
if (typeof this.disabledDates.from !== 'undefined' && this.disabledDates.from) {
|
||||
if (this.utils.getFullYear(date) > this.utils.getFullYear(this.disabledDates.from)) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.customPredictor === 'function' && this.disabledDates.customPredictor(date)) {
|
||||
disabledDates = true
|
||||
}
|
||||
|
||||
return disabledDates
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@ -1,252 +0,0 @@
|
||||
import en from './locale/translations/en'
|
||||
|
||||
const utils = {
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
useUtc: false,
|
||||
/**
|
||||
* Returns the full year, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getFullYear (date) {
|
||||
return this.useUtc ? date.getUTCFullYear() : date.getFullYear()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the month, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getMonth (date) {
|
||||
return this.useUtc ? date.getUTCMonth() : date.getMonth()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the date, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getDate (date) {
|
||||
return this.useUtc ? date.getUTCDate() : date.getDate()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the day, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getDay (date) {
|
||||
return this.useUtc ? date.getUTCDay() : date.getDay()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the hours, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getHours (date) {
|
||||
return this.useUtc ? date.getUTCHours() : date.getHours()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the minutes, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getMinutes (date) {
|
||||
return this.useUtc ? date.getUTCMinutes() : date.getMinutes()
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the full year, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
setFullYear (date, value, useUtc) {
|
||||
return this.useUtc ? date.setUTCFullYear(value) : date.setFullYear(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the month, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
setMonth (date, value, useUtc) {
|
||||
return this.useUtc ? date.setUTCMonth(value) : date.setMonth(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the date, using UTC or not
|
||||
* @param {Date} date
|
||||
* @param {Number} value
|
||||
*/
|
||||
setDate (date, value, useUtc) {
|
||||
return this.useUtc ? date.setUTCDate(value) : date.setDate(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if date1 is equivalent to date2, without comparing the time
|
||||
* @see https://stackoverflow.com/a/6202196/4455925
|
||||
* @param {Date} date1
|
||||
* @param {Date} date2
|
||||
*/
|
||||
compareDates (date1, date2) {
|
||||
const d1 = new Date(date1.getTime())
|
||||
const d2 = new Date(date2.getTime())
|
||||
|
||||
if (this.useUtc) {
|
||||
d1.setUTCHours(0, 0, 0, 0)
|
||||
d2.setUTCHours(0, 0, 0, 0)
|
||||
} else {
|
||||
d1.setHours(0, 0, 0, 0)
|
||||
d2.setHours(0, 0, 0, 0)
|
||||
}
|
||||
return d1.getTime() === d2.getTime()
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates a date object
|
||||
* @param {Date} date - an object instantiated with the new Date constructor
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isValidDate (date) {
|
||||
if (Object.prototype.toString.call(date) !== '[object Date]') {
|
||||
return false
|
||||
}
|
||||
return !isNaN(date.getTime())
|
||||
},
|
||||
|
||||
/**
|
||||
* Return abbreviated week day name
|
||||
* @param {Date}
|
||||
* @param {Array}
|
||||
* @return {String}
|
||||
*/
|
||||
getDayNameAbbr (date, days) {
|
||||
if (typeof date !== 'object') {
|
||||
throw TypeError('Invalid Type')
|
||||
}
|
||||
return days[this.getDay(date)]
|
||||
},
|
||||
|
||||
/**
|
||||
* Return name of the month
|
||||
* @param {Number|Date}
|
||||
* @param {Array}
|
||||
* @return {String}
|
||||
*/
|
||||
getMonthName (month, months) {
|
||||
if (!months) {
|
||||
throw Error('missing 2nd parameter Months array')
|
||||
}
|
||||
if (typeof month === 'object') {
|
||||
return months[this.getMonth(month)]
|
||||
}
|
||||
if (typeof month === 'number') {
|
||||
return months[month]
|
||||
}
|
||||
throw TypeError('Invalid type')
|
||||
},
|
||||
|
||||
/**
|
||||
* Return an abbreviated version of the month
|
||||
* @param {Number|Date}
|
||||
* @return {String}
|
||||
*/
|
||||
getMonthNameAbbr (month, monthsAbbr) {
|
||||
if (!monthsAbbr) {
|
||||
throw Error('missing 2nd paramter Months array')
|
||||
}
|
||||
if (typeof month === 'object') {
|
||||
return monthsAbbr[this.getMonth(month)]
|
||||
}
|
||||
if (typeof month === 'number') {
|
||||
return monthsAbbr[month]
|
||||
}
|
||||
throw TypeError('Invalid type')
|
||||
},
|
||||
|
||||
/**
|
||||
* Alternative get total number of days in month
|
||||
* @param {Number} year
|
||||
* @param {Number} m
|
||||
* @return {Number}
|
||||
*/
|
||||
daysInMonth (year, month) {
|
||||
return /8|3|5|10/.test(month) ? 30 : month === 1 ? (!(year % 4) && year % 100) || !(year % 400) ? 29 : 28 : 31
|
||||
},
|
||||
|
||||
/**
|
||||
* Get nth suffix for date
|
||||
* @param {Number} day
|
||||
* @return {String}
|
||||
*/
|
||||
getNthSuffix (day) {
|
||||
switch (day) {
|
||||
case 1:
|
||||
case 21:
|
||||
case 31:
|
||||
return 'st'
|
||||
case 2:
|
||||
case 22:
|
||||
return 'nd'
|
||||
case 3:
|
||||
case 23:
|
||||
return 'rd'
|
||||
default:
|
||||
return 'th'
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats date object
|
||||
* @param {Date}
|
||||
* @param {String}
|
||||
* @param {Object}
|
||||
* @return {String}
|
||||
*/
|
||||
formatDate (date, format, translation) {
|
||||
translation = (!translation) ? en : translation
|
||||
let year = this.getFullYear(date)
|
||||
let month = this.getMonth(date) + 1
|
||||
let day = this.getDate(date)
|
||||
let str = format
|
||||
.replace(/dd/, ('0' + day).slice(-2))
|
||||
.replace(/d/, day)
|
||||
.replace(/yyyy/, year)
|
||||
.replace(/yy/, String(year).slice(2))
|
||||
.replace(/MMMM/, this.getMonthName(this.getMonth(date), translation.months))
|
||||
.replace(/MMM/, this.getMonthNameAbbr(this.getMonth(date), translation.monthsAbbr))
|
||||
.replace(/MM/, ('0' + month).slice(-2))
|
||||
.replace(/M(?!a|ä|e)/, month)
|
||||
.replace(/su/, this.getNthSuffix(this.getDate(date)))
|
||||
.replace(/D(?!e|é|i)/, this.getDayNameAbbr(date, translation.days))
|
||||
return str
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an array of dates for each day in between two dates.
|
||||
* @param {Date} start
|
||||
* @param {Date} end
|
||||
* @return {Array}
|
||||
*/
|
||||
createDateArray (start, end) {
|
||||
let dates = []
|
||||
while (start <= end) {
|
||||
dates.push(new Date(start))
|
||||
start = this.setDate(new Date(start), this.getDate(new Date(start)) + 1)
|
||||
}
|
||||
return dates
|
||||
},
|
||||
|
||||
/**
|
||||
* method used as a prop validator for input values
|
||||
* @param {*} val
|
||||
* @return {Boolean}
|
||||
*/
|
||||
validateDateInput (val) {
|
||||
return val === null || val instanceof Date || typeof val === 'string' || typeof val === 'number'
|
||||
}
|
||||
}
|
||||
|
||||
export const makeDateUtils = useUtc => ({...utils, useUtc})
|
||||
|
||||
export default {
|
||||
...utils
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,57 +0,0 @@
|
||||
export default class Language {
|
||||
constructor (language, months, monthsAbbr, days) {
|
||||
this.language = language
|
||||
this.months = months
|
||||
this.monthsAbbr = monthsAbbr
|
||||
this.days = days
|
||||
this.rtl = false
|
||||
this.ymd = false
|
||||
this.yearSuffix = ''
|
||||
}
|
||||
|
||||
get language () {
|
||||
return this._language
|
||||
}
|
||||
|
||||
set language (language) {
|
||||
if (typeof language !== 'string') {
|
||||
throw new TypeError('Language must be a string')
|
||||
}
|
||||
this._language = language
|
||||
}
|
||||
|
||||
get months () {
|
||||
return this._months
|
||||
}
|
||||
|
||||
set months (months) {
|
||||
if (months.length !== 12) {
|
||||
throw new RangeError(`There must be 12 months for ${this.language} language`)
|
||||
}
|
||||
this._months = months
|
||||
}
|
||||
|
||||
get monthsAbbr () {
|
||||
return this._monthsAbbr
|
||||
}
|
||||
|
||||
set monthsAbbr (monthsAbbr) {
|
||||
if (monthsAbbr.length !== 12) {
|
||||
throw new RangeError(`There must be 12 abbreviated months for ${this.language} language`)
|
||||
}
|
||||
this._monthsAbbr = monthsAbbr
|
||||
}
|
||||
|
||||
get days () {
|
||||
return this._days
|
||||
}
|
||||
|
||||
set days (days) {
|
||||
if (days.length !== 7) {
|
||||
throw new RangeError(`There must be 7 days for ${this.language} language`)
|
||||
}
|
||||
this._days = days
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,105 +0,0 @@
|
||||
import af from './translations/af'
|
||||
import ar from './translations/ar'
|
||||
import bg from './translations/bg'
|
||||
import bs from './translations/bs'
|
||||
import ca from './translations/ca'
|
||||
import cs from './translations/cs'
|
||||
import da from './translations/da'
|
||||
import de from './translations/de'
|
||||
import ee from './translations/ee'
|
||||
import el from './translations/el'
|
||||
import en from './translations/en'
|
||||
import es from './translations/es'
|
||||
import fa from './translations/fa'
|
||||
import fi from './translations/fi'
|
||||
import fo from './translations/fo'
|
||||
import fr from './translations/fr'
|
||||
import ge from './translations/ge'
|
||||
import gl from './translations/gl'
|
||||
import he from './translations/he'
|
||||
import hr from './translations/hr'
|
||||
import hu from './translations/hu'
|
||||
import id from './translations/id'
|
||||
import is from './translations/is'
|
||||
import it from './translations/it'
|
||||
import ja from './translations/ja'
|
||||
import kk from './translations/kk'
|
||||
import ko from './translations/ko'
|
||||
import lb from './translations/lb'
|
||||
import lt from './translations/lt'
|
||||
import lv from './translations/lv'
|
||||
import mk from './translations/mk'
|
||||
import mn from './translations/mn'
|
||||
import nbNO from './translations/nb-NO'
|
||||
import nl from './translations/nl'
|
||||
import pl from './translations/pl'
|
||||
import ptBR from './translations/pt-BR'
|
||||
import ro from './translations/ro'
|
||||
import ru from './translations/ru'
|
||||
import sk from './translations/sk'
|
||||
import slSI from './translations/sl-SI'
|
||||
import srCYRL from './translations/sr-CYRL'
|
||||
import sr from './translations/sr'
|
||||
import sv from './translations/sv'
|
||||
import th from './translations/th'
|
||||
import tr from './translations/tr'
|
||||
import uk from './translations/uk'
|
||||
import ur from './translations/ur'
|
||||
import vi from './translations/vi'
|
||||
import zh from './translations/zh'
|
||||
import zhHK from './translations/zh-HK'
|
||||
|
||||
export {
|
||||
af,
|
||||
ar,
|
||||
bg,
|
||||
bs,
|
||||
ca,
|
||||
cs,
|
||||
da,
|
||||
de,
|
||||
ee,
|
||||
el,
|
||||
en,
|
||||
es,
|
||||
fa,
|
||||
fi,
|
||||
fo,
|
||||
fr,
|
||||
ge,
|
||||
gl,
|
||||
he,
|
||||
hr,
|
||||
hu,
|
||||
id,
|
||||
is,
|
||||
it,
|
||||
ja,
|
||||
kk,
|
||||
ko,
|
||||
lb,
|
||||
lt,
|
||||
lv,
|
||||
mk,
|
||||
mn,
|
||||
nbNO,
|
||||
nl,
|
||||
pl,
|
||||
ptBR,
|
||||
ro,
|
||||
ru,
|
||||
sk,
|
||||
slSI,
|
||||
srCYRL,
|
||||
sr,
|
||||
sv,
|
||||
th,
|
||||
tr,
|
||||
uk,
|
||||
ur,
|
||||
vi,
|
||||
zh,
|
||||
zhHK
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Afrikaans',
|
||||
['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'],
|
||||
['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['So.', 'Ma.', 'Di.', 'Wo.', 'Do.', 'Vr.', 'Sa.']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,14 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Arabic',
|
||||
['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوڤمبر', 'ديسمبر'],
|
||||
['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوڤمبر', 'ديسمبر'],
|
||||
['أحد', 'إثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت']
|
||||
)
|
||||
|
||||
language.rtl = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Bulgarian',
|
||||
['Януари', 'Февруари', 'Март', 'Април', 'Май', 'Юни', 'Юли', 'Август', 'Септември', 'Октомври', 'Ноември', 'Декември'],
|
||||
['Ян', 'Фев', 'Мар', 'Апр', 'Май', 'Юни', 'Юли', 'Авг', 'Сеп', 'Окт', 'Ное', 'Дек'],
|
||||
['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Bosnian',
|
||||
['Januar', 'Februar', 'Mart', 'April', 'Maj', 'Juni', 'Juli', 'Avgust', 'Septembar', 'Oktobar', 'Novembar', 'Decembar'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Ned', 'Pon', 'Uto', 'Sri', 'Čet', 'Pet', 'Sub']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Catalan',
|
||||
['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'],
|
||||
['Gen', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Oct', 'Nov', 'Des'],
|
||||
['Diu', 'Dil', 'Dmr', 'Dmc', 'Dij', 'Div', 'Dis']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Czech',
|
||||
['leden', 'únor', 'březen', 'duben', 'květen', 'červen', 'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'],
|
||||
['led', 'úno', 'bře', 'dub', 'kvě', 'čer', 'čec', 'srp', 'zář', 'říj', 'lis', 'pro'],
|
||||
['ne', 'po', 'út', 'st', 'čt', 'pá', 'so']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Danish',
|
||||
['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'German',
|
||||
['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
|
||||
['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Estonian',
|
||||
['Jaanuar', 'Veebruar', 'Märts', 'Aprill', 'Mai', 'Juuni', 'Juuli', 'August', 'September', 'Oktoober', 'November', 'Detsember'],
|
||||
['Jaan', 'Veebr', 'Märts', 'Apr', 'Mai', 'Juuni', 'Juuli', 'Aug', 'Sept', 'Okt', 'Nov', 'Dets'],
|
||||
['P', 'E', 'T', 'K', 'N', 'R', 'L']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Greek',
|
||||
['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάϊος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'],
|
||||
['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαι', 'Ιουν', 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'],
|
||||
['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'English',
|
||||
['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Spanish',
|
||||
['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
|
||||
['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
|
||||
['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Persian',
|
||||
['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'],
|
||||
['فرو', 'ارد', 'خرد', 'تیر', 'مرد', 'شهر', 'مهر', 'آبا', 'آذر', 'دی', 'بهم', 'اسف'],
|
||||
['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Finnish',
|
||||
['tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu', 'toukokuu', 'kesäkuu', 'heinäkuu', 'elokuu', 'syyskuu', 'lokakuu', 'marraskuu', 'joulukuu'],
|
||||
['tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä', 'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'],
|
||||
['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Faroese',
|
||||
['Januar', 'Februar', 'Mars', 'Apríl', 'Mai', 'Juni', 'Juli', 'August', 'Septembur', 'Oktobur', 'Novembur', 'Desembur'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['Sun', 'Mán', 'Týs', 'Mik', 'Hós', 'Frí', 'Ley']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'French',
|
||||
['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
|
||||
['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Août', 'Sep', 'Oct', 'Nov', 'Déc'],
|
||||
['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Georgia',
|
||||
['იანვარი', 'თებერვალი', 'მარტი', 'აპრილი', 'მაისი', 'ივნისი', 'ივლისი', 'აგვისტო', 'სექტემბერი', 'ოქტომბერი', 'ნოემბერი', 'დეკემბერი'],
|
||||
['იან', 'თებ', 'მარ', 'აპრ', 'მაი', 'ივნ', 'ივლ', 'აგვ', 'სექ', 'ოქტ', 'ნოე', 'დეკ'],
|
||||
['კვი', 'ორშ', 'სამ', 'ოთხ', 'ხუთ', 'პარ', 'შაბ']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Galician',
|
||||
['Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño', 'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro'],
|
||||
['Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xuñ', 'Xul', 'Ago', 'Set', 'Out', 'Nov', 'Dec'],
|
||||
['Dom', 'Lun', 'Mar', 'Mér', 'Xov', 'Ven', 'Sáb']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,14 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Hebrew',
|
||||
['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'],
|
||||
['ינו', 'פבר', 'מרץ', 'אפר', 'מאי', 'יונ', 'יול', 'אוג', 'ספט', 'אוק', 'נוב', 'דצמ'],
|
||||
['א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ש']
|
||||
)
|
||||
|
||||
language.rtl = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Croatian',
|
||||
['Siječanj', 'Veljača', 'Ožujak', 'Travanj', 'Svibanj', 'Lipanj', 'Srpanj', 'Kolovoz', 'Rujan', 'Listopad', 'Studeni', 'Prosinac'],
|
||||
['Sij', 'Velj', 'Ožu', 'Tra', 'Svi', 'Lip', 'Srp', 'Kol', 'Ruj', 'Lis', 'Stu', 'Pro'],
|
||||
['Ned', 'Pon', 'Uto', 'Sri', 'Čet', 'Pet', 'Sub']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Hungarian',
|
||||
['Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'],
|
||||
['Jan', 'Febr', 'Márc', 'Ápr', 'Máj', 'Jún', 'Júl', 'Aug', 'Szept', 'Okt', 'Nov', 'Dec'],
|
||||
['Vas', 'Hét', 'Ke', 'Sze', 'Csü', 'Pén', 'Szo']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Indonesian',
|
||||
['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Icelandic',
|
||||
['Janúar', 'Febrúar', 'Mars', 'Apríl', 'Maí', 'Júní', 'Júlí', 'Ágúst', 'September', 'Október', 'Nóvember', 'Desember'],
|
||||
['Jan', 'Feb', 'Mars', 'Apr', 'Maí', 'Jún', 'Júl', 'Ágú', 'Sep', 'Okt', 'Nóv', 'Des'],
|
||||
['Sun', 'Mán', 'Þri', 'Mið', 'Fim', 'Fös', 'Lau']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Italian',
|
||||
['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
|
||||
['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
|
||||
['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,15 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Japanese',
|
||||
['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
['日', '月', '火', '水', '木', '金', '土']
|
||||
)
|
||||
|
||||
language.yearSuffix = '年'
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Kazakh',
|
||||
['Қаңтар', 'Ақпан', 'Наурыз', 'Сәуір', 'Мамыр', 'Маусым', 'Шілде', 'Тамыз', 'Қыркүйек', 'Қазан', 'Қараша', 'Желтоқсан'],
|
||||
['Қаң', 'Ақп', 'Нау', 'Сәу', 'Мам', 'Мау', 'Шіл', 'Там', 'Қыр', 'Қаз', 'Қар', 'Жел'],
|
||||
['Жк', 'Дй', 'Сй', 'Ср', 'Бй', 'Жм', 'Сн']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,14 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Korean',
|
||||
['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
|
||||
['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
|
||||
['일', '월', '화', '수', '목', '금', '토']
|
||||
)
|
||||
language.yearSuffix = '년'
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Luxembourgish',
|
||||
['Januar', 'Februar', 'Mäerz', 'Abrëll', 'Mee', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
['Jan', 'Feb', 'Mäe', 'Abr', 'Mee', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
|
||||
['So.', 'Mé.', 'Dë.', 'Më.', 'Do.', 'Fr.', 'Sa.']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,14 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Lithuanian',
|
||||
['Sausis', 'Vasaris', 'Kovas', 'Balandis', 'Gegužė', 'Birželis', 'Liepa', 'Rugpjūtis', 'Rugsėjis', 'Spalis', 'Lapkritis', 'Gruodis'],
|
||||
['Sau', 'Vas', 'Kov', 'Bal', 'Geg', 'Bir', 'Lie', 'Rugp', 'Rugs', 'Spa', 'Lap', 'Gru'],
|
||||
['Sek', 'Pir', 'Ant', 'Tre', 'Ket', 'Pen', 'Šeš']
|
||||
)
|
||||
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Latvian',
|
||||
['Janvāris', 'Februāris', 'Marts', 'Aprīlis', 'Maijs', 'Jūnijs', 'Jūlijs', 'Augusts', 'Septembris', 'Oktobris', 'Novembris', 'Decembris'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jūn', 'Jūl', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Macedonian',
|
||||
['Јануари', 'Февруари', 'Март', 'Април', 'Мај', 'Јуни', 'Јули', 'Август', 'Септември', 'Октомври', 'Ноември', 'Декември'],
|
||||
['Јан', 'Фев', 'Мар', 'Апр', 'Мај', 'Јун', 'Јул', 'Авг', 'Сеп', 'Окт', 'Ное', 'Дек'],
|
||||
['Нед', 'Пон', 'Вто', 'Сре', 'Чет', 'Пет', 'Саб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,14 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Mongolia',
|
||||
['1 дүгээр сар', '2 дугаар сар', '3 дугаар сар', '4 дүгээр сар', '5 дугаар сар', '6 дугаар сар', '7 дугаар сар', '8 дугаар сар', '9 дүгээр сар', '10 дугаар сар', '11 дүгээр сар', '12 дугаар сар'],
|
||||
['1-р сар', '2-р сар', '3-р сар', '4-р сар', '5-р сар', '6-р сар', '7-р сар', '8-р сар', '9-р сар', '10-р сар', '11-р сар', '12-р сар'],
|
||||
['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя']
|
||||
)
|
||||
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Norwegian Bokmål',
|
||||
['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Dutch',
|
||||
['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
|
||||
['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
|
||||
['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Polish',
|
||||
['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
|
||||
['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
|
||||
['Nd', 'Pn', 'Wt', 'Śr', 'Czw', 'Pt', 'Sob']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Brazilian',
|
||||
['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
|
||||
['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
|
||||
['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Romanian',
|
||||
['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'],
|
||||
['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sep', 'Oct', 'Noi', 'Dec'],
|
||||
['D', 'L', 'Ma', 'Mi', 'J', 'V', 'S']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Russian',
|
||||
['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
|
||||
['Янв', 'Февр', 'Март', 'Апр', 'Май', 'Июнь', 'Июль', 'Авг', 'Сент', 'Окт', 'Нояб', 'Дек'],
|
||||
['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Slovakian',
|
||||
['január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'],
|
||||
['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl', 'aug', 'sep', 'okt', 'nov', 'dec'],
|
||||
['ne', 'po', 'ut', 'st', 'št', 'pi', 'so']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Sloveian',
|
||||
['Januar', 'Februar', 'Marec', 'April', 'Maj', 'Junij', 'Julij', 'Avgust', 'September', 'Oktober', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Ned', 'Pon', 'Tor', 'Sre', 'Čet', 'Pet', 'Sob']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Serbian in Cyrillic script',
|
||||
['Јануар', 'Фебруар', 'Март', 'Април', 'Мај', 'Јун', 'Јул', 'Август', 'Септембар', 'Октобар', 'Новембар', 'Децембар'],
|
||||
['Јан', 'Феб', 'Мар', 'Апр', 'Мај', 'Јун', 'Јул', 'Авг', 'Сеп', 'Окт', 'Нов', 'Дец'],
|
||||
['Нед', 'Пон', 'Уто', 'Сре', 'Чет', 'Пет', 'Суб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Serbian',
|
||||
['Januar', 'Februar', 'Mart', 'April', 'Maj', 'Jun', 'Jul', 'Avgust', 'Septembar', 'Oktobar', 'Novembar', 'Decembar'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Ned', 'Pon', 'Uto', 'Sre', 'Čet', 'Pet', 'Sub']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Swedish',
|
||||
['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Thai',
|
||||
['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'],
|
||||
['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
|
||||
['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Turkish',
|
||||
['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
|
||||
['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
|
||||
['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Ukraine',
|
||||
['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'],
|
||||
['Січ', 'Лют', 'Бер', 'Квіт', 'Трав', 'Чер', 'Лип', 'Серп', 'Вер', 'Жовт', 'Лист', 'Груд'],
|
||||
['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,14 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Urdu',
|
||||
['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
|
||||
['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
|
||||
['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'ہفتہ']
|
||||
)
|
||||
|
||||
language.rtl = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,10 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Vietnamese',
|
||||
['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6', 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12'],
|
||||
['T 01', 'T 02', 'T 03', 'T 04', 'T 05', 'T 06', 'T 07', 'T 08', 'T 09', 'T 10', 'T 11', 'T 12'],
|
||||
['CN', 'Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,11 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Chinese_HK',
|
||||
['壹月', '贰月', '叁月', '肆月', '伍月', '陆月', '柒月', '捌月', '玖月', '拾月', '拾壹月', '拾贰月'],
|
||||
['壹月', '贰月', '叁月', '肆月', '伍月', '陆月', '柒月', '捌月', '玖月', '拾月', '拾壹月', '拾贰月'],
|
||||
['日', '壹', '贰', '叁', '肆', '伍', '陆']
|
||||
)
|
||||
language.yearSuffix = '年'
|
||||
|
||||
export default language
|
||||
@ -1,13 +0,0 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Chinese',
|
||||
['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
['日', '一', '二', '三', '四', '五', '六']
|
||||
)
|
||||
language.yearSuffix = '年'
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div
|
||||
:tabindex="searchable ? -1 : tabindex"
|
||||
:class="{'multiselect--active': isOpen, 'multiselect--disabled': disabled, 'multiselect--above': isAbove }"
|
||||
:aria-owns="'listbox-'+id"
|
||||
class="base-select multiselect"
|
||||
:class="multiSelectStyle"
|
||||
:aria-owns="'listbox-' + id"
|
||||
role="combobox"
|
||||
@focus="activate()"
|
||||
@blur="searchable ? false : deactivate()"
|
||||
@ -13,10 +12,10 @@
|
||||
@keyup.esc="deactivate()"
|
||||
>
|
||||
<slot :toggle="toggle" name="caret">
|
||||
<div class="multiselect__select" @mousedown.prevent.stop="toggle()" />
|
||||
<div :class="multiselectSelectStyle" @mousedown.prevent.stop="toggle()" />
|
||||
</slot>
|
||||
<!-- <slot name="clear" :search="search"></slot> -->
|
||||
<div ref="tags" :class="{'in-valid': invalid}" class="multiselect__tags">
|
||||
<div ref="tags" :class="multiSelectTagsStyle">
|
||||
<slot
|
||||
:search="search"
|
||||
:remove="removeElement"
|
||||
@ -24,25 +23,41 @@
|
||||
:is-open="isOpen"
|
||||
name="selection"
|
||||
>
|
||||
<div v-show="visibleValues.length > 0" class="multiselect__tags-wrap">
|
||||
<div
|
||||
v-show="visibleValues.length > 0"
|
||||
:class="multiselectTagsWrapStyle"
|
||||
>
|
||||
<template v-for="(option, index) of visibleValues" @mousedown.prevent>
|
||||
<slot :option="option" :search="search" :remove="removeElement" name="tag">
|
||||
<span :key="index" class="multiselect__tag">
|
||||
<span v-text="getOptionLabel(option)"/>
|
||||
<i class="multiselect__tag-icon" tabindex="1" @keypress.enter.prevent="removeElement(option)" @mousedown.prevent="removeElement(option)"/>
|
||||
<slot
|
||||
:option="option"
|
||||
:search="search"
|
||||
:remove="removeElement"
|
||||
name="tag"
|
||||
>
|
||||
<span :key="index" :class="multiselectTagStyle">
|
||||
<span v-text="getOptionLabel(option)" />
|
||||
<i
|
||||
:class="multiselectTagIconStyle"
|
||||
tabindex="1"
|
||||
@keypress.enter.prevent="removeElement(option)"
|
||||
@mousedown.prevent="removeElement(option)"
|
||||
/>
|
||||
</span>
|
||||
</slot>
|
||||
</template>
|
||||
</div>
|
||||
<template v-if="internalValue && internalValue.length > limit">
|
||||
<slot name="limit">
|
||||
<strong class="multiselect__strong" v-text="limitText(internalValue.length - limit)"/>
|
||||
<strong
|
||||
:class="multiselectStrongStyle"
|
||||
v-text="limitText(internalValue.length - limit)"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
</slot>
|
||||
<transition name="multiselect__loading">
|
||||
<slot name="loading">
|
||||
<div v-show="loading" class="multiselect__spinner"/>
|
||||
<div v-show="loading" :class="multiselectSpinnerStyle" />
|
||||
</slot>
|
||||
</transition>
|
||||
<input
|
||||
@ -54,8 +69,8 @@
|
||||
:value="search"
|
||||
:disabled="disabled"
|
||||
:tabindex="tabindex"
|
||||
:aria-controls="'listbox-'+id"
|
||||
:class="['multiselect__input']"
|
||||
:aria-controls="'listbox-' + id"
|
||||
:class="multiselectInputStyle"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
@ -67,10 +82,10 @@
|
||||
@keydown.up.prevent="pointerBackward()"
|
||||
@keypress.enter.prevent.stop.self="addPointerElement($event)"
|
||||
@keydown.delete.stop="removeLastElement()"
|
||||
>
|
||||
/>
|
||||
<span
|
||||
v-if="isSingleLabelVisible"
|
||||
class="multiselect__single"
|
||||
:class="multiselectSingleStyle"
|
||||
@mousedown.prevent="toggle"
|
||||
>
|
||||
<slot :option="singleValue" name="singleLabel">
|
||||
@ -83,16 +98,23 @@
|
||||
v-show="isOpen"
|
||||
ref="list"
|
||||
:style="{ maxHeight: optimizedHeight + 'px' }"
|
||||
class="multiselect__content-wrapper"
|
||||
:class="multiselectContentWrapperStyle"
|
||||
tabindex="-1"
|
||||
@focus="activate"
|
||||
@mousedown.prevent
|
||||
>
|
||||
<ul :style="contentStyle" :id="'listbox-'+id" class="multiselect__content" role="listbox">
|
||||
<slot name="beforeList"/>
|
||||
<ul
|
||||
:style="contentStyle"
|
||||
:id="'listbox-' + id"
|
||||
:class="multiselectContentStyle"
|
||||
role="listbox"
|
||||
>
|
||||
<slot name="beforeList" />
|
||||
<li v-if="multiple && max === internalValue.length">
|
||||
<span class="multiselect__option">
|
||||
<slot name="maxElements"> {{ $t('validation.maximum_options_error', { max: max }) }} </slot>
|
||||
<span :class="multiselectOptionStyle">
|
||||
<slot name="maxElements">
|
||||
{{ $t('validation.maximum_options_error', { max: max }) }}
|
||||
</slot>
|
||||
</span>
|
||||
</li>
|
||||
<template v-if="!max || internalValue.length < max">
|
||||
@ -100,16 +122,21 @@
|
||||
v-for="(option, index) of filteredOptions"
|
||||
:key="index"
|
||||
:id="id + '-' + index"
|
||||
:role="!(option && (option.$isLabel || option.$isDisabled)) ? 'option' : null"
|
||||
class="multiselect__element"
|
||||
:role="
|
||||
!(option && (option.$isLabel || option.$isDisabled))
|
||||
? 'option'
|
||||
: null
|
||||
"
|
||||
:class="multiselectElementStyle"
|
||||
>
|
||||
<span
|
||||
v-if="!(option && (option.$isLabel || option.$isDisabled))"
|
||||
:class="optionHighlight(index, option)"
|
||||
:data-select="option && option.isTag ? tagPlaceholder : selectLabelText"
|
||||
:data-select="
|
||||
option && option.isTag ? tagPlaceholder : selectLabelText
|
||||
"
|
||||
:data-selected="selectedLabelText"
|
||||
:data-deselect="deselectLabelText"
|
||||
class="multiselect__option"
|
||||
@click.stop="select(option)"
|
||||
@mouseenter.self="pointerSet(index)"
|
||||
>
|
||||
@ -122,7 +149,6 @@
|
||||
:data-select="groupSelect && selectGroupLabelText"
|
||||
:data-deselect="groupSelect && deselectGroupLabelText"
|
||||
:class="groupHighlight(index, option)"
|
||||
class="multiselect__option"
|
||||
@mouseenter.self="groupSelect && pointerSet(index)"
|
||||
@mousedown.prevent="selectGroup(option)"
|
||||
>
|
||||
@ -132,24 +158,50 @@
|
||||
</span>
|
||||
</li>
|
||||
</template>
|
||||
<li v-if="showNoOptions && (options.length === 0 && !search && !loading)">
|
||||
<span class="multiselect__option">
|
||||
<li
|
||||
v-if="showNoOptions && (options.length === 0 && !search && !loading)"
|
||||
>
|
||||
<span :class="multiselectOptionStyle">
|
||||
<slot name="noOptions">{{ $t('general.list_is_empty') }}</slot>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<slot name="afterList"/>
|
||||
<slot name="afterList" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CraterTheme from '../theme/index'
|
||||
import multiselectMixin from './multiselectMixin'
|
||||
import pointerMixin from './pointerMixin'
|
||||
|
||||
const {
|
||||
activeBaseSelectContainer,
|
||||
disabledBaseSelectContainer,
|
||||
baseSelectContainer,
|
||||
multiSelect,
|
||||
disabledMultiSelect,
|
||||
multiSelectTags,
|
||||
multiSelectTagsInvalid,
|
||||
multiSelectTagsDefaultColor,
|
||||
disabledMultiSelectTags,
|
||||
multiselectTagsWrap,
|
||||
multiselectTag,
|
||||
multiselectTagIcon,
|
||||
multiselectStrong,
|
||||
multiselectSpinner,
|
||||
multiselectInput,
|
||||
multiselectSingle,
|
||||
multiselectContentWrapper,
|
||||
multiselectContent,
|
||||
multiselectOption,
|
||||
multiselectElement,
|
||||
} = CraterTheme.BaseSelect
|
||||
|
||||
export default {
|
||||
name: 'vue-multiselect',
|
||||
name: 'VueMultiselect',
|
||||
mixins: [multiselectMixin, pointerMixin],
|
||||
props: {
|
||||
/**
|
||||
@ -159,7 +211,7 @@ export default {
|
||||
*/
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* String to show when pointing to an option
|
||||
@ -168,7 +220,7 @@ export default {
|
||||
*/
|
||||
selectLabel: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* String to show when pointing to an option
|
||||
@ -177,7 +229,7 @@ export default {
|
||||
*/
|
||||
selectGroupLabel: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* String to show next to selected option
|
||||
@ -186,7 +238,7 @@ export default {
|
||||
*/
|
||||
selectedLabel: {
|
||||
type: String,
|
||||
default: 'Selected'
|
||||
default: 'Selected',
|
||||
},
|
||||
/**
|
||||
* String to show when pointing to an already selected option
|
||||
@ -195,7 +247,7 @@ export default {
|
||||
*/
|
||||
deselectLabel: {
|
||||
type: String,
|
||||
default: 'Press enter to remove'
|
||||
default: 'Press enter to remove',
|
||||
},
|
||||
/**
|
||||
* String to show when pointing to an already selected option
|
||||
@ -204,7 +256,7 @@ export default {
|
||||
*/
|
||||
deselectGroupLabel: {
|
||||
type: String,
|
||||
default: 'Press enter to deselect group'
|
||||
default: 'Press enter to deselect group',
|
||||
},
|
||||
/**
|
||||
* Decide whether to show pointer labels
|
||||
@ -213,7 +265,7 @@ export default {
|
||||
*/
|
||||
showLabels: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* Limit the display of selected options. The rest will be hidden within the limitText string.
|
||||
@ -222,7 +274,7 @@ export default {
|
||||
*/
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 99999
|
||||
default: 99999,
|
||||
},
|
||||
/**
|
||||
* Sets maxHeight style value of the dropdown
|
||||
@ -231,7 +283,7 @@ export default {
|
||||
*/
|
||||
maxHeight: {
|
||||
type: Number,
|
||||
default: 300
|
||||
default: 300,
|
||||
},
|
||||
/**
|
||||
* Function that process the message shown when selected
|
||||
@ -242,7 +294,7 @@ export default {
|
||||
*/
|
||||
limitText: {
|
||||
type: Function,
|
||||
default: count => `and ${count} more`
|
||||
default: (count) => `and ${count} more`,
|
||||
},
|
||||
/**
|
||||
* Set true to trigger the loading spinner.
|
||||
@ -251,7 +303,7 @@ export default {
|
||||
*/
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Disables the multiselect if true.
|
||||
@ -260,7 +312,7 @@ export default {
|
||||
*/
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Fixed opening direction
|
||||
@ -269,7 +321,7 @@ export default {
|
||||
*/
|
||||
openDirection: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* Shows slot with message about empty options
|
||||
@ -278,54 +330,54 @@ export default {
|
||||
*/
|
||||
showNoOptions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
showNoResults: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
tabindex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
invalid: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isSingleLabelVisible () {
|
||||
isSingleLabelVisible() {
|
||||
return (
|
||||
(this.singleValue || this.singleValue === 0) &&
|
||||
(!this.isOpen || !this.searchable) &&
|
||||
!this.visibleValues.length
|
||||
)
|
||||
},
|
||||
isPlaceholderVisible () {
|
||||
isPlaceholderVisible() {
|
||||
return !this.internalValue.length && (!this.searchable || !this.isOpen)
|
||||
},
|
||||
visibleValues () {
|
||||
visibleValues() {
|
||||
return this.multiple ? this.internalValue.slice(0, this.limit) : []
|
||||
},
|
||||
singleValue () {
|
||||
singleValue() {
|
||||
return this.internalValue[0]
|
||||
},
|
||||
deselectLabelText () {
|
||||
deselectLabelText() {
|
||||
return this.showLabels ? this.deselectLabel : ''
|
||||
},
|
||||
deselectGroupLabelText () {
|
||||
deselectGroupLabelText() {
|
||||
return this.showLabels ? this.deselectGroupLabel : ''
|
||||
},
|
||||
selectLabelText () {
|
||||
selectLabelText() {
|
||||
return this.showLabels ? this.selectLabel : ''
|
||||
},
|
||||
selectGroupLabelText () {
|
||||
selectGroupLabelText() {
|
||||
return this.showLabels ? this.selectGroupLabel : ''
|
||||
},
|
||||
selectedLabelText () {
|
||||
selectedLabelText() {
|
||||
return this.showLabels ? this.selectedLabel : ''
|
||||
},
|
||||
inputStyle () {
|
||||
inputStyle() {
|
||||
if (
|
||||
this.searchable ||
|
||||
(this.multiple && this.value && this.value.length)
|
||||
@ -334,15 +386,17 @@ export default {
|
||||
|
||||
return this.isOpen
|
||||
? { width: '100%' }
|
||||
: ((this.value) ? { width: '0', position: 'absolute', padding: '0' } : '')
|
||||
: this.value
|
||||
? { width: '0', position: 'absolute', padding: '0' }
|
||||
: ''
|
||||
}
|
||||
},
|
||||
contentStyle () {
|
||||
contentStyle() {
|
||||
return this.options.length
|
||||
? { display: 'inline-block' }
|
||||
: { display: 'block' }
|
||||
},
|
||||
isAbove () {
|
||||
isAbove() {
|
||||
if (this.openDirection === 'above' || this.openDirection === 'top') {
|
||||
return true
|
||||
} else if (
|
||||
@ -354,7 +408,7 @@ export default {
|
||||
return this.preferredOpenDirection === 'above'
|
||||
}
|
||||
},
|
||||
showSearchInput () {
|
||||
showSearchInput() {
|
||||
return (
|
||||
this.searchable &&
|
||||
(this.hasSingleSelectedSlot &&
|
||||
@ -362,7 +416,466 @@ export default {
|
||||
? this.isOpen
|
||||
: true)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
multiSelectStyle() {
|
||||
let style = ['multiselect--active', baseSelectContainer]
|
||||
if (this.isOpen) {
|
||||
style.push(activeBaseSelectContainer)
|
||||
}
|
||||
if (this.disabled) {
|
||||
style.push(disabledBaseSelectContainer)
|
||||
}
|
||||
if (this.isAbove) {
|
||||
style.push('multiselect--above')
|
||||
}
|
||||
return style
|
||||
},
|
||||
multiselectSelectStyle() {
|
||||
let style = [multiSelect]
|
||||
|
||||
if (this.disabled) {
|
||||
style.push(disabledMultiSelect)
|
||||
}
|
||||
|
||||
return style
|
||||
},
|
||||
multiSelectTagsStyle() {
|
||||
let style = [multiSelectTags]
|
||||
|
||||
if (this.invalid) {
|
||||
style.push(multiSelectTagsInvalid)
|
||||
} else {
|
||||
style.push(multiSelectTagsDefaultColor)
|
||||
}
|
||||
|
||||
if (this.disabled) {
|
||||
style.push(disabledMultiSelectTags)
|
||||
}
|
||||
|
||||
return style
|
||||
},
|
||||
multiselectTagsWrapStyle() {
|
||||
return [multiselectTagsWrap]
|
||||
},
|
||||
multiselectTagStyle() {
|
||||
return [multiselectTag]
|
||||
},
|
||||
multiselectTagIconStyle() {
|
||||
return [multiselectTagIcon]
|
||||
},
|
||||
multiselectStrongStyle() {
|
||||
return [multiselectStrong]
|
||||
},
|
||||
multiselectSpinnerStyle() {
|
||||
return [multiselectSpinner]
|
||||
},
|
||||
multiselectInputStyle() {
|
||||
return [multiselectInput]
|
||||
},
|
||||
multiselectSingleStyle() {
|
||||
return [multiselectSingle]
|
||||
},
|
||||
multiselectContentWrapperStyle() {
|
||||
return [multiselectContentWrapper]
|
||||
},
|
||||
multiselectContentStyle() {
|
||||
return [multiselectContent]
|
||||
},
|
||||
multiselectOptionStyle() {
|
||||
return [multiselectOption]
|
||||
},
|
||||
multiselectElementStyle() {
|
||||
return [multiselectElement]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
fieldset[disabled] .multiselect {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.multiselect__spinner {
|
||||
right: 1px;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.multiselect__spinner:before,
|
||||
.multiselect__spinner:after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -8px 0 0 -8px;
|
||||
z-index: 5;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 100%;
|
||||
border-color: #41b883 transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
box-shadow: 0 0 0 1px transparent;
|
||||
}
|
||||
|
||||
.multiselect__spinner:before {
|
||||
animation: spinning 2.4s cubic-bezier(0.41, 0.26, 0.2, 0.62);
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.multiselect__spinner:after {
|
||||
animation: spinning 2.4s cubic-bezier(0.51, 0.09, 0.21, 0.8);
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.multiselect__loading-enter-active,
|
||||
.multiselect__loading-leave-active {
|
||||
transition: opacity 0.4s ease-in-out;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.multiselect__loading-enter,
|
||||
.multiselect__loading-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.multiselect,
|
||||
.multiselect__input,
|
||||
.multiselect__single {
|
||||
font-family: inherit;
|
||||
// font-size: 14px;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
box-sizing: content-box;
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
text-align: left;
|
||||
color: #35495e;
|
||||
}
|
||||
|
||||
.multiselect * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.multiselect:focus {
|
||||
border: 1px solid #817ae3 !important;
|
||||
}
|
||||
|
||||
.multiselect--disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.multiselect--active:not(.multiselect--above) .multiselect__current,
|
||||
.multiselect--active:not(.multiselect--above) .multiselect__input,
|
||||
.multiselect--active:not(.multiselect--above) .multiselect__tags {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.multiselect--active .multiselect__select {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
.multiselect--above.multiselect--active .multiselect__current,
|
||||
.multiselect--above.multiselect--active .multiselect__input,
|
||||
.multiselect--above.multiselect--active .multiselect__tags {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.multiselect__input,
|
||||
.multiselect__single {
|
||||
min-height: 20px;
|
||||
transition: border 0.1s ease;
|
||||
}
|
||||
|
||||
.multiselect__input::placeholder {
|
||||
color: #b9c1d1;
|
||||
}
|
||||
|
||||
.multiselect__tag ~ .multiselect__input,
|
||||
.multiselect__tag ~ .multiselect__single {
|
||||
width: auto;
|
||||
}
|
||||
.multiselect__input:hover,
|
||||
.multiselect__single:hover {
|
||||
border-color: #cfcfcf;
|
||||
}
|
||||
.multiselect__input:focus,
|
||||
.multiselect__single:focus {
|
||||
border-color: #a8a8a8;
|
||||
outline: none;
|
||||
}
|
||||
.multiselect__tag {
|
||||
background: #41b883;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.multiselect__tag-icon {
|
||||
font-style: initial;
|
||||
}
|
||||
.multiselect__tag-icon:after {
|
||||
content: '×';
|
||||
color: #266d4d;
|
||||
font-size: 14px;
|
||||
}
|
||||
.multiselect__tag-icon:focus,
|
||||
.multiselect__tag-icon:hover {
|
||||
background: #369a6e;
|
||||
}
|
||||
.multiselect__tag-icon:focus:after,
|
||||
.multiselect__tag-icon:hover:after {
|
||||
color: white;
|
||||
}
|
||||
.multiselect__current {
|
||||
line-height: 16px;
|
||||
min-height: 40px;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: 8px 12px 0;
|
||||
padding-right: 30px;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ebf1fa;
|
||||
cursor: pointer;
|
||||
}
|
||||
.multiselect__select {
|
||||
right: 1px;
|
||||
top: 1px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.multiselect__select:before {
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 65%;
|
||||
color: #a5acc1;
|
||||
margin-top: 4px;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: #a5acc1 transparent transparent transparent;
|
||||
content: '';
|
||||
}
|
||||
.multiselect__placeholder {
|
||||
color: #b9c1d1;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
.multiselect--active .multiselect__placeholder {
|
||||
display: none;
|
||||
}
|
||||
.multiselect__content-wrapper {
|
||||
max-height: 240px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.multiselect--above .multiselect__content-wrapper {
|
||||
bottom: 100%;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom: none;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
.multiselect__content::webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.multiselect__option {
|
||||
min-height: 40px;
|
||||
}
|
||||
.multiselect__option:after {
|
||||
top: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
line-height: 40px;
|
||||
padding-right: 12px;
|
||||
padding-left: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.multiselect__option--highlight {
|
||||
background: #41b883;
|
||||
outline: none;
|
||||
color: white;
|
||||
}
|
||||
.multiselect__option--highlight:after {
|
||||
content: attr(data-select);
|
||||
background: #41b883;
|
||||
color: white;
|
||||
}
|
||||
.multiselect__option--selected {
|
||||
background: #f3f3f3;
|
||||
color: #35495e;
|
||||
font-weight: bold;
|
||||
}
|
||||
.multiselect__option--selected:after {
|
||||
content: attr(data-selected);
|
||||
color: silver;
|
||||
}
|
||||
.multiselect__option--selected.multiselect__option--highlight {
|
||||
background: #ff6a6a;
|
||||
color: #fff;
|
||||
}
|
||||
.multiselect__option--selected.multiselect__option--highlight:after {
|
||||
background: #ff6a6a;
|
||||
content: attr(data-deselect);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.multiselect--disabled .multiselect__current,
|
||||
.multiselect--disabled .multiselect__select {
|
||||
background: #ebf1fa;
|
||||
color: #b9c1d1;
|
||||
}
|
||||
|
||||
.multiselect--disabled .multiselect__input,
|
||||
.multiselect--disabled .multiselect__single {
|
||||
background: #ebf1fa;
|
||||
color: #b9c1d1;
|
||||
}
|
||||
|
||||
.multiselect__option--disabled {
|
||||
background: transparent !important;
|
||||
color: #dddddd !important;
|
||||
cursor: text;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.multiselect__option--group {
|
||||
background: #ededed;
|
||||
color: #35495e;
|
||||
}
|
||||
|
||||
.multiselect__option--group.multiselect__option--highlight {
|
||||
background: #35495e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.multiselect__option--group.multiselect__option--highlight:after {
|
||||
background: #35495e;
|
||||
}
|
||||
|
||||
.multiselect__option--disabled.multiselect__option--highlight {
|
||||
background: #dedede;
|
||||
}
|
||||
|
||||
.multiselect__option--group-selected.multiselect__option--highlight {
|
||||
background: #ff6a6a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.multiselect__option--group-selected.multiselect__option--highlight:after {
|
||||
background: #ff6a6a;
|
||||
content: attr(data-deselect);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.multiselect-enter-active,
|
||||
.multiselect-leave-active {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.multiselect-enter,
|
||||
.multiselect-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
*[dir='rtl'] .multiselect {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
*[dir='rtl'] .multiselect__select {
|
||||
right: auto;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
*[dir='rtl'] .multiselect__tags {
|
||||
padding: 8px 8px 0px 40px;
|
||||
}
|
||||
|
||||
*[dir='rtl'] .multiselect__content {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
*[dir='rtl'] .multiselect__option:after {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
*[dir='rtl'] .multiselect__clear {
|
||||
right: auto;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
*[dir='rtl'] .multiselect__spinner {
|
||||
right: auto;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
@keyframes spinning {
|
||||
from {
|
||||
transform: rotate(0);
|
||||
}
|
||||
to {
|
||||
transform: rotate(2turn);
|
||||
}
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
.multiselect__option--highlight {
|
||||
background: #5851d8;
|
||||
color: #040405;
|
||||
font-weight: normal !important;
|
||||
|
||||
&.multiselect__option--selected {
|
||||
background: #ebf1fa;
|
||||
color: #040405;
|
||||
font-size: 1rem;
|
||||
font-weight: normal !important;
|
||||
|
||||
&::after {
|
||||
background: #040405;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: #040405;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.multiselect__option--selected {
|
||||
font-weight: normal !important;
|
||||
background: #ebf1fa;
|
||||
}
|
||||
|
||||
.multiselect__tags-wrap .multiselect__tag {
|
||||
background: #5851d8;
|
||||
color: #040405;
|
||||
|
||||
.multiselect__tag-icon {
|
||||
&:hover {
|
||||
background: #5851d8;
|
||||
}
|
||||
|
||||
&::after {
|
||||
color: #040405;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
border: 1px solid #fb7178;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
function isEmpty (opt) {
|
||||
function isEmpty(opt) {
|
||||
if (opt === 0) return false
|
||||
if (Array.isArray(opt) && opt.length === 0) return true
|
||||
return !opt
|
||||
}
|
||||
|
||||
function not (fun) {
|
||||
function not(fun) {
|
||||
return (...params) => !fun(...params)
|
||||
}
|
||||
|
||||
function includes (str, query) {
|
||||
function includes(str, query) {
|
||||
/* istanbul ignore else */
|
||||
if (str === undefined) str = 'undefined'
|
||||
if (str === null) str = 'null'
|
||||
@ -17,22 +17,24 @@ function includes (str, query) {
|
||||
return text.indexOf(query.trim()) !== -1
|
||||
}
|
||||
|
||||
function filterOptions (options, search, label, customLabel) {
|
||||
return options.filter(option => includes(customLabel(option, label), search))
|
||||
function filterOptions(options, search, label, customLabel) {
|
||||
return options.filter((option) =>
|
||||
includes(customLabel(option, label), search)
|
||||
)
|
||||
}
|
||||
|
||||
function stripGroups (options) {
|
||||
return options.filter(option => !option.$isLabel)
|
||||
function stripGroups(options) {
|
||||
return options.filter((option) => !option.$isLabel)
|
||||
}
|
||||
|
||||
function flattenOptions (values, label) {
|
||||
function flattenOptions(values, label) {
|
||||
return (options) =>
|
||||
options.reduce((prev, curr) => {
|
||||
/* istanbul ignore else */
|
||||
if (curr[values] && curr[values].length) {
|
||||
prev.push({
|
||||
$groupLabel: curr[label],
|
||||
$isLabel: true
|
||||
$isLabel: true,
|
||||
})
|
||||
return prev.concat(curr[values])
|
||||
}
|
||||
@ -40,40 +42,47 @@ function flattenOptions (values, label) {
|
||||
}, [])
|
||||
}
|
||||
|
||||
function filterGroups (search, label, values, groupLabel, customLabel) {
|
||||
function filterGroups(search, label, values, groupLabel, customLabel) {
|
||||
return (groups) =>
|
||||
groups.map(group => {
|
||||
groups.map((group) => {
|
||||
/* istanbul ignore else */
|
||||
if (!group[values]) {
|
||||
console.warn(`Options passed to vue-multiselect do not contain groups, despite the config.`)
|
||||
console.warn(
|
||||
`Options passed to vue-multiselect do not contain groups, despite the config.`
|
||||
)
|
||||
return []
|
||||
}
|
||||
const groupOptions = filterOptions(group[values], search, label, customLabel)
|
||||
const groupOptions = filterOptions(
|
||||
group[values],
|
||||
search,
|
||||
label,
|
||||
customLabel
|
||||
)
|
||||
|
||||
return groupOptions.length
|
||||
? {
|
||||
[groupLabel]: group[groupLabel],
|
||||
[values]: groupOptions
|
||||
}
|
||||
[groupLabel]: group[groupLabel],
|
||||
[values]: groupOptions,
|
||||
}
|
||||
: []
|
||||
})
|
||||
}
|
||||
|
||||
const flow = (...fns) => x => fns.reduce((v, f) => f(v), x)
|
||||
const flow = (...fns) => (x) => fns.reduce((v, f) => f(v), x)
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
search: '',
|
||||
isOpen: false,
|
||||
preferredOpenDirection: 'below',
|
||||
optimizedHeight: this.maxHeight
|
||||
optimizedHeight: this.maxHeight,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
initialSearch: {
|
||||
type: String,
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* Decide whether to filter the results based on search query.
|
||||
@ -82,7 +91,7 @@ export default {
|
||||
*/
|
||||
internalSearch: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* Array of available options: Objects, Strings or Integers.
|
||||
@ -92,7 +101,7 @@ export default {
|
||||
*/
|
||||
options: {
|
||||
type: Array,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
/**
|
||||
* Equivalent to the `multiple` attribute on a `<select>` input.
|
||||
@ -101,7 +110,7 @@ export default {
|
||||
*/
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Presets the selected options value.
|
||||
@ -109,9 +118,9 @@ export default {
|
||||
*/
|
||||
value: {
|
||||
type: null,
|
||||
default () {
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Key to compare objects
|
||||
@ -119,7 +128,7 @@ export default {
|
||||
* @type {String}
|
||||
*/
|
||||
trackBy: {
|
||||
type: String
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* Label to look for in option Object
|
||||
@ -127,7 +136,7 @@ export default {
|
||||
* @type {String}
|
||||
*/
|
||||
label: {
|
||||
type: String
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* Enable/disable search in options
|
||||
@ -136,7 +145,7 @@ export default {
|
||||
*/
|
||||
searchable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* Clear the search input after `)
|
||||
@ -145,7 +154,7 @@ export default {
|
||||
*/
|
||||
clearOnSelect: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* Hide already selected options
|
||||
@ -154,7 +163,7 @@ export default {
|
||||
*/
|
||||
hideSelected: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Equivalent to the `placeholder` attribute on a `<select>` input.
|
||||
@ -163,7 +172,7 @@ export default {
|
||||
*/
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'Select option'
|
||||
default: 'Select option',
|
||||
},
|
||||
/**
|
||||
* Allow to remove all selected values
|
||||
@ -172,7 +181,7 @@ export default {
|
||||
*/
|
||||
allowEmpty: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* Reset this.internalValue, this.search after this.internalValue changes.
|
||||
@ -182,7 +191,7 @@ export default {
|
||||
*/
|
||||
resetAfter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Enable/disable closing after selecting an option
|
||||
@ -191,7 +200,7 @@ export default {
|
||||
*/
|
||||
closeOnSelect: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* Function to interpolate the custom label
|
||||
@ -200,10 +209,10 @@ export default {
|
||||
*/
|
||||
customLabel: {
|
||||
type: Function,
|
||||
default (option, label) {
|
||||
default(option, label) {
|
||||
if (isEmpty(option)) return ''
|
||||
return label ? option[label] : option
|
||||
}
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Disable / Enable tagging
|
||||
@ -212,16 +221,16 @@ export default {
|
||||
*/
|
||||
taggable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* String to show when highlighting a potential tag
|
||||
* @default 'Press enter to create a tag'
|
||||
* @type {String}
|
||||
*/
|
||||
*/
|
||||
tagPlaceholder: {
|
||||
type: String,
|
||||
default: 'Press enter to create a tag'
|
||||
default: 'Press enter to create a tag',
|
||||
},
|
||||
/**
|
||||
* By default new tags will appear above the search results.
|
||||
@ -229,56 +238,56 @@ export default {
|
||||
* and will proritize the search results
|
||||
* @default 'top'
|
||||
* @type {String}
|
||||
*/
|
||||
*/
|
||||
tagPosition: {
|
||||
type: String,
|
||||
default: 'top'
|
||||
default: 'top',
|
||||
},
|
||||
/**
|
||||
* Number of allowed selected options. No limit if 0.
|
||||
* @default 0
|
||||
* @type {Number}
|
||||
*/
|
||||
*/
|
||||
max: {
|
||||
type: [Number, Boolean],
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Will be passed with all events as second param.
|
||||
* Useful for identifying events origin.
|
||||
* @default null
|
||||
* @type {String|Integer}
|
||||
*/
|
||||
*/
|
||||
id: {
|
||||
default: null
|
||||
default: null,
|
||||
},
|
||||
/**
|
||||
* Limits the options displayed in the dropdown
|
||||
* to the first X options.
|
||||
* @default 1000
|
||||
* @type {Integer}
|
||||
*/
|
||||
*/
|
||||
optionsLimit: {
|
||||
type: Number,
|
||||
default: 1000
|
||||
default: 1000,
|
||||
},
|
||||
/**
|
||||
* Name of the property containing
|
||||
* the group values
|
||||
* @default 1000
|
||||
* @type {String}
|
||||
*/
|
||||
*/
|
||||
groupValues: {
|
||||
type: String
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* Name of the property containing
|
||||
* the group label
|
||||
* @default 1000
|
||||
* @type {String}
|
||||
*/
|
||||
*/
|
||||
groupLabel: {
|
||||
type: String
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* Allow to select all group values
|
||||
@ -288,43 +297,45 @@ export default {
|
||||
*/
|
||||
groupSelect: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Array of keyboard keys to block
|
||||
* when selecting
|
||||
* @default 1000
|
||||
* @type {String}
|
||||
*/
|
||||
*/
|
||||
blockKeys: {
|
||||
type: Array,
|
||||
default () {
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Prevent from wiping up the search value
|
||||
* @default false
|
||||
* @type {Boolean}
|
||||
*/
|
||||
*/
|
||||
preserveSearch: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* Select 1st options if value is empty
|
||||
* @default false
|
||||
* @type {Boolean}
|
||||
*/
|
||||
*/
|
||||
preselectFirst: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
/* istanbul ignore else */
|
||||
if (!this.multiple && this.max) {
|
||||
console.warn('[Vue-Multiselect warn]: Max prop should not be used when prop Multiple equals false.')
|
||||
console.warn(
|
||||
'[Vue-Multiselect warn]: Max prop should not be used when prop Multiple equals false.'
|
||||
)
|
||||
}
|
||||
if (
|
||||
this.preselectFirst &&
|
||||
@ -339,12 +350,14 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
internalValue () {
|
||||
internalValue() {
|
||||
return this.value || this.value === 0
|
||||
? Array.isArray(this.value) ? this.value : [this.value]
|
||||
? Array.isArray(this.value)
|
||||
? this.value
|
||||
: [this.value]
|
||||
: []
|
||||
},
|
||||
filteredOptions () {
|
||||
filteredOptions() {
|
||||
const search = this.search || ''
|
||||
const normalizedSearch = search.toLowerCase().trim()
|
||||
|
||||
@ -354,9 +367,16 @@ export default {
|
||||
if (this.internalSearch) {
|
||||
options = this.groupValues
|
||||
? this.filterAndFlat(options, normalizedSearch, this.label)
|
||||
: filterOptions(options, normalizedSearch, this.label, this.customLabel)
|
||||
: filterOptions(
|
||||
options,
|
||||
normalizedSearch,
|
||||
this.label,
|
||||
this.customLabel
|
||||
)
|
||||
} else {
|
||||
options = this.groupValues ? flattenOptions(this.groupValues, this.groupLabel)(options) : options
|
||||
options = this.groupValues
|
||||
? flattenOptions(this.groupValues, this.groupLabel)(options)
|
||||
: options
|
||||
}
|
||||
|
||||
options = this.hideSelected
|
||||
@ -364,7 +384,11 @@ export default {
|
||||
: options
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (this.taggable && normalizedSearch.length && !this.isExistingOption(normalizedSearch)) {
|
||||
if (
|
||||
this.taggable &&
|
||||
normalizedSearch.length &&
|
||||
!this.isExistingOption(normalizedSearch)
|
||||
) {
|
||||
if (this.tagPosition === 'bottom') {
|
||||
options.push({ isTag: true, label: search })
|
||||
} else {
|
||||
@ -374,57 +398,71 @@ export default {
|
||||
|
||||
return options.slice(0, this.optionsLimit)
|
||||
},
|
||||
valueKeys () {
|
||||
valueKeys() {
|
||||
if (this.trackBy) {
|
||||
return this.internalValue.map(element => element[this.trackBy])
|
||||
return this.internalValue.map((element) => element[this.trackBy])
|
||||
} else {
|
||||
return this.internalValue
|
||||
}
|
||||
},
|
||||
optionKeys () {
|
||||
const options = this.groupValues ? this.flatAndStrip(this.options) : this.options
|
||||
return options.map(element => this.customLabel(element, this.label).toString().toLowerCase())
|
||||
optionKeys() {
|
||||
const options = this.groupValues
|
||||
? this.flatAndStrip(this.options)
|
||||
: this.options
|
||||
return options.map((element) =>
|
||||
this.customLabel(element, this.label).toString().toLowerCase()
|
||||
)
|
||||
},
|
||||
currentOptionLabel () {
|
||||
currentOptionLabel() {
|
||||
return this.multiple
|
||||
? this.searchable ? '' : this.placeholder
|
||||
? this.searchable
|
||||
? ''
|
||||
: this.placeholder
|
||||
: this.internalValue.length
|
||||
? this.getOptionLabel(this.internalValue[0])
|
||||
: this.searchable ? '' : this.placeholder
|
||||
}
|
||||
? this.getOptionLabel(this.internalValue[0])
|
||||
: this.searchable
|
||||
? ''
|
||||
: this.placeholder
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
internalValue () {
|
||||
internalValue() {
|
||||
/* istanbul ignore else */
|
||||
if (this.resetAfter && this.internalValue.length) {
|
||||
this.search = ''
|
||||
this.$emit('input', this.multiple ? [] : null)
|
||||
}
|
||||
},
|
||||
search () {
|
||||
search() {
|
||||
this.$emit('search-change', this.search, this.id)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Returns the internalValue in a way it can be emited to the parent
|
||||
* @returns {Object||Array||String||Integer}
|
||||
*/
|
||||
getValue () {
|
||||
getValue() {
|
||||
return this.multiple
|
||||
? this.internalValue
|
||||
: this.internalValue.length === 0
|
||||
? null
|
||||
: this.internalValue[0]
|
||||
? null
|
||||
: this.internalValue[0]
|
||||
},
|
||||
/**
|
||||
* Filters and then flattens the options list
|
||||
* @param {Array}
|
||||
* @returns {Array} returns a filtered and flat options list
|
||||
*/
|
||||
filterAndFlat (options, search, label) {
|
||||
filterAndFlat(options, search, label) {
|
||||
return flow(
|
||||
filterGroups(search, label, this.groupValues, this.groupLabel, this.customLabel),
|
||||
filterGroups(
|
||||
search,
|
||||
label,
|
||||
this.groupValues,
|
||||
this.groupLabel,
|
||||
this.customLabel
|
||||
),
|
||||
flattenOptions(this.groupValues, this.groupLabel)
|
||||
)(options)
|
||||
},
|
||||
@ -433,7 +471,7 @@ export default {
|
||||
* @param {Array}
|
||||
* @returns {Array} returns a flat options list without group labels
|
||||
*/
|
||||
flatAndStrip (options) {
|
||||
flatAndStrip(options) {
|
||||
return flow(
|
||||
flattenOptions(this.groupValues, this.groupLabel),
|
||||
stripGroups
|
||||
@ -443,7 +481,7 @@ export default {
|
||||
* Updates the search value
|
||||
* @param {String}
|
||||
*/
|
||||
updateSearch (query) {
|
||||
updateSearch(query) {
|
||||
this.search = query
|
||||
this.$emit('value', this.search)
|
||||
},
|
||||
@ -453,10 +491,8 @@ export default {
|
||||
* @param {String}
|
||||
* @returns {Boolean} returns true if element is available
|
||||
*/
|
||||
isExistingOption (query) {
|
||||
return !this.options
|
||||
? false
|
||||
: this.optionKeys.indexOf(query) > -1
|
||||
isExistingOption(query) {
|
||||
return !this.options ? false : this.optionKeys.indexOf(query) > -1
|
||||
},
|
||||
/**
|
||||
* Finds out if the given element is already present
|
||||
@ -464,10 +500,8 @@ export default {
|
||||
* @param {Object||String||Integer} option passed element to check
|
||||
* @returns {Boolean} returns true if element is selected
|
||||
*/
|
||||
isSelected (option) {
|
||||
const opt = this.trackBy
|
||||
? option[this.trackBy]
|
||||
: option
|
||||
isSelected(option) {
|
||||
const opt = this.trackBy ? option[this.trackBy] : option
|
||||
return this.valueKeys.indexOf(opt) > -1
|
||||
},
|
||||
/**
|
||||
@ -475,7 +509,7 @@ export default {
|
||||
* @param {Object||String||Integer} option passed element to check
|
||||
* @returns {Boolean} returns true if element is disabled
|
||||
*/
|
||||
isOptionDisabled (option) {
|
||||
isOptionDisabled(option) {
|
||||
return !!option.$isDisabled
|
||||
},
|
||||
/**
|
||||
@ -486,7 +520,7 @@ export default {
|
||||
* @param {Object||String||Integer} Passed option
|
||||
* @returns {Object||String}
|
||||
*/
|
||||
getOptionLabel (option) {
|
||||
getOptionLabel(option) {
|
||||
if (isEmpty(option)) return ''
|
||||
/* istanbul ignore else */
|
||||
if (option.isTag) return option.label
|
||||
@ -506,19 +540,22 @@ export default {
|
||||
* @param {Object||String||Integer} option to select/deselect
|
||||
* @param {Boolean} block removing
|
||||
*/
|
||||
select (option, key) {
|
||||
select(option, key) {
|
||||
/* istanbul ignore else */
|
||||
if (option.$isLabel && this.groupSelect) {
|
||||
this.selectGroup(option)
|
||||
return
|
||||
}
|
||||
if (this.blockKeys.indexOf(key) !== -1 ||
|
||||
if (
|
||||
this.blockKeys.indexOf(key) !== -1 ||
|
||||
this.disabled ||
|
||||
option.$isDisabled ||
|
||||
option.$isLabel
|
||||
) return
|
||||
)
|
||||
return
|
||||
/* istanbul ignore else */
|
||||
if (this.max && this.multiple && this.internalValue.length === this.max) return
|
||||
if (this.max && this.multiple && this.internalValue.length === this.max)
|
||||
return
|
||||
/* istanbul ignore else */
|
||||
if (key === 'Tab' && !this.pointerDirty) return
|
||||
if (option.isTag) {
|
||||
@ -553,8 +590,8 @@ export default {
|
||||
*
|
||||
* @param {Object||String||Integer} group to select/deselect
|
||||
*/
|
||||
selectGroup (selectedGroup) {
|
||||
const group = this.options.find(option => {
|
||||
selectGroup(selectedGroup) {
|
||||
const group = this.options.find((option) => {
|
||||
return option[this.groupLabel] === selectedGroup.$groupLabel
|
||||
})
|
||||
|
||||
@ -564,21 +601,18 @@ export default {
|
||||
this.$emit('remove', group[this.groupValues], this.id)
|
||||
|
||||
const newValue = this.internalValue.filter(
|
||||
option => group[this.groupValues].indexOf(option) === -1
|
||||
(option) => group[this.groupValues].indexOf(option) === -1
|
||||
)
|
||||
|
||||
this.$emit('input', newValue, this.id)
|
||||
} else {
|
||||
const optionsToAdd = group[this.groupValues].filter(
|
||||
option => !(this.isOptionDisabled(option) || this.isSelected(option))
|
||||
(option) =>
|
||||
!(this.isOptionDisabled(option) || this.isSelected(option))
|
||||
)
|
||||
|
||||
this.$emit('select', optionsToAdd, this.id)
|
||||
this.$emit(
|
||||
'input',
|
||||
this.internalValue.concat(optionsToAdd),
|
||||
this.id
|
||||
)
|
||||
this.$emit('input', this.internalValue.concat(optionsToAdd), this.id)
|
||||
}
|
||||
},
|
||||
/**
|
||||
@ -586,8 +620,9 @@ export default {
|
||||
*
|
||||
* @param {Object} group to validated selected values against
|
||||
*/
|
||||
wholeGroupSelected (group) {
|
||||
return group[this.groupValues].every(option => this.isSelected(option) || this.isOptionDisabled(option)
|
||||
wholeGroupSelected(group) {
|
||||
return group[this.groupValues].every(
|
||||
(option) => this.isSelected(option) || this.isOptionDisabled(option)
|
||||
)
|
||||
},
|
||||
/**
|
||||
@ -595,7 +630,7 @@ export default {
|
||||
*
|
||||
* @param {Object} group to check for disabled values
|
||||
*/
|
||||
wholeGroupDisabled (group) {
|
||||
wholeGroupDisabled(group) {
|
||||
return group[this.groupValues].every(this.isOptionDisabled)
|
||||
},
|
||||
/**
|
||||
@ -606,7 +641,7 @@ export default {
|
||||
* @param {type} option description
|
||||
* @returns {type} description
|
||||
*/
|
||||
removeElement (option, shouldClose = true) {
|
||||
removeElement(option, shouldClose = true) {
|
||||
/* istanbul ignore else */
|
||||
if (this.disabled) return
|
||||
/* istanbul ignore else */
|
||||
@ -617,13 +652,16 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
const index = typeof option === 'object'
|
||||
? this.valueKeys.indexOf(option[this.trackBy])
|
||||
: this.valueKeys.indexOf(option)
|
||||
const index =
|
||||
typeof option === 'object'
|
||||
? this.valueKeys.indexOf(option[this.trackBy])
|
||||
: this.valueKeys.indexOf(option)
|
||||
|
||||
this.$emit('remove', option, this.id)
|
||||
if (this.multiple) {
|
||||
const newValue = this.internalValue.slice(0, index).concat(this.internalValue.slice(index + 1))
|
||||
const newValue = this.internalValue
|
||||
.slice(0, index)
|
||||
.concat(this.internalValue.slice(index + 1))
|
||||
this.$emit('input', newValue, this.id)
|
||||
} else {
|
||||
this.$emit('input', null, this.id)
|
||||
@ -638,25 +676,36 @@ export default {
|
||||
*
|
||||
* @fires this#removeElement
|
||||
*/
|
||||
removeLastElement () {
|
||||
removeLastElement() {
|
||||
/* istanbul ignore else */
|
||||
if (this.blockKeys.indexOf('Delete') !== -1) return
|
||||
/* istanbul ignore else */
|
||||
if (this.search.length === 0 && Array.isArray(this.internalValue) && this.internalValue.length) {
|
||||
this.removeElement(this.internalValue[this.internalValue.length - 1], false)
|
||||
if (
|
||||
this.search.length === 0 &&
|
||||
Array.isArray(this.internalValue) &&
|
||||
this.internalValue.length
|
||||
) {
|
||||
this.removeElement(
|
||||
this.internalValue[this.internalValue.length - 1],
|
||||
false
|
||||
)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Opens the multiselect’s dropdown.
|
||||
* Sets this.isOpen to TRUE
|
||||
*/
|
||||
activate () {
|
||||
activate() {
|
||||
/* istanbul ignore else */
|
||||
if (this.isOpen || this.disabled) return
|
||||
|
||||
this.adjustPosition()
|
||||
/* istanbul ignore else */
|
||||
if (this.groupValues && this.pointer === 0 && this.filteredOptions.length) {
|
||||
if (
|
||||
this.groupValues &&
|
||||
this.pointer === 0 &&
|
||||
this.filteredOptions.length
|
||||
) {
|
||||
this.pointer = 1
|
||||
}
|
||||
|
||||
@ -674,7 +723,7 @@ export default {
|
||||
* Closes the multiselect’s dropdown.
|
||||
* Sets this.isOpen to FALSE
|
||||
*/
|
||||
deactivate () {
|
||||
deactivate() {
|
||||
/* istanbul ignore else */
|
||||
if (!this.isOpen) return
|
||||
this.isOpen = false
|
||||
@ -694,29 +743,33 @@ export default {
|
||||
* @fires this#activate || this#deactivate
|
||||
* @property {Boolean} isOpen indicates if dropdown is open
|
||||
*/
|
||||
toggle () {
|
||||
this.isOpen
|
||||
? this.deactivate()
|
||||
: this.activate()
|
||||
toggle() {
|
||||
this.isOpen ? this.deactivate() : this.activate()
|
||||
},
|
||||
/**
|
||||
* Updates the hasEnoughSpace variable used for
|
||||
* detecting where to expand the dropdown
|
||||
*/
|
||||
adjustPosition () {
|
||||
adjustPosition() {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
const spaceAbove = this.$el.getBoundingClientRect().top
|
||||
const spaceBelow = window.innerHeight - this.$el.getBoundingClientRect().bottom
|
||||
const spaceBelow =
|
||||
window.innerHeight - this.$el.getBoundingClientRect().bottom
|
||||
const hasEnoughSpaceBelow = spaceBelow > this.maxHeight
|
||||
|
||||
if (hasEnoughSpaceBelow || spaceBelow > spaceAbove || this.openDirection === 'below' || this.openDirection === 'bottom') {
|
||||
if (
|
||||
hasEnoughSpaceBelow ||
|
||||
spaceBelow > spaceAbove ||
|
||||
this.openDirection === 'below' ||
|
||||
this.openDirection === 'bottom'
|
||||
) {
|
||||
this.preferredOpenDirection = 'below'
|
||||
this.optimizedHeight = Math.min(spaceBelow - 40, this.maxHeight)
|
||||
} else {
|
||||
this.preferredOpenDirection = 'above'
|
||||
this.optimizedHeight = Math.min(spaceAbove - 40, this.maxHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import CraterTheme from '../theme/index'
|
||||
const { multiselectOption } = CraterTheme.BaseSelect
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
pointer: 0,
|
||||
pointerDirty: false
|
||||
pointerDirty: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
@ -13,79 +15,106 @@ export default {
|
||||
*/
|
||||
showPointer: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
optionHeight: {
|
||||
type: Number,
|
||||
default: 40
|
||||
}
|
||||
default: 40,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
pointerPosition () {
|
||||
pointerPosition() {
|
||||
return this.pointer * this.optionHeight
|
||||
},
|
||||
visibleElements () {
|
||||
visibleElements() {
|
||||
return this.optimizedHeight / this.optionHeight
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
filteredOptions () {
|
||||
filteredOptions() {
|
||||
this.pointerAdjust()
|
||||
},
|
||||
isOpen () {
|
||||
isOpen() {
|
||||
this.pointerDirty = false
|
||||
},
|
||||
pointer () {
|
||||
this.$refs.search.setAttribute('aria-activedescendant', this.id + '-' + this.pointer.toString())
|
||||
}
|
||||
pointer() {
|
||||
this.$refs.search.setAttribute(
|
||||
'aria-activedescendant',
|
||||
this.id + '-' + this.pointer.toString()
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
optionHighlight (index, option) {
|
||||
return {
|
||||
'multiselect__option--highlight': index === this.pointer && this.showPointer,
|
||||
'multiselect__option--selected': this.isSelected(option)
|
||||
}
|
||||
optionHighlight(index, option) {
|
||||
return [
|
||||
{
|
||||
'multiselect__option--highlight':
|
||||
index === this.pointer && this.showPointer,
|
||||
'multiselect__option--selected': this.isSelected(option),
|
||||
},
|
||||
multiselectOption,
|
||||
]
|
||||
},
|
||||
groupHighlight (index, selectedGroup) {
|
||||
groupHighlight(index, selectedGroup) {
|
||||
if (!this.groupSelect) {
|
||||
return ['multiselect__option--group', 'multiselect__option--disabled']
|
||||
return [
|
||||
'multiselect__option--group',
|
||||
'multiselect__option--disabled',
|
||||
multiselectOption,
|
||||
]
|
||||
}
|
||||
|
||||
const group = this.options.find(option => {
|
||||
const group = this.options.find((option) => {
|
||||
return option[this.groupLabel] === selectedGroup.$groupLabel
|
||||
})
|
||||
|
||||
return group && !this.wholeGroupDisabled(group) ? [
|
||||
'multiselect__option--group',
|
||||
{ 'multiselect__option--highlight': index === this.pointer && this.showPointer },
|
||||
{ 'multiselect__option--group-selected': this.wholeGroupSelected(group) }
|
||||
] : 'multiselect__option--disabled'
|
||||
return group && !this.wholeGroupDisabled(group)
|
||||
? [
|
||||
'multiselect__option--group',
|
||||
{
|
||||
'multiselect__option--highlight':
|
||||
index === this.pointer && this.showPointer,
|
||||
},
|
||||
{
|
||||
'multiselect__option--group-selected': this.wholeGroupSelected(
|
||||
group
|
||||
),
|
||||
},
|
||||
multiselectOption,
|
||||
]
|
||||
: ['multiselect__option--disabled', multiselectOption]
|
||||
},
|
||||
addPointerElement ({ key } = 'Enter') {
|
||||
addPointerElement({ key } = 'Enter') {
|
||||
/* istanbul ignore else */
|
||||
if (this.filteredOptions.length > 0) {
|
||||
this.select(this.filteredOptions[this.pointer], key)
|
||||
}
|
||||
this.pointerReset()
|
||||
},
|
||||
pointerForward () {
|
||||
pointerForward() {
|
||||
/* istanbul ignore else */
|
||||
if (this.pointer < this.filteredOptions.length - 1) {
|
||||
this.pointer++
|
||||
/* istanbul ignore next */
|
||||
if (this.$refs.list.scrollTop <= this.pointerPosition - (this.visibleElements - 1) * this.optionHeight) {
|
||||
this.$refs.list.scrollTop = this.pointerPosition - (this.visibleElements - 1) * this.optionHeight
|
||||
if (
|
||||
this.$refs.list.scrollTop <=
|
||||
this.pointerPosition - (this.visibleElements - 1) * this.optionHeight
|
||||
) {
|
||||
this.$refs.list.scrollTop =
|
||||
this.pointerPosition -
|
||||
(this.visibleElements - 1) * this.optionHeight
|
||||
}
|
||||
/* istanbul ignore else */
|
||||
if (
|
||||
this.filteredOptions[this.pointer] &&
|
||||
this.filteredOptions[this.pointer].$isLabel &&
|
||||
!this.groupSelect
|
||||
) this.pointerForward()
|
||||
)
|
||||
this.pointerForward()
|
||||
}
|
||||
this.pointerDirty = true
|
||||
},
|
||||
pointerBackward () {
|
||||
pointerBackward() {
|
||||
if (this.pointer > 0) {
|
||||
this.pointer--
|
||||
/* istanbul ignore else */
|
||||
@ -97,18 +126,20 @@ export default {
|
||||
this.filteredOptions[this.pointer] &&
|
||||
this.filteredOptions[this.pointer].$isLabel &&
|
||||
!this.groupSelect
|
||||
) this.pointerBackward()
|
||||
)
|
||||
this.pointerBackward()
|
||||
} else {
|
||||
/* istanbul ignore else */
|
||||
if (
|
||||
this.filteredOptions[this.pointer] &&
|
||||
this.filteredOptions[0].$isLabel &&
|
||||
!this.groupSelect
|
||||
) this.pointerForward()
|
||||
)
|
||||
this.pointerForward()
|
||||
}
|
||||
this.pointerDirty = true
|
||||
},
|
||||
pointerReset () {
|
||||
pointerReset() {
|
||||
/* istanbul ignore else */
|
||||
if (!this.closeOnSelect) return
|
||||
this.pointer = 0
|
||||
@ -117,7 +148,7 @@ export default {
|
||||
this.$refs.list.scrollTop = 0
|
||||
}
|
||||
},
|
||||
pointerAdjust () {
|
||||
pointerAdjust() {
|
||||
/* istanbul ignore else */
|
||||
if (this.pointer >= this.filteredOptions.length - 1) {
|
||||
this.pointer = this.filteredOptions.length
|
||||
@ -125,16 +156,17 @@ export default {
|
||||
: 0
|
||||
}
|
||||
|
||||
if (this.filteredOptions.length > 0 &&
|
||||
if (
|
||||
this.filteredOptions.length > 0 &&
|
||||
this.filteredOptions[this.pointer].$isLabel &&
|
||||
!this.groupSelect
|
||||
) {
|
||||
this.pointerForward()
|
||||
}
|
||||
},
|
||||
pointerSet (index) {
|
||||
pointerSet(index) {
|
||||
this.pointer = index
|
||||
this.pointerDirty = true
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
import { pick } from '../helpers'
|
||||
|
||||
export default class Column {
|
||||
constructor (columnComponent) {
|
||||
const properties = pick(columnComponent, [
|
||||
'show', 'label', 'dataType', 'sortable', 'sortBy', 'filterable',
|
||||
'filterOn', 'hidden', 'formatter', 'cellClass', 'headerClass', 'sortAs'
|
||||
])
|
||||
|
||||
for (const property in properties) {
|
||||
this[property] = columnComponent[property]
|
||||
}
|
||||
|
||||
this.template = columnComponent.$scopedSlots.default
|
||||
}
|
||||
|
||||
isFilterable () {
|
||||
return this.filterable
|
||||
}
|
||||
|
||||
getFilterFieldName () {
|
||||
return this.filterOn || this.show
|
||||
}
|
||||
|
||||
isSortable () {
|
||||
return this.sortable
|
||||
}
|
||||
|
||||
getSortPredicate (sortOrder, allColumns) {
|
||||
const sortFieldName = this.getSortFieldName()
|
||||
|
||||
const sortColumn = allColumns.find(column => (column.sortAs === sortFieldName || column.show === sortFieldName))
|
||||
|
||||
const dataType = sortColumn.dataType
|
||||
|
||||
if (dataType.startsWith('date') || dataType === 'numeric') {
|
||||
return (row1, row2) => {
|
||||
const value1 = row1.getSortableValue(sortFieldName)
|
||||
const value2 = row2.getSortableValue(sortFieldName)
|
||||
|
||||
if (sortOrder === 'desc') {
|
||||
return value2 < value1 ? -1 : 1
|
||||
}
|
||||
|
||||
return value1 < value2 ? -1 : 1
|
||||
}
|
||||
}
|
||||
|
||||
return (row1, row2) => {
|
||||
const value1 = row1.getSortableValue(sortFieldName)
|
||||
const value2 = row2.getSortableValue(sortFieldName)
|
||||
|
||||
if (sortOrder === 'desc') {
|
||||
return value2.localeCompare(value1)
|
||||
}
|
||||
|
||||
return value1.localeCompare(value2)
|
||||
}
|
||||
}
|
||||
|
||||
getSortFieldName () {
|
||||
return this.sortBy || this.sortAs || this.show
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
import moment from 'moment'
|
||||
import { get } from '../helpers'
|
||||
|
||||
export default class Row {
|
||||
constructor (data, columns) {
|
||||
this.data = data
|
||||
this.columns = columns
|
||||
}
|
||||
|
||||
getValue (columnName) {
|
||||
return get(this.data, columnName)
|
||||
}
|
||||
|
||||
getColumn (columnName) {
|
||||
return this.columns.find(column => (column.show === columnName || column.sortAs === columnName))
|
||||
}
|
||||
|
||||
getFilterableValue (columnName) {
|
||||
const value = this.getValue(columnName)
|
||||
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return value.toString().toLowerCase()
|
||||
}
|
||||
|
||||
getSortableValue (columnName) {
|
||||
const dataType = this.getColumn(columnName).dataType
|
||||
|
||||
let value = this.getValue(columnName)
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
value = value.toLowerCase()
|
||||
}
|
||||
|
||||
if (dataType.startsWith('date')) {
|
||||
const format = dataType.replace('date:', '')
|
||||
|
||||
return moment(value, format).format('YYYYMMDDHHmmss')
|
||||
}
|
||||
|
||||
if (dataType === 'numeric') {
|
||||
return value
|
||||
}
|
||||
|
||||
return value.toString()
|
||||
}
|
||||
|
||||
passesFilter (filter) {
|
||||
return this.columns
|
||||
.filter(column => column.isFilterable())
|
||||
.map(column => this.getFilterableValue(column.getFilterFieldName()))
|
||||
.filter(filterableValue => filterableValue.indexOf(filter.toLowerCase()) >= 0)
|
||||
.length
|
||||
}
|
||||
}
|
||||
@ -1,120 +0,0 @@
|
||||
<template>
|
||||
<nav v-if="shouldShowPagination">
|
||||
<ul class="pagination justify-content-center">
|
||||
<li :class="{ disabled: pagination.currentPage === 1 }">
|
||||
<a
|
||||
:class="{ disabled: pagination.currentPage === 1 }"
|
||||
@click="pageClicked( pagination.currentPage - 1 )"
|
||||
>
|
||||
<i class="left chevron icon">«</i>
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="hasFirst" :class="{ active: isActive(1) }" class="page-item">
|
||||
<a class="page-link" @click="pageClicked(1)">1</a>
|
||||
</li>
|
||||
<li v-if="hasFirstEllipsis"><span class="pagination-ellipsis">…</span></li>
|
||||
<li v-for="page in pages" :key="page" :class="{ active: isActive(page), disabled: page === '...' }" class="page-item">
|
||||
<a class="page-link" @click="pageClicked(page)">{{ page }}</a>
|
||||
</li>
|
||||
<li v-if="hasLastEllipsis"><span class="pagination-ellipsis">…</span></li>
|
||||
<li
|
||||
v-if="hasLast"
|
||||
:class="{ active: isActive(this.pagination.totalPages) }"
|
||||
class="page-item"
|
||||
>
|
||||
<a class="page-link" @click="pageClicked(pagination.totalPages)">
|
||||
{{ pagination.totalPages }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
:class="{ disabled: pagination.currentPage === pagination.totalPages }"
|
||||
@click="pageClicked( pagination.currentPage + 1 )"
|
||||
>
|
||||
<i class="right chevron icon">»</i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
pages () {
|
||||
return this.pagination.totalPages === undefined
|
||||
? []
|
||||
: this.pageLinks()
|
||||
},
|
||||
|
||||
hasFirst () {
|
||||
return this.pagination.currentPage >= 4 || this.pagination.totalPages < 10
|
||||
},
|
||||
|
||||
hasLast () {
|
||||
return this.pagination.currentPage <= this.pagination.totalPages - 3 || this.pagination.totalPages < 10
|
||||
},
|
||||
|
||||
hasFirstEllipsis () {
|
||||
return this.pagination.currentPage >= 4 && this.pagination.totalPages >= 10
|
||||
},
|
||||
|
||||
hasLastEllipsis () {
|
||||
return this.pagination.currentPage <= this.pagination.totalPages - 3 && this.pagination.totalPages >= 10
|
||||
},
|
||||
|
||||
shouldShowPagination () {
|
||||
if (this.pagination.totalPages === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.pagination.count === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.pagination.totalPages > 1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isActive (page) {
|
||||
const currentPage = this.pagination.currentPage || 1
|
||||
|
||||
return currentPage === page
|
||||
},
|
||||
pageClicked (page) {
|
||||
if (page === '...' ||
|
||||
page === this.pagination.currentPage ||
|
||||
page > this.pagination.totalPages ||
|
||||
page < 1) {
|
||||
return
|
||||
}
|
||||
this.$emit('pageChange', page)
|
||||
},
|
||||
|
||||
pageLinks () {
|
||||
const pages = []
|
||||
|
||||
let left = 2
|
||||
let right = this.pagination.totalPages - 1
|
||||
|
||||
if (this.pagination.totalPages >= 10) {
|
||||
left = Math.max(1, this.pagination.currentPage - 2)
|
||||
right = Math.min(this.pagination.currentPage + 2, this.pagination.totalPages)
|
||||
}
|
||||
for (let i = left; i <= right; i++) {
|
||||
pages.push(i)
|
||||
}
|
||||
|
||||
return pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
@ -1,24 +0,0 @@
|
||||
export default {
|
||||
functional: true,
|
||||
|
||||
props: ['column', 'row', 'responsiveLabel'],
|
||||
|
||||
render (createElement, { props }) {
|
||||
const data = {}
|
||||
|
||||
if (props.column.cellClass) {
|
||||
data.class = props.column.cellClass
|
||||
}
|
||||
|
||||
if (props.column.template) {
|
||||
return createElement('td', data, props.column.template(props.row.data))
|
||||
}
|
||||
|
||||
data.domProps = {}
|
||||
data.domProps.innerHTML = props.column.formatter(props.row.getValue(props.column.show), props.row.data)
|
||||
|
||||
return createElement('td', [
|
||||
createElement('span', props.responsiveLabel), data.domProps.innerHTML
|
||||
])
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<!-- Never render the contents -->
|
||||
<!-- The scoped slot won't have the required data -->
|
||||
<div v-if="false">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import settings from '../settings'
|
||||
export default {
|
||||
props: {
|
||||
show: { required: false, type: String },
|
||||
label: { default: null, type: String },
|
||||
dataType: { default: 'string', type: String },
|
||||
|
||||
sortable: { default: true, type: Boolean },
|
||||
sortBy: { default: null },
|
||||
|
||||
filterable: { default: true, type: Boolean },
|
||||
sortAs: { default: null },
|
||||
filterOn: { default: null },
|
||||
|
||||
formatter: { default: v => v, type: Function },
|
||||
|
||||
hidden: { default: false, type: Boolean },
|
||||
|
||||
cellClass: { default: settings.cellClass },
|
||||
headerClass: { default: settings.headerClass },
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -1,72 +0,0 @@
|
||||
<template>
|
||||
<th
|
||||
v-if="this.isVisible"
|
||||
slot-scope="col"
|
||||
:aria-sort="ariaSort"
|
||||
:aria-disabled="ariaDisabled"
|
||||
:class="headerClass"
|
||||
role="columnheader"
|
||||
@click="clicked"
|
||||
>
|
||||
{{ label }}
|
||||
</th>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { classList } from '../helpers'
|
||||
export default {
|
||||
props: ['column', 'sort'],
|
||||
|
||||
computed: {
|
||||
ariaDisabled () {
|
||||
if (!this.column.isSortable()) {
|
||||
return 'true'
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
ariaSort () {
|
||||
if (!this.column.isSortable ()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if ((this.column.sortAs || this.column.show) !== this.sort.fieldName) {
|
||||
return 'none'
|
||||
}
|
||||
|
||||
return this.sort.order === 'asc' ? 'ascending' : 'descending';
|
||||
},
|
||||
|
||||
headerClass () {
|
||||
if (!this.column.isSortable()) {
|
||||
return classList('table-component__th', this.column.headerClass);
|
||||
}
|
||||
|
||||
if ((this.column.sortAs || this.column.show) !== this.sort.fieldName) {
|
||||
return classList('table-component__th table-component__th--sort', this.column.headerClass);
|
||||
}
|
||||
|
||||
return classList(`table-component__th table-component__th--sort-${this.sort.order}`, this.column.headerClass);
|
||||
},
|
||||
|
||||
isVisible () {
|
||||
return !this.column.hidden
|
||||
},
|
||||
|
||||
label () {
|
||||
if (this.column.label === null) {
|
||||
return this.column.show
|
||||
}
|
||||
return this.column.label
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
clicked () {
|
||||
if (this.column.isSortable()) {
|
||||
this.$emit('click', this.column)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -1,330 +0,0 @@
|
||||
<template>
|
||||
<div class="table-component">
|
||||
<div v-if="showFilter && filterableColumnExists" class="table-component__filter">
|
||||
<input
|
||||
:class="fullFilterInputClass"
|
||||
v-model="filter"
|
||||
:placeholder="filterPlaceholder"
|
||||
type="text"
|
||||
>
|
||||
<a v-if="filter" class="table-component__filter__clear" @click="filter = ''">×</a>
|
||||
</div>
|
||||
|
||||
<div class="table-component__table-wrapper">
|
||||
<base-loader v-if="loading" class="table-loader" />
|
||||
|
||||
<table :class="fullTableClass">
|
||||
<caption
|
||||
v-if="showCaption"
|
||||
class="table-component__table__caption"
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
>{{ ariaCaption }}</caption>
|
||||
<thead :class="fullTableHeadClass">
|
||||
<tr>
|
||||
<table-column-header
|
||||
v-for="column in columns"
|
||||
:key="column.show || column.show"
|
||||
:sort="sort"
|
||||
:column="column"
|
||||
@click="changeSorting"
|
||||
/>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody :class="fullTableBodyClass">
|
||||
<table-row
|
||||
v-for="row in displayedRows"
|
||||
:key="row.vueTableComponentInternalRowId"
|
||||
:row="row"
|
||||
:columns="columns"
|
||||
@rowClick="emitRowClick"
|
||||
/>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<slot :rows="rows" name="tfoot" />
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-if="displayedRows.length === 0 && !loading" class="table-component__message">{{ filterNoResults }}</div>
|
||||
|
||||
<div style="display:none;">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<pagination v-if="pagination && !loading" :pagination="pagination" @pageChange="pageChange" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Column from '../classes/Column'
|
||||
import expiringStorage from '../expiring-storage'
|
||||
import Row from '../classes/Row'
|
||||
import TableColumnHeader from './TableColumnHeader'
|
||||
import TableRow from './TableRow'
|
||||
import settings from '../settings'
|
||||
import Pagination from './Pagination'
|
||||
import { classList, pick } from '../helpers'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TableColumnHeader,
|
||||
TableRow,
|
||||
Pagination
|
||||
},
|
||||
|
||||
props: {
|
||||
data: { default: () => [], type: [Array, Function] },
|
||||
|
||||
showFilter: { type: Boolean, default: true },
|
||||
showCaption: { type: Boolean, default: true },
|
||||
|
||||
sortBy: { default: '', type: String },
|
||||
sortOrder: { default: '', type: String },
|
||||
|
||||
cacheKey: { default: null },
|
||||
cacheLifetime: { default: 5 },
|
||||
|
||||
tableClass: { default: () => settings.tableClass },
|
||||
theadClass: { default: () => settings.theadClass },
|
||||
tbodyClass: { default: () => settings.tbodyClass },
|
||||
filterInputClass: { default: () => settings.filterInputClass },
|
||||
filterPlaceholder: { default: () => settings.filterPlaceholder },
|
||||
filterNoResults: { default: () => settings.filterNoResults }
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
columns: [],
|
||||
rows: [],
|
||||
filter: '',
|
||||
sort: {
|
||||
fieldName: '',
|
||||
order: ''
|
||||
},
|
||||
pagination: null,
|
||||
|
||||
loading: false,
|
||||
localSettings: {}
|
||||
}),
|
||||
|
||||
computed: {
|
||||
fullTableClass () {
|
||||
return classList('table-component__table', this.tableClass)
|
||||
},
|
||||
|
||||
fullTableHeadClass () {
|
||||
return classList('table-component__table__head', this.theadClass)
|
||||
},
|
||||
|
||||
fullTableBodyClass () {
|
||||
return classList('table-component__table__body', this.tbodyClass)
|
||||
},
|
||||
|
||||
fullFilterInputClass () {
|
||||
return classList('table-component__filter__field', this.filterInputClass)
|
||||
},
|
||||
|
||||
ariaCaption () {
|
||||
if (this.sort.fieldName === '') {
|
||||
return 'Table not sorted'
|
||||
}
|
||||
|
||||
return (
|
||||
`Table sorted by ${this.sort.fieldName} ` +
|
||||
(this.sort.order === 'asc' ? '(ascending)' : '(descending)')
|
||||
)
|
||||
},
|
||||
|
||||
usesLocalData () {
|
||||
return Array.isArray(this.data)
|
||||
},
|
||||
|
||||
displayedRows () {
|
||||
if (!this.usesLocalData) {
|
||||
return this.sortedRows
|
||||
}
|
||||
|
||||
if (!this.showFilter) {
|
||||
return this.sortedRows
|
||||
}
|
||||
|
||||
if (!this.columns.filter(column => column.isFilterable()).length) {
|
||||
return this.sortedRows
|
||||
}
|
||||
|
||||
return this.sortedRows.filter(row => row.passesFilter(this.filter))
|
||||
},
|
||||
|
||||
sortedRows () {
|
||||
if (!this.usesLocalData) {
|
||||
return this.rows
|
||||
}
|
||||
|
||||
if (this.sort.fieldName === '') {
|
||||
return this.rows
|
||||
}
|
||||
|
||||
if (this.columns.length === 0) {
|
||||
return this.rows
|
||||
}
|
||||
|
||||
const sortColumn = this.getColumn(this.sort.fieldName)
|
||||
|
||||
if (!sortColumn) {
|
||||
return this.rows
|
||||
}
|
||||
|
||||
return this.rows.sort(
|
||||
sortColumn.getSortPredicate(this.sort.order, this.columns)
|
||||
)
|
||||
},
|
||||
|
||||
filterableColumnExists () {
|
||||
return this.columns.filter(c => c.isFilterable()).length > 0
|
||||
},
|
||||
|
||||
storageKey () {
|
||||
return this.cacheKey
|
||||
? `vue-table-component.${this.cacheKey}`
|
||||
: `vue-table-component.${window.location.host}${window.location.pathname}${this.cacheKey}`
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
filter () {
|
||||
if (!this.usesLocalData) {
|
||||
this.mapDataToRows()
|
||||
}
|
||||
|
||||
this.saveState()
|
||||
},
|
||||
|
||||
data () {
|
||||
if (this.usesLocalData) {
|
||||
this.mapDataToRows()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.sort.order = this.sortOrder
|
||||
|
||||
this.restoreState()
|
||||
},
|
||||
|
||||
async mounted () {
|
||||
this.sort.fieldName = this.sortBy
|
||||
const columnComponents = this.$slots.default
|
||||
.filter(column => column.componentInstance)
|
||||
.map(column => column.componentInstance)
|
||||
|
||||
this.columns = columnComponents.map(column => new Column(column))
|
||||
|
||||
columnComponents.forEach(columnCom => {
|
||||
Object.keys(columnCom.$options.props).forEach(prop =>
|
||||
columnCom.$watch(prop, () => {
|
||||
this.columns = columnComponents.map(column => new Column(column))
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
await this.mapDataToRows()
|
||||
},
|
||||
|
||||
methods: {
|
||||
async pageChange (page) {
|
||||
this.pagination.currentPage = page
|
||||
|
||||
await this.mapDataToRows()
|
||||
},
|
||||
|
||||
async mapDataToRows () {
|
||||
const data = this.usesLocalData
|
||||
? this.prepareLocalData()
|
||||
: await this.fetchServerData()
|
||||
|
||||
let rowId = 0
|
||||
|
||||
this.rows = data
|
||||
.map(rowData => {
|
||||
rowData.vueTableComponentInternalRowId = rowId++
|
||||
return rowData
|
||||
})
|
||||
.map(rowData => new Row(rowData, this.columns))
|
||||
},
|
||||
|
||||
prepareLocalData () {
|
||||
this.pagination = null
|
||||
|
||||
return this.data
|
||||
},
|
||||
|
||||
async fetchServerData () {
|
||||
const page = (this.pagination && this.pagination.currentPage) || 1
|
||||
this.loading = true
|
||||
|
||||
const response = await this.data({
|
||||
filter: this.filter,
|
||||
sort: this.sort,
|
||||
page: page
|
||||
})
|
||||
|
||||
this.pagination = response.pagination
|
||||
this.loading = false
|
||||
return response.data
|
||||
},
|
||||
|
||||
async refresh () {
|
||||
if (this.pagination) {
|
||||
this.pagination.currentPage = 1
|
||||
}
|
||||
await this.mapDataToRows()
|
||||
},
|
||||
|
||||
changeSorting (column) {
|
||||
if (this.sort.fieldName !== (column.sortAs || column.show)) {
|
||||
this.sort.fieldName = (column.sortAs || column.show)
|
||||
this.sort.order = 'asc'
|
||||
} else {
|
||||
this.sort.order = this.sort.order === 'asc' ? 'desc' : 'asc'
|
||||
}
|
||||
|
||||
if (!this.usesLocalData) {
|
||||
this.mapDataToRows()
|
||||
}
|
||||
|
||||
this.saveState()
|
||||
},
|
||||
|
||||
getColumn (columnName) {
|
||||
return this.columns.find(column => column.show === columnName)
|
||||
},
|
||||
|
||||
saveState () {
|
||||
expiringStorage.set(
|
||||
this.storageKey,
|
||||
pick(this.$data, ['filter', 'sort']),
|
||||
this.cacheLifetime
|
||||
)
|
||||
},
|
||||
|
||||
restoreState () {
|
||||
const previousState = expiringStorage.get(this.storageKey)
|
||||
|
||||
if (previousState === null) {
|
||||
return
|
||||
}
|
||||
|
||||
this.sort = previousState.sort
|
||||
this.filter = previousState.filter
|
||||
|
||||
this.saveState()
|
||||
},
|
||||
|
||||
emitRowClick (row) {
|
||||
this.$emit('rowClick', row)
|
||||
this.$emit('row-click', row)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<tr @click="onClick">
|
||||
<table-cell
|
||||
v-for="column in visibleColumns"
|
||||
:row="row"
|
||||
:column="column"
|
||||
:key="column.id"
|
||||
:responsive-label="column.label"
|
||||
></table-cell>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableCell from './TableCell';
|
||||
|
||||
export default {
|
||||
props: ['columns', 'row'],
|
||||
|
||||
components: {
|
||||
TableCell,
|
||||
},
|
||||
|
||||
computed: {
|
||||
visibleColumns() {
|
||||
return this.columns.filter(column => ! column.hidden);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onClick(e) {
|
||||
this.$emit('rowClick', {
|
||||
e,
|
||||
row: this.row
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@ -1,34 +0,0 @@
|
||||
class ExpiringStorage {
|
||||
get (key) {
|
||||
const cached = JSON.parse(
|
||||
localStorage.getItem(key)
|
||||
)
|
||||
|
||||
if (!cached) {
|
||||
return null
|
||||
}
|
||||
|
||||
const expires = new Date(cached.expires)
|
||||
|
||||
if (expires < new Date()) {
|
||||
localStorage.removeItem(key)
|
||||
return null
|
||||
}
|
||||
|
||||
return cached.value
|
||||
}
|
||||
|
||||
has (key) {
|
||||
return this.get(key) !== null
|
||||
}
|
||||
|
||||
set (key, value, lifeTimeInMinutes) {
|
||||
const currentTime = new Date().getTime()
|
||||
|
||||
const expires = new Date(currentTime + lifeTimeInMinutes * 60000)
|
||||
|
||||
localStorage.setItem(key, JSON.stringify({ value, expires }))
|
||||
}
|
||||
}
|
||||
|
||||
export default new ExpiringStorage()
|
||||
@ -1,30 +0,0 @@
|
||||
export function classList (...classes) {
|
||||
return classes
|
||||
.map(c => Array.isArray(c) ? c : [c])
|
||||
.reduce((classes, c) => classes.concat(c), [])
|
||||
}
|
||||
|
||||
export function get (object, path) {
|
||||
if (!path) {
|
||||
return object
|
||||
}
|
||||
|
||||
if (object === null || typeof object !== 'object') {
|
||||
return object
|
||||
}
|
||||
|
||||
const [pathHead, pathTail] = path.split(/\.(.+)/)
|
||||
|
||||
return get(object[pathHead], pathTail)
|
||||
}
|
||||
|
||||
export function pick (object, properties) {
|
||||
return properties.reduce((pickedObject, property) => {
|
||||
pickedObject[property] = object[property]
|
||||
return pickedObject
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function range (from, to) {
|
||||
return [...Array(to - from)].map((_, i) => i + from)
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
import TableComponent from './components/TableComponent'
|
||||
import TableColumn from './components/TableColumn'
|
||||
import Pagination from './components/Pagination'
|
||||
import { mergeSettings } from './settings'
|
||||
|
||||
export default {
|
||||
install (Vue, options = {}) {
|
||||
mergeSettings(options)
|
||||
|
||||
Vue.component('table-component', TableComponent)
|
||||
Vue.component('table-column', TableColumn)
|
||||
Vue.component('pagination', Pagination)
|
||||
},
|
||||
|
||||
settings (settings) {
|
||||
mergeSettings(settings)
|
||||
}
|
||||
}
|
||||
|
||||
export { TableComponent, TableColumn }
|
||||
@ -1,18 +0,0 @@
|
||||
const settings = {
|
||||
tableClass: '',
|
||||
theadClass: '',
|
||||
tbodyClass: '',
|
||||
headerClass: '',
|
||||
cellClass: '',
|
||||
filterInputClass: '',
|
||||
filterPlaceholder: 'Filter table…',
|
||||
filterNoResults: 'There are no matching rows'
|
||||
}
|
||||
|
||||
export function mergeSettings (newSettings) {
|
||||
for (const setting in newSettings) {
|
||||
settings[setting] = newSettings[setting]
|
||||
}
|
||||
}
|
||||
|
||||
export default settings
|
||||
@ -1,37 +1,42 @@
|
||||
import BaseButton from './BaseButton.vue'
|
||||
import ItemModal from './modal/ItemModal.vue'
|
||||
import BaseModal from './modal/BaseModal.vue'
|
||||
import BaseDatePicker from './base-date-picker/BaseDatePicker.vue'
|
||||
import BaseInput from './BaseInput.vue'
|
||||
import BaseSwitch from './BaseSwitch.vue'
|
||||
import BaseTextArea from './BaseTextArea.vue'
|
||||
import BaseSelect from './base-select/BaseSelect.vue'
|
||||
import BaseLoader from './BaseLoader.vue'
|
||||
import BaseCustomerSelect from './BaseCustomerSelect.vue'
|
||||
import BasePrefixInput from './BasePrefixInput.vue'
|
||||
|
||||
import BasePopup from './popup/BasePopup.vue'
|
||||
import BaseCustomInput from './BaseCustomInput.vue'
|
||||
|
||||
import CustomerSelectPopup from './popup/CustomerSelectPopup.vue'
|
||||
import TaxSelectPopup from './popup/TaxSelectPopup.vue'
|
||||
import NoteSelectPopup from './popup/NoteSelectPopup.vue'
|
||||
|
||||
import {TableColumn, TableComponent} from './base-table/index'
|
||||
import BaseDatePicker from '../base/BaseDatePicker.vue'
|
||||
import BaseTimePicker from './BaseTimePicker.vue'
|
||||
import BasePage from './BasePage.vue'
|
||||
|
||||
import GlobalSearch from '../GlobalSearch.vue'
|
||||
|
||||
import DotIcon from '../../components/icon/DotIcon.vue'
|
||||
import SaveIcon from '../../components/icon/SaveIcon.vue'
|
||||
|
||||
import SwSelect from '@bytefury/spacewind/src/components/sw-select'
|
||||
|
||||
Vue.component('base-button', BaseButton)
|
||||
Vue.component('item-modal', ItemModal)
|
||||
Vue.component('base-modal', BaseModal)
|
||||
Vue.component('global-search', GlobalSearch)
|
||||
|
||||
Vue.component('base-page', BasePage)
|
||||
|
||||
Vue.component('base-date-picker', BaseDatePicker)
|
||||
Vue.component('base-input', BaseInput)
|
||||
Vue.component('base-switch', BaseSwitch)
|
||||
Vue.component('base-text-area', BaseTextArea)
|
||||
|
||||
Vue.component('base-loader', BaseLoader)
|
||||
Vue.component('base-prefix-input', BasePrefixInput)
|
||||
Vue.component('sw-select', SwSelect)
|
||||
|
||||
Vue.component('table-component', TableComponent)
|
||||
Vue.component('table-column', TableColumn)
|
||||
|
||||
Vue.component('base-select', BaseSelect)
|
||||
Vue.component('base-customer-select', BaseCustomerSelect)
|
||||
Vue.component('base-custom-input', BaseCustomInput)
|
||||
|
||||
Vue.component('base-popup', BasePopup)
|
||||
Vue.component('customer-select-popup', CustomerSelectPopup)
|
||||
Vue.component('tax-select-popup', TaxSelectPopup)
|
||||
Vue.component('note-select-popup', NoteSelectPopup)
|
||||
|
||||
Vue.component('base-time-picker', BaseTimePicker)
|
||||
|
||||
Vue.component('dot-icon', DotIcon)
|
||||
Vue.component('save-icon', SaveIcon)
|
||||
|
||||
179
resources/assets/js/components/base/modal/BackupModal.vue
Normal file
179
resources/assets/js/components/base/modal/BackupModal.vue
Normal file
@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="relative customer-modal">
|
||||
<base-loader
|
||||
v-if="isRequestOngoing"
|
||||
class="h-130"
|
||||
:show-bg-overlay="true"
|
||||
/>
|
||||
<form @submit.prevent="createNewBackup">
|
||||
<div class="p-6">
|
||||
<sw-input-group
|
||||
:label="$t('settings.backup.select_backup_type')"
|
||||
:error="optionError"
|
||||
horizontal
|
||||
required
|
||||
class="py-2"
|
||||
>
|
||||
<sw-select
|
||||
v-model="formData.option"
|
||||
:options="options"
|
||||
:searchable="true"
|
||||
:show-labels="false"
|
||||
:placeholder="$t('settings.backup.select_backup_type')"
|
||||
:allow-empty="false"
|
||||
:maxHeight="100"
|
||||
/>
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.disk.select_disk')"
|
||||
:error="selectDiskError"
|
||||
horizontal
|
||||
required
|
||||
class="py-2"
|
||||
>
|
||||
<sw-select
|
||||
v-model="formData.selected_disk"
|
||||
:options="getDisks"
|
||||
:searchable="true"
|
||||
:show-labels="false"
|
||||
:placeholder="$t('settings.disk.select_disk')"
|
||||
:allow-empty="false"
|
||||
track-by="id"
|
||||
:preselect-first="true"
|
||||
:custom-label="getCustomLabel"
|
||||
:maxHeight="100"
|
||||
:loading="isLoading"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
<div
|
||||
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
||||
>
|
||||
<sw-button
|
||||
class="mr-3"
|
||||
variant="primary-outline"
|
||||
type="button"
|
||||
@click="cancelBackup"
|
||||
>
|
||||
{{ $t('general.cancel') }}
|
||||
</sw-button>
|
||||
<sw-button
|
||||
:loading="isCreateLoading"
|
||||
variant="primary"
|
||||
type="submit"
|
||||
:disabled="isCreateLoading"
|
||||
>
|
||||
<save-icon v-if="!isCreateLoading" class="mr-2" />
|
||||
{{ $t('general.create') }}
|
||||
</sw-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import AddressStub from '../../../stub/address'
|
||||
import _ from 'lodash'
|
||||
|
||||
const { required } = require('vuelidate/lib/validators')
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
isCreateLoading: false,
|
||||
isRequestOngoing: false,
|
||||
formData: {
|
||||
option: 'full',
|
||||
selected_disk: { driver: 'local' },
|
||||
},
|
||||
options: ['full', 'only-db', 'only-files'],
|
||||
}
|
||||
},
|
||||
|
||||
validations: {
|
||||
formData: {
|
||||
option: {
|
||||
required,
|
||||
},
|
||||
selected_disk: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters('disks', ['getDisks']),
|
||||
|
||||
...mapGetters('modal', ['refreshData']),
|
||||
|
||||
optionError() {
|
||||
if (!this.$v.formData.option.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.option) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
},
|
||||
|
||||
selectDiskError() {
|
||||
if (!this.$v.formData.selected_disk.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.selected_disk) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
...mapActions('backup', ['createBackup']),
|
||||
|
||||
...mapActions('disks', ['fetchDisks']),
|
||||
|
||||
...mapActions('modal', ['closeModal']),
|
||||
|
||||
getCustomLabel({ driver, name }) {
|
||||
return `${name} — [${driver}]`
|
||||
},
|
||||
|
||||
async createNewBackup() {
|
||||
let data = {
|
||||
option: this.formData.option,
|
||||
file_disk_id: this.formData.selected_disk.id,
|
||||
}
|
||||
|
||||
try {
|
||||
this.isCreateLoading = true
|
||||
await this.createBackup(data)
|
||||
this.isCreateLoading = false
|
||||
window.toastr['success'](this.$t('settings.backup.created_message'))
|
||||
this.refreshData ? this.refreshData() : ''
|
||||
this.cancelBackup()
|
||||
} catch (e) {
|
||||
this.isCreateLoading = false
|
||||
window.toastr['error'](e.response.data.message)
|
||||
}
|
||||
},
|
||||
|
||||
async loadData() {
|
||||
this.isRequestOngoing = true
|
||||
|
||||
let res = await this.fetchDisks({ limit: 'all' })
|
||||
this.formData.selected_disk = res.data.disks.data[0]
|
||||
this.isRequestOngoing = false
|
||||
},
|
||||
|
||||
cancelBackup() {
|
||||
this.closeModal()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,19 +1,22 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-if="modalActive" :class="'size-' + modalSize" class="base-modal">
|
||||
<div class="modal-body">
|
||||
<div class="close-icon" @click="closeModal">
|
||||
<font-awesome-icon icon="times" />
|
||||
<div>
|
||||
<sw-modal ref="baseModal" :variant="variant">
|
||||
<template v-slot:header>
|
||||
<div
|
||||
class="absolute flex content-center justify-center w-5 cursor-pointer"
|
||||
style="top: 20px; right: 15px"
|
||||
@click="closeModal"
|
||||
>
|
||||
<x-icon />
|
||||
</div>
|
||||
<div class="modal-header p-3">
|
||||
<h5 class="modal-heading">{{ modalTitle }}</h5>
|
||||
</div>
|
||||
<component :is="component" />
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<span>{{ modalTitle }}</span>
|
||||
</template>
|
||||
<component :is="component" />
|
||||
</sw-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { XIcon } from '@vue-hero-icons/solid'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import TaxTypeModal from './TaxTypeModal'
|
||||
import ItemModal from './ItemModal'
|
||||
@ -21,9 +24,17 @@ import EstimateTemplate from './EstimateTemplate'
|
||||
import InvoiceTemplate from './InvoiceTemplate'
|
||||
import CustomerModal from './CustomerModal'
|
||||
import CategoryModal from './CategoryModal'
|
||||
import BackupModal from './BackupModal'
|
||||
import PaymentMode from './PaymentModeModal'
|
||||
import ItemUnit from './ItemUnitModal'
|
||||
import MailTestModal from './MailTestModal'
|
||||
import SendInvoiceModal from './SendInvoiceModal'
|
||||
import SendEstimateModal from './SendEstimateModal'
|
||||
import SendPaymentModal from './SendPaymentModal'
|
||||
import FileDiskModal from './FileDiskModal'
|
||||
import SetDefaultDiskModal from './SetDefaultDiskModal'
|
||||
import CustomFieldModal from './CustomField/Index'
|
||||
import NoteSelectModal from './NoteModal'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -33,14 +44,22 @@ export default {
|
||||
InvoiceTemplate,
|
||||
CustomerModal,
|
||||
CategoryModal,
|
||||
BackupModal,
|
||||
PaymentMode,
|
||||
ItemUnit,
|
||||
MailTestModal
|
||||
MailTestModal,
|
||||
SendInvoiceModal,
|
||||
SendEstimateModal,
|
||||
SendPaymentModal,
|
||||
XIcon,
|
||||
FileDiskModal,
|
||||
SetDefaultDiskModal,
|
||||
CustomFieldModal,
|
||||
NoteSelectModal,
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
component: '',
|
||||
hasFocus: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -49,31 +68,28 @@ export default {
|
||||
'modalTitle',
|
||||
'componentName',
|
||||
'modalSize',
|
||||
'modalData'
|
||||
])
|
||||
'modalData',
|
||||
'variant',
|
||||
]),
|
||||
},
|
||||
watch: {
|
||||
componentName (component) {
|
||||
componentName(component) {
|
||||
if (!component) {
|
||||
return
|
||||
}
|
||||
|
||||
this.component = component
|
||||
}
|
||||
},
|
||||
modalActive(status) {
|
||||
if (status) {
|
||||
this.$refs.baseModal.open()
|
||||
return true
|
||||
}
|
||||
this.$refs.baseModal.close()
|
||||
return false
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('modal', [
|
||||
'openModal',
|
||||
'closeModal'
|
||||
])
|
||||
}
|
||||
...mapActions('modal', ['openModal', 'closeModal']),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,98 +1,115 @@
|
||||
<template>
|
||||
<div class="category-modal">
|
||||
<form action="" @submit.prevent="submitCategoryData">
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label input-label">{{ $t('expenses.category') }}<span class="required text-danger">*</span></label>
|
||||
<div class="col-sm-7">
|
||||
<base-input
|
||||
ref="name"
|
||||
:invalid="$v.formData.name.$error"
|
||||
v-model="formData.name"
|
||||
type="text"
|
||||
@input="$v.formData.name.$touch()"
|
||||
/>
|
||||
<form action="" @submit.prevent="submitCategoryData">
|
||||
<div class="p-8 sm:p-6">
|
||||
<sw-input-group
|
||||
:label="$t('expenses.category')"
|
||||
:error="nameError"
|
||||
variant="horizontal"
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
ref="name"
|
||||
:invalid="$v.formData.name.$error"
|
||||
v-model="formData.name"
|
||||
type="text"
|
||||
@input="$v.formData.name.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
|
||||
<div v-if="$v.formData.name.$error">
|
||||
<span v-if="!$v.formData.name.required" class="text-danger">{{ $tc('validation.required') }}</span>
|
||||
<span v-if="!$v.formData.name.minLength" class="text-danger"> {{ $tc('validation.name_min_length', $v.formData.name.$params.minLength.min, { count: $v.formData.name.$params.minLength.min }) }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label input-label">{{ $t('expenses.description') }}</label>
|
||||
<div class="col-sm-7">
|
||||
<base-text-area
|
||||
v-model="formData.description"
|
||||
rows="4"
|
||||
cols="50"
|
||||
@input="$v.formData.description.$touch()"
|
||||
/>
|
||||
<div v-if="$v.formData.description.$error">
|
||||
<span v-if="!$v.formData.name.maxLength" class="text-danger"> {{ $tc('validation.description_maxlength') }} </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<base-button
|
||||
:outline="true"
|
||||
class="mr-3"
|
||||
color="theme"
|
||||
@click="closeCategoryModal"
|
||||
>
|
||||
{{ $t('general.cancel') }}
|
||||
</base-button>
|
||||
<base-button
|
||||
:loading="isLoading"
|
||||
icon="save"
|
||||
color="theme"
|
||||
type="submit"
|
||||
>
|
||||
{{ !isEdit ? $t('general.save') : $t('general.update') }}
|
||||
</base-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<sw-input-group
|
||||
:label="$t('expenses.description')"
|
||||
:error="descriptionError"
|
||||
class="mt-5"
|
||||
variant="horizontal"
|
||||
>
|
||||
<sw-textarea
|
||||
v-model="formData.description"
|
||||
rows="4"
|
||||
cols="50"
|
||||
@input="$v.formData.description.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
<div
|
||||
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid border-modal-bg"
|
||||
>
|
||||
<sw-button
|
||||
type="button"
|
||||
variant="primary-outline"
|
||||
class="mr-3 text-sm"
|
||||
@click="closeCategoryModal"
|
||||
>
|
||||
{{ $t('general.cancel') }}
|
||||
</sw-button>
|
||||
<sw-button variant="primary" type="submit" :loading="isLoading">
|
||||
<save-icon v-if="!isLoading" class="mr-2" />
|
||||
{{ !isEdit ? $t('general.save') : $t('general.update') }}
|
||||
</sw-button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import { validationMixin } from 'vuelidate'
|
||||
const { required, minLength, maxLength } = require('vuelidate/lib/validators')
|
||||
export default {
|
||||
mixins: [validationMixin],
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
isEdit: false,
|
||||
isLoading: false,
|
||||
formData: {
|
||||
id: null,
|
||||
name: null,
|
||||
description: null
|
||||
}
|
||||
description: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('modal', [
|
||||
'modalDataID',
|
||||
'modalData',
|
||||
'modalActive'
|
||||
])
|
||||
'modalActive',
|
||||
'refreshData',
|
||||
]),
|
||||
nameError() {
|
||||
if (!this.$v.formData.name.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.name.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
|
||||
if (!this.$v.formData.name.minLength) {
|
||||
return this.$tc(
|
||||
'validation.name_min_length',
|
||||
this.$v.formData.name.$params.minLength.min,
|
||||
{ count: this.$v.formData.name.$params.minLength.min }
|
||||
)
|
||||
}
|
||||
},
|
||||
descriptionError() {
|
||||
if (!this.$v.formData.description.$error) {
|
||||
return ''
|
||||
}
|
||||
if (!this.$v.formData.name.maxLength) {
|
||||
return this.$tc('validation.description_maxlength')
|
||||
}
|
||||
},
|
||||
},
|
||||
validations: {
|
||||
formData: {
|
||||
name: {
|
||||
required,
|
||||
minLength: minLength(3)
|
||||
minLength: minLength(3),
|
||||
},
|
||||
description: {
|
||||
maxLength: maxLength(255)
|
||||
}
|
||||
}
|
||||
maxLength: maxLength(255),
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'modalDataID' (val) {
|
||||
modalDataID(val) {
|
||||
if (val) {
|
||||
this.isEdit = true
|
||||
this.setData()
|
||||
@ -100,40 +117,33 @@ export default {
|
||||
this.isEdit = false
|
||||
}
|
||||
},
|
||||
'modalActive' (val) {
|
||||
modalActive(val) {
|
||||
if (!this.modalActive) {
|
||||
this.resetFormData()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
|
||||
mounted() {
|
||||
this.$refs.name.focus = true
|
||||
if (this.modalDataID) {
|
||||
this.isEdit = true
|
||||
this.setData()
|
||||
}
|
||||
},
|
||||
destroyed () {
|
||||
|
||||
},
|
||||
methods: {
|
||||
...mapActions('modal', [
|
||||
'closeModal',
|
||||
'resetModalData'
|
||||
]),
|
||||
...mapActions('category', [
|
||||
'addCategory',
|
||||
'updateCategory'
|
||||
]),
|
||||
resetFormData () {
|
||||
...mapActions('modal', ['closeModal']),
|
||||
...mapActions('category', ['addCategory', 'updateCategory']),
|
||||
resetFormData() {
|
||||
this.formData = {
|
||||
id: null,
|
||||
name: null,
|
||||
description: null
|
||||
description: null,
|
||||
}
|
||||
this.$v.formData.$reset()
|
||||
},
|
||||
async submitCategoryData () {
|
||||
async submitCategoryData() {
|
||||
this.$v.formData.$touch()
|
||||
|
||||
if (this.$v.$invalid) {
|
||||
@ -149,28 +159,33 @@ export default {
|
||||
|
||||
if (response.data) {
|
||||
if (!this.isEdit) {
|
||||
window.toastr['success'](this.$t('settings.expense_category.created_message'))
|
||||
window.toastr['success'](
|
||||
this.$t('settings.expense_category.created_message')
|
||||
)
|
||||
} else {
|
||||
window.toastr['success'](this.$t('settings.expense_category.updated_message'))
|
||||
window.toastr['success'](
|
||||
this.$t('settings.expense_category.updated_message')
|
||||
)
|
||||
}
|
||||
window.hub.$emit('newCategory', response.data.category)
|
||||
this.refreshData ? this.refreshData() : ''
|
||||
this.closeCategoryModal()
|
||||
this.isLoading = false
|
||||
return true
|
||||
}
|
||||
window.toastr['error'](response.data.error)
|
||||
},
|
||||
async setData () {
|
||||
async setData() {
|
||||
this.formData = {
|
||||
id: this.modalData.id,
|
||||
name: this.modalData.name,
|
||||
description: this.modalData.description
|
||||
description: this.modalData.description,
|
||||
}
|
||||
},
|
||||
closeCategoryModal () {
|
||||
closeCategoryModal() {
|
||||
this.resetFormData()
|
||||
this.closeModal()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
500
resources/assets/js/components/base/modal/CustomField/Index.vue
Normal file
500
resources/assets/js/components/base/modal/CustomField/Index.vue
Normal file
@ -0,0 +1,500 @@
|
||||
<template>
|
||||
<div class="custom-field-modal">
|
||||
<form action="" @submit.prevent="submitCustomFieldData">
|
||||
<div
|
||||
class="px-8 py-8 overflow-y-auto sw-scroll sm:p-6"
|
||||
style="max-height: 600px"
|
||||
>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.name')"
|
||||
:error="nameError"
|
||||
horizontal
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
ref="name"
|
||||
:invalid="$v.formData.name.$error"
|
||||
v-model="formData.name"
|
||||
type="text"
|
||||
@input="$v.formData.name.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.model')"
|
||||
:error="modalTypeError"
|
||||
class="mt-5"
|
||||
horizontal
|
||||
required
|
||||
>
|
||||
<sw-select
|
||||
v-model="formData.model_type"
|
||||
:options="modelTypes"
|
||||
:invalid="$v.formData.model_type.$error"
|
||||
:searchable="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
@input="$v.formData.model_type.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.required')"
|
||||
class="mt-5"
|
||||
horizontal
|
||||
>
|
||||
<sw-switch v-model="formData.is_required" style="margin-top: -20px" />
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.type')"
|
||||
:error="dataTypeError"
|
||||
class="mt-5"
|
||||
horizontal
|
||||
required
|
||||
>
|
||||
<sw-select
|
||||
v-model="selectType"
|
||||
:options="dataTypes"
|
||||
:invalid="$v.selectType.$error"
|
||||
:searchable="true"
|
||||
:show-labels="false"
|
||||
:allow-empty="false"
|
||||
track-by="label"
|
||||
label="label"
|
||||
@input="onSelectTypeChange"
|
||||
/>
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.label')"
|
||||
:error="labelError"
|
||||
class="mt-5"
|
||||
horizontal
|
||||
required
|
||||
>
|
||||
<sw-input
|
||||
ref="name"
|
||||
:invalid="$v.formData.label.$error"
|
||||
v-model="formData.label"
|
||||
type="text"
|
||||
@input="$v.formData.label.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.options')"
|
||||
class="mt-5"
|
||||
v-if="isDropdownSelected"
|
||||
horizontal
|
||||
>
|
||||
<option-create @onAdd="addNewOptions" />
|
||||
<div
|
||||
v-for="(option, index) in formData.options"
|
||||
:key="index"
|
||||
class="flex items-center"
|
||||
style="margin-top: 5px"
|
||||
>
|
||||
<sw-input v-model="option.name" type="text" style="width: 90%" />
|
||||
<minus-circle-icon
|
||||
@click="removeOption(index)"
|
||||
class="ml-1 cursor-pointer icon text-danger"
|
||||
/>
|
||||
</div>
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.default_value')"
|
||||
horizontal
|
||||
class="relative mt-5"
|
||||
v-if="formData.type"
|
||||
>
|
||||
<component
|
||||
:value="formData.default_answer"
|
||||
:is="formData.type + 'Type'"
|
||||
:options="formData.options"
|
||||
:defaultDateTime="formData.dateTimeValue"
|
||||
v-model="formData.default_answer"
|
||||
/>
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.placeholder')"
|
||||
v-if="!isSwitchTypeSelected"
|
||||
class="mt-5"
|
||||
horizontal
|
||||
>
|
||||
<sw-input v-model="formData.placeholder" type="text" />
|
||||
</sw-input-group>
|
||||
<sw-input-group
|
||||
:label="$t('settings.custom_fields.order')"
|
||||
:error="orderError"
|
||||
class="mt-5"
|
||||
horizontal
|
||||
>
|
||||
<sw-input
|
||||
v-model="formData.order"
|
||||
:invalid="$v.formData.order.$error"
|
||||
type="number"
|
||||
@input="$v.formData.order.$touch()"
|
||||
/>
|
||||
</sw-input-group>
|
||||
</div>
|
||||
<div
|
||||
class="z-0 flex justify-end p-4 border-t border-solid border-gray-light border-modal-bg"
|
||||
>
|
||||
<sw-button
|
||||
class="mr-3"
|
||||
type="button"
|
||||
variant="primary-outline"
|
||||
@click="closeCategoryModal"
|
||||
>
|
||||
{{ $t('general.cancel') }}
|
||||
</sw-button>
|
||||
<sw-button
|
||||
:loading="isLoading"
|
||||
:disabled="isLoading"
|
||||
variant="primary"
|
||||
type="submit"
|
||||
>
|
||||
<save-icon v-if="!isLoading" class="mr-2" />
|
||||
{{ !isEdit ? $t('general.save') : $t('general.update') }}
|
||||
</sw-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import { MinusCircleIcon } from '@vue-hero-icons/solid'
|
||||
import InputType from './types/InputType'
|
||||
import NumberType from './types/NumberType'
|
||||
import SwitchType from './types/SwitchType'
|
||||
import TextAreaType from './types/TextAreaType'
|
||||
import TimeType from './types/TimeType'
|
||||
import UrlType from './types/UrlType'
|
||||
import PhoneType from './types/PhoneType'
|
||||
import DateTimeType from './types/DateTimeType'
|
||||
import DateType from './types/DateType'
|
||||
import DropdownType from './types/DropdownType'
|
||||
|
||||
import OptionCreate from './types/OptionsCreate'
|
||||
import moment from 'moment'
|
||||
|
||||
const {
|
||||
required,
|
||||
requiredIf,
|
||||
numeric,
|
||||
minLength,
|
||||
} = require('vuelidate/lib/validators')
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MinusCircleIcon,
|
||||
OptionCreate,
|
||||
InputType,
|
||||
NumberType,
|
||||
SwitchType,
|
||||
TextAreaType,
|
||||
TimeType,
|
||||
UrlType,
|
||||
PhoneType,
|
||||
DateTimeType,
|
||||
DateType,
|
||||
DropdownType,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEdit: false,
|
||||
dateType: 'custom',
|
||||
isLoading: false,
|
||||
relativeDateTypes: [
|
||||
'Today',
|
||||
'Tomorrow',
|
||||
'Yesterday',
|
||||
'Starting Date of Week',
|
||||
'Ending Date of Week',
|
||||
'Starting Date of Next Week',
|
||||
'Ending Date of Next Week',
|
||||
'Starting Date of Previous Week',
|
||||
'Ending Date of Previous Week',
|
||||
'Starting Date of Month',
|
||||
'Ending Date of Month',
|
||||
'Starting Date of Next Month',
|
||||
'Ending Date of Next Month',
|
||||
'Starting Date of Previous Month',
|
||||
'Ending Date of Previous Month',
|
||||
'Starting Date of Fiscal Month',
|
||||
'Ending Date of Fiscal Month',
|
||||
],
|
||||
dataTypes: [
|
||||
{ label: 'Text', value: 'Input' },
|
||||
{ label: 'Textarea', value: 'TextArea' },
|
||||
{ label: 'Phone', value: 'Phone' },
|
||||
{ label: 'URL', value: 'Url' },
|
||||
{ label: 'Number', value: 'Number' },
|
||||
{ label: 'Select Field', value: 'Dropdown' },
|
||||
{ label: 'Switch Toggle', value: 'Switch' },
|
||||
{ label: 'Date', value: 'Date' },
|
||||
{ label: 'Time', value: 'Time' },
|
||||
{ label: 'Date & Time', value: 'DateTime' },
|
||||
],
|
||||
modelTypes: ['Customer', 'Invoice', 'Estimate', 'Expense', 'Payment'],
|
||||
selectType: null,
|
||||
formData: {
|
||||
label: null,
|
||||
type: null,
|
||||
name: null,
|
||||
default_answer: null,
|
||||
is_required: false,
|
||||
placeholder: null,
|
||||
model_type: null,
|
||||
order: 1,
|
||||
options: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
selectType: {
|
||||
required,
|
||||
},
|
||||
formData: {
|
||||
name: {
|
||||
required,
|
||||
},
|
||||
label: {
|
||||
required,
|
||||
},
|
||||
model_type: {
|
||||
required,
|
||||
},
|
||||
order: {
|
||||
required,
|
||||
numeric,
|
||||
},
|
||||
options: {
|
||||
required: requiredIf('isDropdownSelected'),
|
||||
minLength: minLength(1),
|
||||
$each: {
|
||||
name: {
|
||||
required: requiredIf('isDropdownSelected'),
|
||||
minLength: minLength(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('modal', ['modalData', 'modalDataID', 'refreshData']),
|
||||
isDropdownSelected() {
|
||||
if (this.selectType && this.selectType.label === 'Select Field') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
isSwitchTypeSelected() {
|
||||
if (this.selectType && this.selectType.label === 'Switch Toggle') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
nameError() {
|
||||
if (!this.$v.formData.name.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.name.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
},
|
||||
labelError() {
|
||||
if (!this.$v.formData.label.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.label.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
},
|
||||
modalTypeError() {
|
||||
if (!this.$v.formData.model_type.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.model_type.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
},
|
||||
dataTypeError() {
|
||||
if (!this.$v.selectType.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.selectType.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
},
|
||||
hasPlaceHolder() {
|
||||
if (this.selectType.label == 'Switch Toggle') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
orderError() {
|
||||
if (!this.$v.formData.order.$error) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!this.$v.formData.order.required) {
|
||||
return this.$tc('validation.required')
|
||||
}
|
||||
|
||||
if (!this.$v.formData.order.numeric) {
|
||||
return this.$tc('validation.numbers_only')
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'formData.type'(newValue, oldvalue) {
|
||||
if (oldvalue != null || oldvalue != undefined) {
|
||||
this.onChangeReset()
|
||||
}
|
||||
},
|
||||
dateType(newValue, oldvalue) {
|
||||
if (oldvalue != null || oldvalue != undefined) {
|
||||
this.onChangeReset()
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.modalDataID) {
|
||||
this.setData()
|
||||
return true
|
||||
}
|
||||
this.formData.model_type = this.modelTypes[0]
|
||||
this.selectType = this.dataTypes[0]
|
||||
this.formData.type = this.dataTypes[0].value
|
||||
},
|
||||
methods: {
|
||||
...mapActions('customFields', [
|
||||
'addCustomField',
|
||||
'updateCustomField',
|
||||
'fetchCustomField',
|
||||
]),
|
||||
...mapActions('modal', ['closeModal']),
|
||||
resetFormData() {
|
||||
this.formData = {
|
||||
label: null,
|
||||
label: null,
|
||||
type: null,
|
||||
dateTimeValue: null,
|
||||
default_answer: null,
|
||||
is_required: false,
|
||||
placeholder: null,
|
||||
model_type: null,
|
||||
options: [{ name: '' }],
|
||||
}
|
||||
this.$v.$reset()
|
||||
},
|
||||
async submitCustomFieldData() {
|
||||
this.$v.selectType.$touch()
|
||||
this.$v.formData.$touch()
|
||||
|
||||
if (this.$v.$invalid) {
|
||||
return false
|
||||
}
|
||||
|
||||
let data = {
|
||||
...this.formData,
|
||||
options: this.formData.options.map((option) => option.name),
|
||||
default_answer:
|
||||
this.isDropdownSelected && this.formData.default_answer
|
||||
? this.formData.default_answer.name
|
||||
: this.formData.default_answer,
|
||||
}
|
||||
if (this.isSwitchTypeSelected && this.formData.default_answer == null) {
|
||||
data.default_answer = false
|
||||
}
|
||||
if (data.type == 'Date') {
|
||||
data.default_answer = data.default_answer
|
||||
? moment(data.default_answer).format('YYYY-MM-DD')
|
||||
: null
|
||||
}
|
||||
if (data.type == 'Time' && typeof data.default_answer == 'object') {
|
||||
let HH =
|
||||
data && data.default_answer && data.default_answer.HH
|
||||
? data.default_answer.HH
|
||||
: null
|
||||
let mm =
|
||||
data && data.default_answer && data.default_answer.mm
|
||||
? data.default_answer.mm
|
||||
: null
|
||||
let ss =
|
||||
data && data.default_answer && data.default_answer.ss
|
||||
? data.default_answer.ss
|
||||
: null
|
||||
data.default_answer = `${HH}:${mm}:${ss}`
|
||||
}
|
||||
let response = null
|
||||
if (this.isEdit) {
|
||||
this.isLoading = true
|
||||
response = await this.updateCustomField(data)
|
||||
window.toastr['success'](
|
||||
this.$tc('settings.custom_fields.updated_message')
|
||||
)
|
||||
this.refreshData()
|
||||
this.closeCategoryModal()
|
||||
return true
|
||||
}
|
||||
|
||||
this.isLoading = true
|
||||
response = await this.addCustomField(data)
|
||||
|
||||
window.toastr['success'](this.$tc('settings.custom_fields.added_message'))
|
||||
this.refreshData()
|
||||
this.closeCategoryModal()
|
||||
return true
|
||||
},
|
||||
addNewOptions(option) {
|
||||
this.formData.options = [{ name: option }, ...this.formData.options]
|
||||
},
|
||||
removeOption(index) {
|
||||
this.formData.options.splice(index, 1)
|
||||
},
|
||||
async setData() {
|
||||
let response = await this.fetchCustomField(this.modalDataID)
|
||||
let fieldData = response.data.customField
|
||||
this.isEdit = true
|
||||
let data = {
|
||||
...fieldData,
|
||||
options: [],
|
||||
dateTimeValue: fieldData.defaultAnswer,
|
||||
default_answer: fieldData.defaultAnswer,
|
||||
options: fieldData.options
|
||||
? fieldData.options.map((option) => {
|
||||
return { name: option ? option : '' }
|
||||
})
|
||||
: [],
|
||||
}
|
||||
this.selectType = this.dataTypes.find(
|
||||
(type) => type.value == fieldData.type
|
||||
)
|
||||
if (data.type == 'Dropdown') {
|
||||
data.default_answer = { name: fieldData.defaultAnswer }
|
||||
}
|
||||
this.formData = { ...data }
|
||||
},
|
||||
onChangeReset() {
|
||||
this.formData = {
|
||||
...this.formData,
|
||||
default_answer: null,
|
||||
is_required: false,
|
||||
placeholder: null,
|
||||
options: [],
|
||||
}
|
||||
},
|
||||
onSelectTypeChange(data) {
|
||||
this.formData.type = data.value
|
||||
this.$v.selectType.$touch()
|
||||
},
|
||||
closeCategoryModal() {
|
||||
this.resetFormData()
|
||||
this.closeModal()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<base-date-picker
|
||||
v-model="inputValue"
|
||||
:enable-time="true"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
defaultDateTime: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value ? this.value : moment().format('YYYY-MM-DD hh:mm'),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
defaultDateTime(data) {
|
||||
this.dateTimeValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<base-date-picker
|
||||
:calendar-button="true"
|
||||
v-model="inputValue"
|
||||
calendar-button-icon="calendar"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Date],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: moment().format('YYYY-MM-DD'),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<sw-select
|
||||
v-model="inputValue"
|
||||
:options="inputOptions"
|
||||
:taggable="true"
|
||||
:show-labels="false"
|
||||
label="name"
|
||||
track-by="name"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Object],
|
||||
default: null,
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
inputOptions: this.options,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options(data) {
|
||||
this.inputOptions = data
|
||||
this.inputValue = null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<sw-input
|
||||
v-model="inputValue"
|
||||
type="text"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<sw-input
|
||||
v-model="inputValue"
|
||||
type="number"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="flex items-center" style="margin-top: 5px">
|
||||
<sw-input
|
||||
v-model="option"
|
||||
type="text"
|
||||
style="width: 90%"
|
||||
@handleEnter.stop="onAddOption"
|
||||
/>
|
||||
<plus-circle-icon
|
||||
class="ml-1 cursor-pointer text-danger"
|
||||
@click="onAddOption"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { PlusCircleIcon } from '@vue-hero-icons/solid'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlusCircleIcon,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
option: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onAddOption() {
|
||||
if (
|
||||
this.option == null ||
|
||||
this.option == '' ||
|
||||
this.option == undefined
|
||||
) {
|
||||
return true
|
||||
}
|
||||
this.$emit('onAdd', this.option)
|
||||
this.option = null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<sw-input
|
||||
v-model="inputValue"
|
||||
type="text"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<sw-switch
|
||||
class="-mt-3"
|
||||
v-model="inputValue"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [Boolean, Number],
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<sw-textarea
|
||||
v-model="inputValue"
|
||||
rows="2"
|
||||
name="description"
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<base-time-picker
|
||||
:value="inputValue"
|
||||
v-model="inputValue"
|
||||
hide-clear-button
|
||||
@input="$emit('input', inputValue)"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Object],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(data) {
|
||||
this.inputValue = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user