mirror of https://github.com/nocodb/nocodb
Pranav C
2 years ago
44 changed files with 3345 additions and 74 deletions
@ -0,0 +1,123 @@
|
||||
<template> |
||||
<div |
||||
class="d-flex align-center img-container d-100 h-100" |
||||
v-on="$listeners" |
||||
@dragover.prevent="dragOver = true" |
||||
@dragenter.prevent="dragOver = true" |
||||
@dragexit="dragOver = false" |
||||
@dragleave="dragOver = false" |
||||
@dragend="dragOver = false" |
||||
@drop.prevent |
||||
> |
||||
<div v-if="dragOver" class="drop-overlay"> |
||||
<div> |
||||
<v-icon small> |
||||
mdi-cloud-upload-outline |
||||
</v-icon> |
||||
<span class="caption font-weight-bold">Drop here</span> |
||||
</div> |
||||
</div> |
||||
<template v-if="localState"> |
||||
<div v-for="item in localState" :key="item.title" class="thumbnail d-flex align-center justify-center"> |
||||
<v-lazy class="d-flex align-center justify-center"> |
||||
<v-tooltip bottom> |
||||
<template #activator="{on}"> |
||||
<img v-if="isImage(item.title)" alt="#" :src="item.url" v-on="on"> |
||||
<v-icon v-else-if="item.icon" size="33" v-on="on"> |
||||
{{ item.icon }} |
||||
</v-icon> |
||||
<v-icon v-else size="33" v-on="on"> |
||||
mdi-file |
||||
</v-icon> |
||||
</template> |
||||
<span>{{ item.title }}</span> |
||||
</v-tooltip> |
||||
</v-lazy> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { isImage } from '@/components/project/spreadsheet/helpers/imageExt' |
||||
|
||||
export default { |
||||
name: 'AttachmentCell', |
||||
props: ['value', 'column'], |
||||
data: () => ({ |
||||
dragOver: false |
||||
}), |
||||
computed: { |
||||
localState() { |
||||
try { |
||||
return JSON.parse(this.value) || [] |
||||
} catch (e) { |
||||
return [] |
||||
} |
||||
} |
||||
}, |
||||
methods: { |
||||
isImage |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.img-container { |
||||
margin: 0 -2px; |
||||
position: relative; |
||||
} |
||||
|
||||
.thumbnail { |
||||
height: 29px; |
||||
width: 29px; |
||||
margin: 2px; |
||||
border-radius: 4px; |
||||
} |
||||
|
||||
.thumbnail img { |
||||
max-height: 29px; |
||||
max-width: 29px; |
||||
} |
||||
|
||||
.drop-overlay { |
||||
z-index: 5; |
||||
position: absolute; |
||||
width: 100%; |
||||
height: 100%; |
||||
left: 0; |
||||
right: 0; |
||||
top: 0; |
||||
bottom: 5px; |
||||
background: #aaaaaa44; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
pointer-events: none; |
||||
} |
||||
|
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts"> |
||||
import { inject } from '@vue/runtime-core' |
||||
|
||||
const value = inject<boolean | number>('value') |
||||
</script> |
||||
|
||||
<template> |
||||
<div class="d-flex align-center"> |
||||
<input v-model="value" type="checkbox" :disabled="true"> |
||||
</div> |
||||
</template> |
@ -0,0 +1,40 @@
|
||||
<template> |
||||
<a v-if="value">{{ currency }}</a> |
||||
<span v-else /> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'CurrencyCell', |
||||
props: { |
||||
column: Object, |
||||
value: [String, Number] |
||||
}, |
||||
computed: { |
||||
currency() { |
||||
try { |
||||
return isNaN(this.value) |
||||
? this.value |
||||
: new Intl.NumberFormat(this.currencyMeta.currency_locale || 'en-US', |
||||
{ style: 'currency', currency: this.currencyMeta.currency_code || 'USD' }) |
||||
.format(this.value) |
||||
} catch (e) { |
||||
return this.value |
||||
} |
||||
}, |
||||
currencyMeta() { |
||||
return { |
||||
currency_locale: 'en-US', |
||||
currency_code: 'USD', |
||||
...(this.column && this.column.meta |
||||
? this.column.meta |
||||
: {}) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,21 @@
|
||||
<template> |
||||
<span>{{ date }}</span> |
||||
</template> |
||||
|
||||
<script> |
||||
import dayjs from 'dayjs' |
||||
|
||||
export default { |
||||
name: 'DateCell', |
||||
props: ['value'], |
||||
computed: { |
||||
date() { |
||||
return (/^\d+$/.test(this.value) ? dayjs(+this.value) : dayjs(this.value)).format('YYYY-MM-DD') |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,21 @@
|
||||
<template> |
||||
<span>{{ dateTime }}</span> |
||||
</template> |
||||
|
||||
<script> |
||||
import dayjs from 'dayjs' |
||||
|
||||
export default { |
||||
name: 'DateTimeCell', |
||||
props: ['value'], |
||||
computed: { |
||||
dateTime() { |
||||
return !this.value ? this.value : (/^\d+$/.test(this.value) ? dayjs(+this.value) : dayjs(this.value)).format('YYYY-MM-DD HH:mm') |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,69 @@
|
||||
<template> |
||||
<input |
||||
v-model="localValue" |
||||
:placeholder="durationPlaceholder" |
||||
readonly |
||||
> |
||||
</template> |
||||
|
||||
<script> |
||||
import { durationOptions, convertMS2Duration } from '~/helpers/durationHelper' |
||||
|
||||
export default { |
||||
name: 'DurationCell', |
||||
props: { |
||||
column: Object, |
||||
value: [String, Number] |
||||
}, |
||||
data: () => ({ |
||||
showWarningMessage: false, |
||||
localValue: null |
||||
}), |
||||
computed: { |
||||
durationPlaceholder() { |
||||
return durationOptions[this.column?.meta?.duration || 0].title |
||||
} |
||||
}, |
||||
watch: { |
||||
'column.meta.duration'(newValue, oldValue) { |
||||
if (oldValue !== newValue) { |
||||
this.localValue = convertMS2Duration(this.value, newValue) |
||||
} |
||||
}, |
||||
value(val, oldVal) { |
||||
this.localValue = convertMS2Duration(val !== oldVal && (!val && val !== 0) ? oldVal : val, this.column?.meta?.duration || 0) |
||||
} |
||||
}, |
||||
created() { |
||||
this.localValue = convertMS2Duration(this.value, this.column?.meta?.duration || 0) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
||||
|
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Wing-Kam Wong <wingkwong.code@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,22 @@
|
||||
<template> |
||||
<a v-if="isEmail" :href="`mailto:${value}`" target="_blank">{{ value }}</a> |
||||
<span v-else>{{ value }}</span> |
||||
</template> |
||||
|
||||
<script> |
||||
import { isEmail } from '~/helpers' |
||||
|
||||
export default { |
||||
name: 'EmailCell', |
||||
props: ['value'], |
||||
computed: { |
||||
isEmail() { |
||||
return isEmail(this.value || '') |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,31 @@
|
||||
<script setup lang="ts"> |
||||
import type { ColumnType } from 'nocodb-sdk' |
||||
import { inject } from 'vue' |
||||
import { enumColor } from '~/composables/colors' |
||||
|
||||
const colors = enumColor.light |
||||
|
||||
const value = inject('value') |
||||
const column = inject<ColumnType>('column') |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<span |
||||
v-for="v in [(value || '').replace(/\\'/g, '\'').replace(/^'|'$/g, '')]" |
||||
:key="v" |
||||
:style="{ |
||||
background: colors[v], |
||||
}" |
||||
class="set-item ma-1 py-1 px-3" |
||||
>{{ v }}</span> |
||||
</div> |
||||
</template> |
||||
|
||||
<style scoped> |
||||
.set-item { |
||||
display: inline-block; |
||||
border-radius: 25px; |
||||
white-space: nowrap; |
||||
} |
||||
</style> |
@ -0,0 +1,14 @@
|
||||
<template> |
||||
<pre class="text-left caption">{{ value }}</pre> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'JsonCell', |
||||
props: ['value'] |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,68 @@
|
||||
<script> |
||||
// import colors from '@/mixins/colors' |
||||
|
||||
export default { |
||||
name: 'SetListCell', |
||||
// mixins: [colors], |
||||
props: ['value', 'column'], |
||||
computed: { |
||||
setValues() { |
||||
if (this.column && this.column.dtxp) |
||||
return this.column.dtxp.split(',').map(v => v.replace(/\\'/g, '\'').replace(/^'|'$/g, '')) |
||||
|
||||
return [] |
||||
}, |
||||
selectedValues() { |
||||
return this.value ? this.value.split(',').map(v => v.replace(/\\'/g, '\'').replace(/^'|'$/g, '')) : [] |
||||
}, |
||||
}, |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<div> |
||||
<v-chip |
||||
v-for="v in selectedValues" |
||||
v-show="v || setValues.includes(v)" |
||||
:key="v" |
||||
small |
||||
:color="colors[setValues.indexOf(v) % colors.length]" |
||||
class="set-item ma-1 py-1 px-3" |
||||
> |
||||
{{ v }} |
||||
</v-chip> |
||||
</div> |
||||
</template> |
||||
|
||||
<style scoped> |
||||
/* |
||||
.set-item { |
||||
display: inline-block; |
||||
border-radius: 25px; |
||||
white-space: nowrap; |
||||
}*/ |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,19 @@
|
||||
<template> |
||||
<span>{{ time }}</span> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'TimeCell', |
||||
props: ['value'], |
||||
computed: { |
||||
time() { |
||||
return typeof this.value === 'string' ? this.value.replace(/(\d)T(?=\d)/, '$1 ') : this.value |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,23 @@
|
||||
<template> |
||||
<a v-if="isValid" :href="value" target="_blank">{{ value }}</a> |
||||
<span v-else>{{ value }}</span> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
import { isValidURL } from '~/helpers' |
||||
|
||||
export default { |
||||
name: 'UrlCell', |
||||
props: ['value'], |
||||
computed: { |
||||
isValid() { |
||||
return this.value && isValidURL(this.value) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,85 @@
|
||||
<template> |
||||
<div class="d-flex align-center " :class="{'justify-center':!isForm,'nc-cell-hover-show': !localState}"> |
||||
<v-icon small :color="checkboxMeta.color" @click="toggle"> |
||||
{{ localState ? checkedIcon :uncheckedIcon }} |
||||
</v-icon> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'BooleanCell', |
||||
props: { |
||||
column: Object, |
||||
value: [String, Number, Boolean], |
||||
isForm: Boolean, |
||||
readOnly: Boolean |
||||
}, |
||||
computed: { |
||||
|
||||
checkedIcon() { |
||||
return (this.checkboxMeta && this.checkboxMeta.icon && this.checkboxMeta.icon.checked) || 'mdi-check-bold' |
||||
}, |
||||
uncheckedIcon() { |
||||
return (this.checkboxMeta && this.checkboxMeta.icon && this.checkboxMeta.icon.unchecked) || 'mdi-crop-square' |
||||
}, |
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val) |
||||
} |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
return $listeners |
||||
}, |
||||
checkboxMeta() { |
||||
return { |
||||
icon: { |
||||
checked: 'mdi-check-circle-outline', |
||||
unchecked: 'mdi-checkbox-blank-circle-outline' |
||||
}, |
||||
color: 'primary', |
||||
...(this.column && this.column.meta |
||||
? this.column.meta |
||||
: {}) |
||||
} |
||||
} |
||||
}, |
||||
methods: { |
||||
toggle() { |
||||
this.localState = !this.localState |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,92 @@
|
||||
<template> |
||||
<v-menu> |
||||
<template #activator="{on}"> |
||||
<input :value="date" class="value" v-on="on"> |
||||
</template> |
||||
<v-date-picker |
||||
v-model="localState" |
||||
flat |
||||
@click.native.stop |
||||
v-on="parentListeners" |
||||
/> |
||||
</v-menu> |
||||
</template> |
||||
|
||||
<script> |
||||
import dayjs from 'dayjs' |
||||
|
||||
export default { |
||||
name: 'DatePickerCell', |
||||
props: { |
||||
value: [String, Date] |
||||
}, |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
if (!this.value || !dayjs(this.value).isValid()) { return undefined } |
||||
|
||||
return (/^\d+$/.test(this.value) ? dayjs(+this.value) : dayjs(this.value)).format('YYYY-MM-DD') |
||||
}, |
||||
set(val) { |
||||
if (dayjs(val).isValid()) { |
||||
this.$emit('input', val && dayjs(val).format('YYYY-MM-DD')) |
||||
} |
||||
} |
||||
}, |
||||
date() { |
||||
if (!this.value || this.localState) { |
||||
return this.localState |
||||
} |
||||
return 'Invalid Date' |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
if (this.$el && this.$el.$el) { |
||||
this.$el.$el.focus() |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.value { |
||||
width: 100%; |
||||
min-height: 20px; |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,132 @@
|
||||
<template> |
||||
<div> |
||||
<div v-show="!showMessage"> |
||||
<v-datetime-picker |
||||
ref="picker" |
||||
v-model="localState" |
||||
class="caption xc-date-time-picker" |
||||
:text-field-props="{ |
||||
class:'caption mt-0 pt-0', |
||||
flat:true, |
||||
solo:true, |
||||
dense:true, |
||||
hideDetails:true |
||||
}" |
||||
:time-picker-props="{ |
||||
format:'24hr' |
||||
}" |
||||
v-on="parentListeners" |
||||
/> |
||||
</div> |
||||
<div v-show="showMessage" class="edit-warning" @dblclick="$refs.picker.display = true"> |
||||
<!-- TODO: i18n --> |
||||
ERR: Couldn't parse {{ this.value }} |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
import dayjs from 'dayjs' |
||||
import utc from 'dayjs/plugin/utc' |
||||
|
||||
dayjs.extend(utc) |
||||
|
||||
export default { |
||||
name: 'DateTimePickerCell', |
||||
props: { |
||||
value: [String, Date, Number], ignoreFocus: Boolean |
||||
}, |
||||
data: () => ({ |
||||
showMessage: false |
||||
}), |
||||
computed: { |
||||
isMysql() { |
||||
return ['mysql', 'mysql2'].indexOf(this.$store.getters['project/GtrClientType']) |
||||
}, |
||||
localState: { |
||||
get() { |
||||
if (!this.value) { |
||||
return this.value |
||||
} |
||||
const d = (/^\d+$/.test(this.value) ? dayjs(+this.value) : dayjs(this.value)) |
||||
if (d.isValid()) { |
||||
this.showMessage = false |
||||
return d.format('YYYY-MM-DD HH:mm') |
||||
} else { |
||||
this.showMessage = true |
||||
} |
||||
}, |
||||
set(value) { |
||||
if (this.isMysql) { |
||||
this.$emit('input', value && dayjs(value).format('YYYY-MM-DD HH:mm:ss')) |
||||
} else { |
||||
this.$emit('input', value && dayjs(value).format('YYYY-MM-DD HH:mm:ssZ')) |
||||
} |
||||
} |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
// $listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
// listen dialog click:outside event and save on close |
||||
if (this.$refs.picker && this.$refs.picker.$children && this.$refs.picker.$children[0]) { |
||||
this.$refs.picker.$children[0].$on('click:outside', () => { |
||||
this.$refs.picker.okHandler() |
||||
}) |
||||
} |
||||
|
||||
if (!this.ignoreFocus) { |
||||
this.$refs.picker.display = true |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
/deep/ .v-input, /deep/ .v-text-field { |
||||
margin-top: 0 !important; |
||||
padding-top: 0 !important; |
||||
font-size: inherit !important; |
||||
} |
||||
|
||||
.edit-warning { |
||||
padding: 10px; |
||||
text-align: left; |
||||
color: #E65100; |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,139 @@
|
||||
<template> |
||||
<div class="duration-cell-wrapper"> |
||||
<input |
||||
ref="durationInput" |
||||
v-model="localState" |
||||
:placeholder="durationPlaceholder" |
||||
@blur="onBlur" |
||||
@keypress="checkDurationFormat($event)" |
||||
@keydown.enter="isEdited && $emit('input', durationInMS)" |
||||
v-on="parentListeners" |
||||
> |
||||
<div v-if="showWarningMessage == true" class="duration-warning"> |
||||
<!-- TODO: i18n --> |
||||
Please enter a number |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { durationOptions, convertMS2Duration, convertDurationToSeconds } from '~/helpers/durationHelper' |
||||
|
||||
export default { |
||||
name: 'DurationCell', |
||||
props: { |
||||
column: Object, |
||||
value: [Number, String], |
||||
readOnly: Boolean |
||||
}, |
||||
data: () => ({ |
||||
// flag to determine to show warning message or not |
||||
showWarningMessage: false, |
||||
// duration in milliseconds |
||||
durationInMS: null, |
||||
// check if the cell is edited or not |
||||
isEdited: false |
||||
}), |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
return convertMS2Duration(this.value, this.durationType) |
||||
}, |
||||
set(val) { |
||||
this.isEdited = true |
||||
const res = convertDurationToSeconds(val, this.durationType) |
||||
if (res._isValid) { |
||||
this.durationInMS = res._sec |
||||
} |
||||
} |
||||
}, |
||||
durationPlaceholder() { |
||||
return durationOptions[this.durationType].title |
||||
}, |
||||
durationType() { |
||||
return this.column?.meta?.duration || 0 |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
window.addEventListener('keypress', (_) => { |
||||
if (this.$refs.durationInput) { |
||||
this.$refs.durationInput.focus() |
||||
} |
||||
}) |
||||
}, |
||||
methods: { |
||||
checkDurationFormat(evt) { |
||||
evt = evt || window.event |
||||
const charCode = (evt.which) ? evt.which : evt.keyCode |
||||
// ref: http://www.columbia.edu/kermit/ascii.html |
||||
const PRINTABLE_CTL_RANGE = charCode > 31 |
||||
const NON_DIGIT = charCode < 48 || charCode > 57 |
||||
const NON_COLON = charCode !== 58 |
||||
const NON_PERIOD = charCode !== 46 |
||||
if (PRINTABLE_CTL_RANGE && NON_DIGIT && NON_COLON && NON_PERIOD) { |
||||
this.showWarningMessage = true |
||||
evt.preventDefault() |
||||
} else { |
||||
this.showWarningMessage = false |
||||
// only allow digits, '.' and ':' (without quotes) |
||||
return true |
||||
} |
||||
}, |
||||
onBlur() { |
||||
if (this.isEdited) { |
||||
this.$emit('input', this.durationInMS) |
||||
} |
||||
this.isEdited = false |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
.duration-cell-wrapper { |
||||
padding: 10px; |
||||
} |
||||
|
||||
.duration-warning { |
||||
text-align: left; |
||||
margin-top: 10px; |
||||
color: #E65100; |
||||
} |
||||
</style> |
||||
|
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Wing-Kam Wong <wingkwong.code@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,599 @@
|
||||
<template> |
||||
<div |
||||
class="main h-100" |
||||
@dragover.prevent="dragOver = true" |
||||
@dragenter.prevent="dragOver = true" |
||||
@dragexit="dragOver = false" |
||||
@dragleave="dragOver = false" |
||||
@dragend="dragOver = false" |
||||
@drop.prevent.stop="onFileDrop" |
||||
> |
||||
<div v-show="(isForm || _isUIAllowed('tableAttachment')) && dragOver" class="drop-overlay"> |
||||
<div> |
||||
<v-icon small> |
||||
mdi-cloud-upload-outline |
||||
</v-icon> |
||||
<span class="caption font-weight-bold">Drop here</span> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="d-flex align-center img-container"> |
||||
<div class="d-flex no-overflow"> |
||||
<div |
||||
v-for="(item,i) in (isPublicForm ? localFilesState : localState)" |
||||
:key="item.url || item.title" |
||||
class="thumbnail align-center justify-center d-flex" |
||||
> |
||||
<v-tooltip bottom> |
||||
<template #activator="{on}"> |
||||
<v-img |
||||
v-if="isImage(item.title, item.mimetype)" |
||||
lazy-src="https://via.placeholder.com/60.png?text=Loading..." |
||||
alt="#" |
||||
max-height="99px" |
||||
contain |
||||
:src="item.url || item.data" |
||||
v-on="on" |
||||
@click="selectImage(item.url || item.data, i)" |
||||
> |
||||
<template #placeholder> |
||||
<v-skeleton-loader |
||||
type="image" |
||||
:height="active ? 33 : 22" |
||||
:width="active ? 33 : 22" |
||||
/> |
||||
</template> |
||||
</v-img> |
||||
<v-icon |
||||
v-else-if="item.icon" |
||||
:size="active ? 33 : 22" |
||||
v-on="on" |
||||
@click="openUrl(item.url || item.data,'_blank')" |
||||
> |
||||
{{ |
||||
item.icon |
||||
}} |
||||
</v-icon> |
||||
<v-icon v-else :size="active ? 33 : 22" v-on="on" @click="openUrl(item.url|| item.data,'_blank')"> |
||||
mdi-file |
||||
</v-icon> |
||||
</template> |
||||
<span>{{ item.title }}</span> |
||||
</v-tooltip> |
||||
</div> |
||||
</div> |
||||
<div |
||||
v-if="isForm || active && !isPublicGrid && !isLocked" |
||||
class="add d-flex align-center justify-center px-1 nc-attachment-add" |
||||
@click="addFile" |
||||
> |
||||
<v-icon v-if="uploading" small color="primary" class="nc-attachment-add-spinner"> |
||||
mdi-loading mdi-spin |
||||
</v-icon> |
||||
<v-btn |
||||
v-else-if="isForm" |
||||
outlined |
||||
x-small |
||||
color="" |
||||
text |
||||
class="nc-attachment-add-btn" |
||||
> |
||||
<v-icon x-small color=""> |
||||
mdi-plus |
||||
</v-icon> |
||||
Attachment |
||||
</v-btn> |
||||
<v-icon |
||||
v-else-if="_isUIAllowed('tableAttachment')" |
||||
v-show="active" |
||||
small |
||||
color="primary nc-attachment-add-icon" |
||||
> |
||||
mdi-plus |
||||
</v-icon> |
||||
</div> |
||||
|
||||
<v-spacer /> |
||||
|
||||
<v-icon class="expand-icon mr-1" x-small color="primary" @click.stop="dialog = true"> |
||||
mdi-arrow-expand |
||||
</v-icon> |
||||
<input ref="file" type="file" multiple class="d-none" @change="onFileSelection"> |
||||
</div> |
||||
<v-dialog |
||||
v-if="dialog" |
||||
v-model="dialog" |
||||
width="800" |
||||
> |
||||
<v-card class="h-100 images-modal"> |
||||
<v-card-text class="h-100 backgroundColor"> |
||||
<div class="d-flex mx-2"> |
||||
<v-btn |
||||
v-if="(isForm || _isUIAllowed('tableAttachment')) && !isPublicGrid && !isLocked" |
||||
small |
||||
class="my-4 " |
||||
:loading="uploading" |
||||
@click="addFile" |
||||
> |
||||
<v-icon small class="mr-2"> |
||||
mdi-link-variant |
||||
</v-icon> |
||||
<span class="caption">Attach File</span> |
||||
</v-btn> |
||||
|
||||
<!-- <v-text-field v-model="urlString" @keypress.enter="uploadByUrl" />--> |
||||
</div> |
||||
|
||||
<div class="d-flex flex-wrap h-100"> |
||||
<v-container fluid style="max-height:calc(90vh - 80px);overflow-y: auto"> |
||||
<draggable |
||||
v-model="localState" |
||||
class="row" |
||||
@update="onOrderUpdate" |
||||
> |
||||
<v-col v-for="(item,i) in (isPublicForm ? localFilesState : localState)" :key="i" cols="4"> |
||||
<v-card |
||||
class="modal-thumbnail-card align-center justify-center d-flex" |
||||
height="200px" |
||||
style="position: relative" |
||||
> |
||||
<v-icon |
||||
v-if="_isUIAllowed('tableAttachment') && !isPublicGrid && !isLocked" |
||||
small |
||||
class="remove-icon" |
||||
@click="removeItem(i)" |
||||
> |
||||
mdi-close-circle |
||||
</v-icon> |
||||
<v-icon color="grey" class="download-icon" @click.stop="downloadItem(item,i)"> |
||||
mdi-download |
||||
</v-icon> |
||||
<div class="pa-2 d-flex align-center" style="height:200px"> |
||||
<img |
||||
v-if="isImage(item.title, item.mimetype)" |
||||
style="max-height: 100%;max-width: 100%" |
||||
alt="#" |
||||
:src="item.url || item.data" |
||||
@click="selectImage(item.url,i)" |
||||
> |
||||
|
||||
<v-icon v-else-if="item.icon" size="33" @click="openUrl(item.url || item.data,'_blank')"> |
||||
{{ |
||||
item.icon |
||||
}} |
||||
</v-icon> |
||||
<v-icon v-else size="33" @click="openUrl(item.url || item.data,'_blank')"> |
||||
mdi-file |
||||
</v-icon> |
||||
</div> |
||||
</v-card> |
||||
<p class="caption mt-2 modal-title" :title="item.title"> |
||||
{{ item.title }} |
||||
</p> |
||||
</v-col> |
||||
</draggable> |
||||
</v-container> |
||||
</div> |
||||
</v-card-text> |
||||
</v-card> |
||||
</v-dialog> |
||||
|
||||
<v-overlay v-if="showImage" v-model="showImage" z-index="99999" opacity=".93"> |
||||
<div v-click-outside="hideIfVisible" class="image-overlay-container"> |
||||
<template v-if="showImage && selectedImage"> |
||||
<v-carousel v-model="carousel" height="calc(100vh - 100px)" hide-delimiters> |
||||
<v-carousel-item |
||||
v-for="(item,i) in (isPublicForm ? localFilesState : localState)" |
||||
:key="i" |
||||
> |
||||
<div class="mx-auto d-flex flex-column justify-center align-center" style="min-height:100px"> |
||||
<p class="title text-center"> |
||||
{{ item.title }} |
||||
<v-icon class="ml-3" color="grey" @click.stop="downloadItem(item,i)"> |
||||
mdi-download |
||||
</v-icon> |
||||
</p> |
||||
<div style="width:90vh;height:calc(100vh - 150px)" class="d-flex align-center justify-center"> |
||||
<img |
||||
v-if="isImage(item.title, item.mimetype)" |
||||
style="max-width:90vh;max-height:calc(100vh - 100px)" |
||||
:src="item.url || item.data" |
||||
> |
||||
<v-icon v-else-if="item.icon" size="55"> |
||||
{{ item.icon }} |
||||
</v-icon> |
||||
<v-icon v-else size="55"> |
||||
mdi-file |
||||
</v-icon> |
||||
</div> |
||||
</div> |
||||
</v-carousel-item> |
||||
</v-carousel> |
||||
</template> |
||||
<v-sheet |
||||
v-if="showImage" |
||||
class="mx-auto align-center justify-center" |
||||
max-width="90vw" |
||||
height="80px" |
||||
style="background: transparent" |
||||
> |
||||
<v-slide-group |
||||
multiple |
||||
show-arrows |
||||
> |
||||
<v-slide-item |
||||
v-for="(item,i) in (isPublicForm ? localFilesState : localState)" |
||||
:key="i" |
||||
> |
||||
<v-card |
||||
:key="i" |
||||
class="ma-2 pa-2 d-flex align-center justify-center overlay-thumbnail" |
||||
:class="{active: carousel === i}" |
||||
width="48" |
||||
height="48" |
||||
@click="carousel = i" |
||||
> |
||||
<img |
||||
v-if="isImage(item.title, item.mimetype)" |
||||
style="max-width:100%;max-height:100%" |
||||
:src="item.url || item.data" |
||||
> |
||||
<v-icon v-else-if="item.icon" size="48"> |
||||
{{ item.icon }} |
||||
</v-icon> |
||||
<v-icon v-else size="48"> |
||||
mdi-file |
||||
</v-icon> |
||||
</v-card> |
||||
</v-slide-item> |
||||
</v-slide-group> |
||||
</v-sheet> |
||||
<v-icon x-large class="close-icon" @click="showImage=false"> |
||||
mdi-close-circle |
||||
</v-icon> |
||||
</div> |
||||
</v-overlay> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import FileSaver from 'file-saver' |
||||
import draggable from 'vuedraggable' |
||||
import { isImage } from '@/components/project/spreadsheet/helpers/imageExt' |
||||
|
||||
export default { |
||||
name: 'EditableAttachmentCell', |
||||
components: { draggable }, |
||||
props: ['dbAlias', 'value', 'active', 'isLocked', 'meta', 'column', 'isPublicGrid', 'isForm', 'isPublicForm', 'viewId'], |
||||
data: () => ({ |
||||
carousel: null, |
||||
uploading: false, |
||||
localState: '', |
||||
dialog: false, |
||||
showImage: false, |
||||
selectedImage: null, |
||||
dragOver: false, |
||||
localFilesState: [], |
||||
urlString: '' |
||||
}), |
||||
watch: { |
||||
value(val, prev) { |
||||
try { |
||||
this.localState = ((typeof val === 'string' && val !== prev ? JSON.parse(val) : val) || []).filter(Boolean) |
||||
} catch (e) { |
||||
this.localState = [] |
||||
} |
||||
} |
||||
}, |
||||
created() { |
||||
try { |
||||
this.localState = ((typeof this.value === 'string' ? JSON.parse(this.value) : this.value) || []).filter(Boolean) |
||||
} catch (e) { |
||||
this.localState = [] |
||||
} |
||||
document.addEventListener('keydown', this.onArrowDown) |
||||
}, |
||||
beforeDestroy() { |
||||
document.removeEventListener('keydown', this.onArrowDown) |
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: { |
||||
async uploadByUrl() { |
||||
const data = await this.$api.storage.uploadByUrl( |
||||
{ |
||||
path: ['noco', this.projectName, this.meta.title, this.column.title].join('/') |
||||
}, |
||||
[{ |
||||
url: this.urlString |
||||
}] |
||||
) |
||||
|
||||
this.localState.push(...data) |
||||
}, |
||||
openUrl(url, target) { |
||||
window.open(url, target) |
||||
}, |
||||
isImage, |
||||
hideIfVisible() { |
||||
if (this.showImage) { |
||||
this.showImage = false |
||||
} |
||||
}, |
||||
selectImage(selectedImage, i) { |
||||
this.carousel = i |
||||
this.selectedImage = selectedImage |
||||
this.showImage = true |
||||
}, |
||||
addFile() { |
||||
if (!this.isLocked) { |
||||
this.$refs.file.click() |
||||
} |
||||
}, |
||||
async onFileSelection() { |
||||
if (this.isPublicGrid) { |
||||
return |
||||
} |
||||
if (!this.$refs.file.files || !this.$refs.file.files.length) { |
||||
return |
||||
} |
||||
|
||||
if (this.isPublicForm) { |
||||
this.localFilesState.push(...Array.from(this.$refs.file.files).map((file) => { |
||||
const res = { file, title: file.name } |
||||
if (isImage(file.name, file.mimetype)) { |
||||
const reader = new FileReader() |
||||
reader.onload = (e) => { |
||||
this.$set(res, 'data', e.target.result) |
||||
} |
||||
reader.readAsDataURL(file) |
||||
} |
||||
return res |
||||
})) |
||||
|
||||
this.$emit('input', this.localFilesState.map(f => f.file)) |
||||
return |
||||
} |
||||
|
||||
this.uploading = true |
||||
for (const file of this.$refs.file.files) { |
||||
try { |
||||
const data = await this.$api.storage.upload( |
||||
{ |
||||
path: ['noco', this.projectName, this.meta.title, this.column.title].join('/') |
||||
}, { |
||||
files: file, |
||||
json: '{}' |
||||
} |
||||
) |
||||
|
||||
this.localState.push(...data) |
||||
} catch (e) { |
||||
this.$toast.error((e.message) || 'Some internal error occurred').goAway(3000) |
||||
this.uploading = false |
||||
return |
||||
} |
||||
} |
||||
|
||||
this.uploading = false |
||||
this.$emit('input', JSON.stringify(this.localState)) |
||||
this.$emit('update') |
||||
}, |
||||
onOrderUpdate() { |
||||
this.$emit('input', JSON.stringify(this.localState)) |
||||
this.$emit('update') |
||||
}, |
||||
removeItem(i) { |
||||
if (this.isPublicForm) { |
||||
this.localFilesState.splice(i, 1) |
||||
this.$emit('input', this.localFilesState.map(f => f.file)) |
||||
} else { |
||||
this.localState.splice(i, 1) |
||||
this.$emit('input', JSON.stringify(this.localState)) |
||||
} |
||||
this.$emit('update') |
||||
}, |
||||
downloadItem(item) { |
||||
FileSaver.saveAs(item.url || item.data, item.title) |
||||
}, |
||||
onArrowDown(e) { |
||||
if (!this.showImage) { |
||||
return |
||||
} |
||||
e = e || window.event |
||||
// eslint-disable-next-line eqeqeq |
||||
if (e.keyCode == '37') { |
||||
this.carousel = (this.carousel || this.localState.length) - 1 |
||||
// eslint-disable-next-line eqeqeq |
||||
} else if (e.keyCode == '39') { |
||||
this.carousel = ++this.carousel % this.localState.length |
||||
// eslint-disable-next-line eqeqeq |
||||
} else if (e.keyCode == '27') { |
||||
this.hideIfVisible() |
||||
} |
||||
}, |
||||
async onFileDrop(e) { |
||||
this.dragOver = false |
||||
this.$refs.file.files = e.dataTransfer.files |
||||
await this.onFileSelection() |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.img-container { |
||||
margin: 0 -2px; |
||||
} |
||||
|
||||
.no-overflow { |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.add { |
||||
transition: .2s background-color; |
||||
/*background-color: #666666ee;*/ |
||||
border-radius: 4px; |
||||
height: 33px; |
||||
margin: 5px 2px; |
||||
} |
||||
|
||||
.add:hover { |
||||
/*background-color: #66666699;*/ |
||||
} |
||||
|
||||
.thumbnail { |
||||
height: 99px; |
||||
width: 99px; |
||||
margin: 2px; |
||||
border-radius: 4px; |
||||
} |
||||
|
||||
.thumbnail img { |
||||
/*max-height: 33px;*/ |
||||
max-width: 99px; |
||||
} |
||||
|
||||
.main { |
||||
min-height: 20px; |
||||
position: relative; |
||||
height: auto; |
||||
} |
||||
|
||||
.expand-icon { |
||||
margin-left: 8px; |
||||
border-radius: 2px; |
||||
/*opacity: 0;*/ |
||||
transition: .3s background-color; |
||||
} |
||||
|
||||
.expand-icon:hover { |
||||
/*opacity: 1;*/ |
||||
background-color: var(--v-primary-lighten4); |
||||
} |
||||
|
||||
.modal-thumbnail img { |
||||
height: 50px; |
||||
max-width: 100%; |
||||
border-radius: 4px; |
||||
|
||||
} |
||||
|
||||
.modal-thumbnail { |
||||
position: relative; |
||||
margin: 10px 10px; |
||||
} |
||||
|
||||
.remove-icon { |
||||
position: absolute; |
||||
top: 5px; |
||||
right: 5px |
||||
} |
||||
|
||||
.modal-thumbnail-card { |
||||
|
||||
.download-icon { |
||||
position: absolute; |
||||
bottom: 5px; |
||||
right: 5px; |
||||
opacity: 0; |
||||
transition: .4s opacity; |
||||
} |
||||
|
||||
&:hover .download-icon { |
||||
opacity: 1 |
||||
} |
||||
} |
||||
|
||||
.image-overlay-container { |
||||
max-height: 100vh; |
||||
overflow-y: auto; |
||||
position: relative; |
||||
} |
||||
|
||||
.image-overlay-container .close-icon { |
||||
position: fixed; |
||||
top: 15px; |
||||
right: 15px |
||||
} |
||||
|
||||
.overlay-thumbnail { |
||||
transition: .4s transform, .4s opacity; |
||||
opacity: .5; |
||||
} |
||||
|
||||
.overlay-thumbnail.active { |
||||
transform: scale(1.4); |
||||
opacity: 1; |
||||
} |
||||
|
||||
.overlay-thumbnail:hover { |
||||
opacity: 1; |
||||
} |
||||
|
||||
.modal-title { |
||||
text-overflow: ellipsis; |
||||
white-space: nowrap; |
||||
width: 100%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.modal-thumbnail-card { |
||||
transition: .4s transform; |
||||
} |
||||
|
||||
.modal-thumbnail-card:hover { |
||||
transform: scale(1.05); |
||||
} |
||||
|
||||
.drop-overlay { |
||||
z-index: 5; |
||||
position: absolute; |
||||
width: 100%; |
||||
height: 100%; |
||||
left: 0; |
||||
right: 0; |
||||
top: 0; |
||||
bottom: 5px; |
||||
background: #aaaaaa44; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
pointer-events: none; |
||||
} |
||||
|
||||
.expand-icon { |
||||
opacity: 0; |
||||
transition: .4s opacity; |
||||
} |
||||
|
||||
.main:hover .expand-icon { |
||||
opacity: 1; |
||||
} |
||||
|
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* @author Wing-Kam Wong <wingkwong.code@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,80 @@
|
||||
<template> |
||||
<input v-model="localState" v-on="parentListeners"> |
||||
</template> |
||||
|
||||
<script> |
||||
import { isValidURL } from '@/helpers' |
||||
|
||||
export default { |
||||
name: 'EditableUrlCell', |
||||
props: { |
||||
value: String, |
||||
column: Object |
||||
}, |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
if (!( |
||||
this.column && |
||||
this.column.meta && |
||||
this.column.meta.validate |
||||
) || isValidURL(val)) { this.$emit('input', val) } |
||||
} |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
if (this.$listeners.cancel) { |
||||
$listeners.cancel = this.$listeners.cancel |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.$el.focus() |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
input, textarea { |
||||
width: 100%; |
||||
height: 100%; |
||||
color: var(--v-textColor-base); |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,132 @@
|
||||
<template> |
||||
<v-select |
||||
v-model="localState" |
||||
solo |
||||
dense |
||||
flat |
||||
:items="enumValues" |
||||
hide-details |
||||
class="mt-0" |
||||
:clearable="!column.rqd" |
||||
v-on="parentListeners" |
||||
> |
||||
<template #selection="{item}"> |
||||
<div |
||||
class="d-100" |
||||
:class="{ |
||||
'text-center' : !isForm |
||||
}" |
||||
> |
||||
<v-chip small :color="colors[enumValues.indexOf(item) % colors.length]" class="ma-1"> |
||||
{{ item }} |
||||
</v-chip> |
||||
</div> |
||||
</template> |
||||
<template #item="{item}"> |
||||
<v-chip small :color="colors[enumValues.indexOf(item) % colors.length]"> |
||||
{{ item }} |
||||
</v-chip> |
||||
</template> |
||||
<template #append> |
||||
<v-icon small class="mt-1"> |
||||
mdi-menu-down |
||||
</v-icon> |
||||
</template> |
||||
</v-select> |
||||
</template> |
||||
|
||||
<script> |
||||
import colors from '@/mixins/colors' |
||||
|
||||
export default { |
||||
name: 'EnumListEditableCell', |
||||
mixins: [colors], |
||||
|
||||
props: { |
||||
value: String, |
||||
column: Object, |
||||
isForm: Boolean |
||||
}, |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
return this.value && this.value.replace(/\\'/g, '\'').replace(/^'|'$/g, '') |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val) |
||||
} |
||||
}, |
||||
enumValues() { |
||||
if (this.column && this.column.dtxp) { |
||||
return this.column.dtxp |
||||
.split(',') |
||||
.map(v => v.replace(/\\'/g, '\'').replace(/^'|'$/g, '')) |
||||
} |
||||
return [] |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
// this.$el.focus(); |
||||
// let event; |
||||
// event = document.createEvent('MouseEvents'); |
||||
// event.initMouseEvent('mousedown', true, true, window); |
||||
// this.$el.dispatchEvent(event); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
::v-deep { |
||||
.v-select { |
||||
min-width: 150px; |
||||
} |
||||
.v-input__slot{ |
||||
padding-right: 0 !important; |
||||
padding-left: 35px !important; |
||||
} |
||||
.v-input__icon.v-input__icon--clear { |
||||
width: 15px !important; |
||||
min-width: 13px !important; |
||||
|
||||
.v-icon { |
||||
font-size: 13px !important; |
||||
} |
||||
} |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,101 @@
|
||||
<template> |
||||
<div class="d-flex align-center"> |
||||
<div> |
||||
<div v-for="(val,i) of enumValues" :key="val" class="item"> |
||||
<input :id="`key-radio-${val}`" v-model="localState" type="radio" class="orange--text" :value="val"> |
||||
<label |
||||
class="py-1 px-3 d-inline-block my-1 label" |
||||
:for="`key-radio-${val}`" |
||||
:style="{ |
||||
background:colors[i % colors.length ] |
||||
}" |
||||
>{{ val }}</label> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { enumColor as colors } from '@/components/project/spreadsheet/helpers/colors' |
||||
|
||||
export default { |
||||
name: 'EnumRadioEditableCell', |
||||
props: { |
||||
value: String, |
||||
column: Object |
||||
}, |
||||
computed: { |
||||
colors() { |
||||
return this.$store.state.settings.darkTheme ? colors.dark : colors.light |
||||
}, |
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val) |
||||
this.$emit('update') |
||||
} |
||||
}, |
||||
enumValues() { |
||||
if (this.column && this.column.dtxp) { |
||||
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')) |
||||
} |
||||
return [] |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
// this.$el.focus(); |
||||
// let event; |
||||
// event = document.createEvent('MouseEvents'); |
||||
// event.initMouseEvent('mousedown', true, true, window); |
||||
// this.$el.dispatchEvent(event); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
.label { |
||||
border-radius: 25px; |
||||
} |
||||
|
||||
.item { |
||||
white-space: nowrap; |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,69 @@
|
||||
<template> |
||||
<input v-model="localState" type="number" v-on="parentListeners"> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'FloatCell', |
||||
props: { |
||||
value: [String, Number] |
||||
}, |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', +val) |
||||
} |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.$el.focus() |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
input { |
||||
width: 100%; |
||||
height: 100%; |
||||
color: var(--v-textColor-base); |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,69 @@
|
||||
<template> |
||||
<input v-model="localState" type="number" v-on="parentListeners"> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'IntegerCell', |
||||
props: { |
||||
value: [String, Number] |
||||
}, |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', parseInt(val, 10)) |
||||
} |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.$el.focus() |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
input { |
||||
width: 100%; |
||||
height: 100%; |
||||
color: var(--v-textColor-base); |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,138 @@
|
||||
<template> |
||||
<v-dialog :is="expand ? 'v-dialog' : 'div'" v-model="expand" max-width="800px" class="cell-container" @keydown.stop.enter> |
||||
<div class="d-flex pa-1 " :class="{backgroundColor:expand}"> |
||||
<v-spacer /> |
||||
<v-icon small class="mr-2" @click="expand = !expand"> |
||||
{{ expand ? 'mdi-arrow-collapse' : 'mdi-arrow-expand' }} |
||||
</v-icon> |
||||
<template v-if="!isForm"> |
||||
<v-btn outlined x-small class="mr-1" @click="$emit('cancel')"> |
||||
<!-- Cancel --> |
||||
{{ $t('general.cancel') }} |
||||
</v-btn> |
||||
<v-btn x-small color="primary" :disabled="!isValid" @click="save"> |
||||
<!-- Save --> |
||||
{{ $t('general.save') }} |
||||
</v-btn> |
||||
</template> |
||||
<v-btn v-else-if="expand" x-small @click="expand=false"> |
||||
<!-- Close --> |
||||
{{ $t('general.close') }} |
||||
</v-btn> |
||||
</div> |
||||
<monaco-json-object-editor |
||||
v-if="expand" |
||||
v-model="localState" |
||||
class="text-left caption" |
||||
style="width: 300px;min-height:min(600px,80vh);min-width:100%; " |
||||
@validate="validate" |
||||
/> |
||||
<monaco-json-object-editor |
||||
v-else |
||||
v-model="localState" |
||||
class="text-left caption" |
||||
style="width: 300px;min-height:200px;min-width:100%;" |
||||
@validate="validate" |
||||
/> |
||||
<div v-show="error" class="px-2 py-1 text-left caption error--text"> |
||||
{{ error }} |
||||
</div> |
||||
</v-dialog> |
||||
</template> |
||||
|
||||
<script> |
||||
import MonacoJsonObjectEditor from '@/components/monaco/MonacoJsonObjectEditor' |
||||
|
||||
export default { |
||||
name: 'JsonEditableCell', |
||||
components: { MonacoJsonObjectEditor }, |
||||
props: { |
||||
value: [String, Object], |
||||
isForm: Boolean |
||||
}, |
||||
data: () => ({ |
||||
localState: '', |
||||
expand: false, |
||||
isValid: true, |
||||
error: undefined |
||||
}), |
||||
computed: { |
||||
|
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
watch: { |
||||
value(val) { |
||||
try { |
||||
this.localState = typeof val === 'string' ? JSON.parse(val) : val |
||||
} catch (e) { |
||||
// ignore parse error for invalid JSON |
||||
} |
||||
}, |
||||
localState(val) { |
||||
if (this.isForm) { |
||||
this.$emit('input', JSON.stringify(val)) |
||||
} |
||||
} |
||||
}, |
||||
created() { |
||||
try { |
||||
this.localState = typeof this.value === 'string' ? JSON.parse(this.value) : this.value |
||||
} catch (e) { |
||||
// ignore parse error for invalid JSON |
||||
} |
||||
}, |
||||
mounted() { |
||||
}, |
||||
methods: { |
||||
save() { |
||||
this.expand = false |
||||
this.$emit('input', JSON.stringify(this.localState)) |
||||
}, |
||||
validate(n, e) { |
||||
this.isValid = n |
||||
this.error = e |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.cell-container { |
||||
width: 100% |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,71 @@
|
||||
<template> |
||||
<div class="d-100 h-100" :class="{'nc-cell-hover-show': localState == 0 || !localState}"> |
||||
<v-rating |
||||
v-model="localState" |
||||
:length="ratingMeta.max" |
||||
dense |
||||
x-small |
||||
:readonly="readOnly" |
||||
clearable |
||||
> |
||||
<template #item="{isFilled, click}"> |
||||
<v-icon v-if="isFilled" :size="15" :color="ratingMeta.color" @click="click"> |
||||
{{ fullIcon }} |
||||
</v-icon> |
||||
<v-icon |
||||
v-else |
||||
:color="ratingMeta.color" |
||||
:size="15" |
||||
class="nc-cell-hover-show" |
||||
@click="click" |
||||
> |
||||
{{ emptyIcon }} |
||||
</v-icon> |
||||
</template> |
||||
</v-rating> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'RatingCell', |
||||
props: { |
||||
column: Object, |
||||
value: [String, Number], |
||||
readOnly: Boolean |
||||
}, |
||||
computed: { |
||||
fullIcon() { |
||||
return (this.ratingMeta && this.ratingMeta.icon && this.ratingMeta.icon.full) || 'mdi-star' |
||||
}, |
||||
emptyIcon() { |
||||
return (this.ratingMeta && this.ratingMeta.icon && this.ratingMeta.icon.empty) || 'mdi-star-outline' |
||||
}, |
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val) |
||||
} |
||||
}, |
||||
ratingMeta() { |
||||
return { |
||||
icon: { |
||||
full: 'mdi-star', |
||||
empty: 'mdi-star-outline' |
||||
}, |
||||
color: '#fcb401', |
||||
max: 5, |
||||
...(this.column && this.column.meta |
||||
? this.column.meta |
||||
: {}) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,101 @@
|
||||
<template> |
||||
<div class="d-flex align-center"> |
||||
<div> |
||||
<div v-for="(val,i) of setValues" :key="val" class=""> |
||||
<input :id="`key-check-box-${val}`" v-model="localState" type="checkbox" class="orange--text" :value="val"> |
||||
<label |
||||
class="py-1 px-3 d-inline-block my-1 label" |
||||
:for="`key-check-box-${val}`" |
||||
:style="{ |
||||
background:colors[i % colors.length ] |
||||
}" |
||||
>{{ val }}</label> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import colors from '@/components/project/spreadsheet/helpers/colors' |
||||
|
||||
export default { |
||||
name: 'SetListCheckboxCell', |
||||
props: { |
||||
value: String, |
||||
column: Object, |
||||
values: Array |
||||
}, |
||||
data() { |
||||
}, |
||||
computed: { |
||||
colors() { |
||||
return this.$store.state.settings.darkTheme ? colors.dark : colors.light |
||||
}, |
||||
localState: { |
||||
get() { |
||||
return (this.value && this.value.split(',')) || [] |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val.join(',')) |
||||
this.$emit('update') |
||||
} |
||||
}, |
||||
setValues() { |
||||
if (this.column && this.column.dtxp) { |
||||
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')) |
||||
} |
||||
return this.values || [] |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.$el.focus() |
||||
const event = document.createEvent('MouseEvents') |
||||
event.initMouseEvent('mousedown', true, true, window) |
||||
this.$el.dispatchEvent(event) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
.label { |
||||
border-radius: 25px; |
||||
} |
||||
|
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,129 @@
|
||||
<template> |
||||
<div> |
||||
<v-combobox |
||||
v-model="localState" |
||||
:items="setValues" |
||||
multiple |
||||
chips |
||||
flat |
||||
dense |
||||
solo |
||||
hide-details |
||||
deletable-chips |
||||
class="text-center mt-0 " |
||||
> |
||||
<template #selection="data"> |
||||
<v-chip |
||||
:key="data.item" |
||||
small |
||||
class="ma-1 " |
||||
:color="colors[setValues.indexOf(data.item) % colors.length]" |
||||
@click:close="data.parent.selectItem(data.item)" |
||||
> |
||||
{{ data.item }} |
||||
</v-chip> |
||||
</template> |
||||
|
||||
<template #item="{item}"> |
||||
<v-chip small :color="colors[setValues.indexOf(item) % colors.length]"> |
||||
{{ item }} |
||||
</v-chip> |
||||
</template> |
||||
<template #append> |
||||
<v-icon small class="mt-2"> |
||||
mdi-menu-down |
||||
</v-icon> |
||||
</template> |
||||
</v-combobox> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import colors from '@/mixins/colors' |
||||
|
||||
export default { |
||||
name: 'SetListEditableCell', |
||||
mixins: [colors], |
||||
props: { |
||||
value: String, |
||||
column: Object |
||||
}, |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
return this.value && this.value |
||||
.match(/(?:[^',]|\\')+(?='?(?:,|$))/g) |
||||
.map(v => v.replace(/\\'/g, '\'')) |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val.filter(v => this.setValues.includes(v)).join(',')) |
||||
} |
||||
}, |
||||
setValues() { |
||||
if (this.column && this.column.dtxp) { |
||||
return this.column.dtxp |
||||
.match(/(?:[^']|\\')+(?='?(?:,|$))/g) |
||||
.map(v => v.replace(/\\'/g, '\'').replace(/^'|'$/g, '')) |
||||
} |
||||
return [] |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
// this.$el.focus(); |
||||
// let event; |
||||
// event = document.createEvent('MouseEvents'); |
||||
// event.initMouseEvent('mousedown', true, true, window); |
||||
// this.$el.dispatchEvent(event); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
select { |
||||
width: 100%; |
||||
height: 100%; |
||||
color: var(--v-textColor-base); |
||||
-webkit-appearance: menulist; |
||||
/*webkit browsers */ |
||||
-moz-appearance: menulist; |
||||
/*Firefox */ |
||||
appearance: menulist; |
||||
} |
||||
|
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,81 @@
|
||||
<template> |
||||
<textarea |
||||
ref="textarea" |
||||
v-model="localState" |
||||
rows="4" |
||||
v-on="parentListeners" |
||||
@keydown.alt.enter.stop |
||||
@keydown.shift.enter.stop |
||||
/> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'TextAreaCell', |
||||
props: { |
||||
value: String |
||||
}, |
||||
computed: { |
||||
|
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val) |
||||
} |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
created() { |
||||
this.localState = this.value |
||||
}, |
||||
mounted() { |
||||
this.$refs.textarea && this.$refs.textarea.focus() |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
input, textarea { |
||||
width: 100%; |
||||
min-height: 60px; |
||||
height: 100%; |
||||
color: var(--v-textColor-base); |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,105 @@
|
||||
<template> |
||||
<div> |
||||
<div v-if="!isForm" class="d-flex ma-1"> |
||||
<v-spacer /> |
||||
<v-btn v-if="!isForm" outlined x-small class="mr-1" @click="$emit('cancel')"> |
||||
<!-- Cancel --> |
||||
{{ $t('general.cancel') }} |
||||
</v-btn> |
||||
<v-btn v-if="!isForm" x-small color="primary" @click="save"> |
||||
<!-- Save --> |
||||
{{ $t('general.save') }} |
||||
</v-btn> |
||||
</div> |
||||
<textarea |
||||
ref="textarea" |
||||
v-model="localState" |
||||
rows="3" |
||||
v-on="parentListeners" |
||||
@input="isForm && save()" |
||||
@keydown.stop.enter |
||||
/> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'TextAreaCell', |
||||
props: { |
||||
value: String, |
||||
isForm: Boolean |
||||
}, |
||||
data: () => ({ |
||||
localState: '' |
||||
}), |
||||
computed: { |
||||
|
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
watch: { |
||||
value(val) { |
||||
this.localState = val |
||||
}, |
||||
localState(val) { |
||||
if (this.isForm) { |
||||
this.$emit('input', val) |
||||
} |
||||
} |
||||
}, |
||||
created() { |
||||
this.localState = this.value |
||||
}, |
||||
mounted() { |
||||
this.$refs.textarea && this.$refs.textarea.focus() |
||||
}, |
||||
methods: { |
||||
save() { |
||||
this.$emit('input', this.localState) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
input, textarea { |
||||
width: 100%; |
||||
min-height:60px; |
||||
height: calc(100% - 28px); |
||||
color: var(--v-textColor-base); |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,73 @@
|
||||
<template> |
||||
<input v-model="localState" v-on="parentListeners"> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'TextCell', |
||||
props: { |
||||
value: [String, Object, Number, Boolean, Array] |
||||
}, |
||||
computed: { |
||||
localState: { |
||||
get() { |
||||
return this.value |
||||
}, |
||||
set(val) { |
||||
this.$emit('input', val) |
||||
} |
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
if (this.$listeners.cancel) { |
||||
$listeners.cancel = this.$listeners.cancel |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.$el.focus() |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
input, textarea { |
||||
width: 100%; |
||||
height: 100%; |
||||
color: var(--v-textColor-base); |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,111 @@
|
||||
<template> |
||||
<v-menu> |
||||
<template #activator="{on}"> |
||||
<input v-model="localState" class="value" v-on="on"> |
||||
</template> |
||||
<div class="d-flex flex-column justify-center" @click.stop> |
||||
<v-time-picker v-model="localState" v-on="parentListeners" /> |
||||
<v-btn small color="primary" @click="$emit('save')"> |
||||
<!-- Save --> |
||||
{{ $t('general.save') }} |
||||
</v-btn> |
||||
</div> |
||||
</v-menu> |
||||
</template> |
||||
|
||||
<script> |
||||
import dayjs from 'dayjs' |
||||
|
||||
export default { |
||||
name: 'TimePickerCell', |
||||
props: { |
||||
value: [String, Date] |
||||
}, |
||||
computed: { |
||||
isMysql() { |
||||
return ['mysql', 'mysql2'].indexOf(this.$store.getters['project/GtrClientType']) |
||||
}, |
||||
localState: { |
||||
get() { |
||||
if (!this.value) { |
||||
return this.value |
||||
} |
||||
let dateTime = dayjs(this.value) |
||||
if (!dateTime.isValid()) { |
||||
dateTime = dayjs(this.value, 'HH:mm:ss') |
||||
} |
||||
if (!dateTime.isValid()) { |
||||
dateTime = dayjs(`1999-01-01 ${this.value}`) |
||||
} |
||||
if (!dateTime.isValid()) { |
||||
return this.value |
||||
} |
||||
return dateTime.format('HH:mm:ss') |
||||
}, |
||||
set(val) { |
||||
const dateTime = dayjs(`1999-01-01 ${val}:00`) |
||||
if (dateTime.isValid()) { |
||||
if (this.isMysql) { |
||||
this.$emit('input', dateTime.format('YYYY-MM-DD HH:mm:ss')) |
||||
} else { |
||||
this.$emit('input', dateTime.format('YYYY-MM-DD HH:mm:ssZ')) |
||||
} |
||||
} |
||||
} |
||||
|
||||
}, |
||||
parentListeners() { |
||||
const $listeners = {} |
||||
|
||||
if (this.$listeners.blur) { |
||||
$listeners.blur = this.$listeners.blur |
||||
} |
||||
if (this.$listeners.focus) { |
||||
$listeners.focus = this.$listeners.focus |
||||
} |
||||
|
||||
if (this.$listeners.cancel) { |
||||
$listeners.cancel = this.$listeners.cancel |
||||
} |
||||
|
||||
return $listeners |
||||
} |
||||
}, |
||||
mounted() { |
||||
if (this.$el && this.$el.$el) { |
||||
this.$el.$el.focus() |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.value { |
||||
width: 100%; |
||||
min-height: 20px; |
||||
} |
||||
</style> |
||||
<!-- |
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Naveen MR <oof1lab@gmail.com> |
||||
* @author Pranav C Balan <pranavxc@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
--> |
@ -0,0 +1,48 @@
|
||||
<script setup lang="ts"> |
||||
import type { ColumnType } from 'nocodb-sdk' |
||||
import { useColumn } from '~/composables/column' |
||||
|
||||
const { column, value } = defineProps<{ column: ColumnType; value: any }>() |
||||
provide('column', column) |
||||
provide('value', value) |
||||
|
||||
const { |
||||
isSet, |
||||
isEnum, |
||||
isURL, |
||||
isEmail, |
||||
isJSON, |
||||
isDate, |
||||
isDateTime, |
||||
isTime, |
||||
isBoolean, |
||||
isDuration, |
||||
isRating, |
||||
isCurrency, |
||||
isAttachment, |
||||
isTextArea, |
||||
} = useColumn(column) |
||||
</script> |
||||
|
||||
<template> |
||||
<!-- <CellEditableAttachment v-if="isAttachment" /> --> |
||||
<CellSetList v-if="isSet" /> |
||||
<CellEnum v-else-if="isEnum" /> |
||||
<!-- <CellUrl v-else-if="isURL" /> --> |
||||
<!-- <CellEmail v-else-if="isEmail" /> --> |
||||
<!-- <CellJson v-else-if="isJSON" /> --> |
||||
<!-- <CellDate v-else-if="isDate" /> --> |
||||
<!-- <CellDateTime v-else-if="isDateTime" /> --> |
||||
<!-- <CellTime v-else-if="isTime" /> --> |
||||
<CellBoolean v-else-if="isBoolean" /> |
||||
<!-- <CellDuration v-else-if="isDuration" /> --> |
||||
<!-- <CellRating v-else-if="isRating" /> --> |
||||
<!-- <CellCurrency v-else-if="isCurrency" /> --> |
||||
<span |
||||
v-else |
||||
:title="title" |
||||
>{{ value }}</span> |
||||
</template> |
||||
|
||||
<style scoped> |
||||
</style> |
@ -0,0 +1,189 @@
|
||||
<script setup lang="ts"> |
||||
import type { ColumnType } from 'nocodb-sdk' |
||||
import { useColumn } from '~/composables/column' |
||||
|
||||
const { column, value } = defineProps<{ column: ColumnType; value: any }>() |
||||
provide('column', column) |
||||
provide('value', value) |
||||
|
||||
const { |
||||
isSet, |
||||
isEnum, |
||||
isURL, |
||||
isEmail, |
||||
isJSON, |
||||
isDate, |
||||
isDateTime, |
||||
isTime, |
||||
isBoolean, |
||||
isDuration, |
||||
isRating, |
||||
isCurrency, |
||||
isAttachment, |
||||
isTextArea, |
||||
} = useColumn(column) |
||||
</script> |
||||
|
||||
<template> |
||||
<div |
||||
class="nc-cell" |
||||
@keydown.stop.left |
||||
@keydown.stop.right |
||||
@keydown.stop.up |
||||
@keydown.stop.down |
||||
> |
||||
<EditableAttachmentCell |
||||
v-if="isAttachment" |
||||
v-model="localState" |
||||
:active="active" |
||||
:db-alias="dbAlias" |
||||
:meta="meta" |
||||
:is-form="isForm" |
||||
:column="column" |
||||
:is-public-grid="isPublic && !isForm" |
||||
:is-public-form="isPublic && isForm" |
||||
:view-id="viewId" |
||||
:is-locked="isLocked" |
||||
v-on="$listeners" |
||||
/> |
||||
|
||||
<RatingCell |
||||
v-else-if="isRating" |
||||
v-model="localState" |
||||
:active="active" |
||||
:is-form="isForm" |
||||
:column="column" |
||||
:is-public-grid="isPublic && !isForm" |
||||
:is-public-form="isPublic && isForm" |
||||
:is-locked="isLocked" |
||||
v-on="$listeners" |
||||
/> |
||||
|
||||
<DurationCell |
||||
v-else-if="isDuration" |
||||
v-model="localState" |
||||
:active="active" |
||||
:is-form="isForm" |
||||
:column="column" |
||||
:is-locked="isLocked" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<BooleanCell |
||||
v-else-if="isBoolean" |
||||
v-model="localState" |
||||
:column="column" |
||||
:is-form="isForm" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<IntegerCell |
||||
v-else-if="isInt" |
||||
v-model="localState" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<FloatCell |
||||
v-else-if="isFloat" |
||||
v-model="localState" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<DatePickerCell |
||||
v-else-if="isDate" |
||||
v-model="localState" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<TimePickerCell |
||||
v-else-if="isTime" |
||||
v-model="localState" |
||||
v-on="parentListeners" |
||||
@save="$emit('save')" |
||||
/> |
||||
|
||||
<DateTimePickerCell |
||||
v-else-if="isDateTime" |
||||
v-model="localState" |
||||
ignore-focus |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<EnumCell |
||||
v-else-if="isEnum && ((!isForm && !active) || isLocked || (isPublic && !isForm))" |
||||
v-model="localState" |
||||
:column="column" |
||||
v-on="parentListeners" |
||||
/> |
||||
<EnumListCell |
||||
v-else-if="isEnum" |
||||
v-model="localState" |
||||
:is-form="isForm" |
||||
:column="column" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<JsonEditableCell |
||||
v-else-if="isJSON" |
||||
v-model="localState" |
||||
:is-form="isForm" |
||||
v-on="parentListeners" |
||||
@input="$emit('save')" |
||||
/> |
||||
|
||||
<SetListEditableCell |
||||
v-else-if="isSet && (active || isForm) && !isLocked && !(isPublic && !isForm)" |
||||
v-model="localState" |
||||
:column="column" |
||||
v-on="parentListeners" |
||||
/> |
||||
<SetListCell |
||||
v-else-if="isSet" |
||||
v-model="localState" |
||||
:column="column" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<EditableUrlCell v-else-if="isURL" v-model="localState" v-on="parentListeners" /> |
||||
|
||||
<TextCell v-else-if="isString" v-model="localState" v-on="parentListeners" /> |
||||
|
||||
<TextAreaCell |
||||
v-else-if="isTextArea" |
||||
v-model="localState" |
||||
:is-form="isForm" |
||||
v-on="parentListeners" |
||||
/> |
||||
|
||||
<TextCell v-else v-model="localState" v-on="$listeners" /> |
||||
<span v-if="hint" class="nc-hint">{{ hint }}</span> |
||||
|
||||
<div v-if="(isLocked || (isPublic && !isForm)) && !isAttachment" class="nc-locked-overlay" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<style scoped> |
||||
div { |
||||
width: 100%; |
||||
height: 100%; |
||||
color: var(--v-textColor-base); |
||||
} |
||||
|
||||
.nc-hint { |
||||
font-size: .61rem; |
||||
color: grey; |
||||
} |
||||
|
||||
.nc-cell { |
||||
position: relative; |
||||
} |
||||
|
||||
.nc-locked-overlay { |
||||
position: absolute; |
||||
z-index: 2; |
||||
height: 100%; |
||||
width: 100%; |
||||
top: 0; |
||||
left: 0; |
||||
} |
||||
</style> |
@ -0,0 +1,58 @@
|
||||
export default { |
||||
light: [ |
||||
|
||||
'#ffdce5', |
||||
'#fee2d5', |
||||
'#ffeab6', |
||||
'#d1f7c4', |
||||
'#ede2fe', |
||||
'#eee', |
||||
'#cfdffe', |
||||
'#d0f1fd', |
||||
'#c2f5e8', |
||||
'#ffdaf6', |
||||
], |
||||
dark: [ |
||||
'#f82b6099', |
||||
'#ff6f2c99', |
||||
'#fcb40099', |
||||
'#20c93399', |
||||
'#8b46ff99', |
||||
'#666', |
||||
'#2d7ff999', |
||||
'#18bfff99', |
||||
'#20d9d299', |
||||
'#ff08c299', |
||||
], |
||||
} |
||||
|
||||
const enumColor = { |
||||
light: [ |
||||
'#cfdffe', |
||||
'#d0f1fd', |
||||
'#c2f5e8', |
||||
'#ffdaf6', |
||||
'#ffdce5', |
||||
'#fee2d5', |
||||
'#ffeab6', |
||||
'#d1f7c4', |
||||
'#ede2fe', |
||||
'#eeeeee', |
||||
], |
||||
dark: [ |
||||
'#2d7ff999', |
||||
'#18bfff99', |
||||
'#20d9d299', |
||||
'#ff08c299', |
||||
'#f82b6099', |
||||
'#ff6f2c99', |
||||
'#fcb40099', |
||||
'#20c93399', |
||||
'#8b46ff99', |
||||
'#666', |
||||
], |
||||
} |
||||
|
||||
export { |
||||
enumColor, |
||||
} |
@ -0,0 +1,72 @@
|
||||
import { SqlUiFactory, UITypes, isVirtualCol } from 'nocodb-sdk' |
||||
import { useProject } from '~/composables/project' |
||||
|
||||
export const useColumn = (column) => { |
||||
const { project } = useProject() |
||||
|
||||
const uiDatatype = column && column.uidt |
||||
const abstractType = isVirtualCol(column) ? null : SqlUiFactory.create(project.value?.bases?.[0]?.config || { client: 'mysql2' }).getAbstractType(column) |
||||
|
||||
const dataTypeLow = column && column.dt && column.dt.toLowerCase() |
||||
const isBoolean = abstractType === 'boolean' |
||||
const isString = abstractType === 'string' |
||||
const isTextArea = uiDatatype === UITypes.LongText |
||||
const isInt = abstractType === 'integer' |
||||
const isFloat = abstractType === 'float' |
||||
const isDate = abstractType === 'date' || uiDatatype === 'Date' |
||||
const isTime = abstractType === 'time' || uiDatatype === 'Time' |
||||
const isDateTime = abstractType === 'datetime' || uiDatatype === 'DateTime' |
||||
const isJSON = uiDatatype === 'JSON' |
||||
const isEnum = uiDatatype === 'SingleSelect' |
||||
const isSet = uiDatatype === 'MultiSelect' |
||||
const isURL = uiDatatype === 'URL' |
||||
const isEmail = uiDatatype === UITypes.Email |
||||
const isAttachment = uiDatatype === 'Attachment' |
||||
const isRating = uiDatatype === UITypes.Rating |
||||
const isCurrency = uiDatatype === 'Currency' |
||||
const isDuration = uiDatatype === UITypes.Duration |
||||
const isAutoSaved = [ |
||||
UITypes.SingleLineText, |
||||
UITypes.LongText, |
||||
UITypes.PhoneNumber, |
||||
UITypes.Email, |
||||
UITypes.URL, |
||||
UITypes.Number, |
||||
UITypes.Decimal, |
||||
UITypes.Percent, |
||||
UITypes.Count, |
||||
UITypes.AutoNumber, |
||||
UITypes.SpecificDBType, |
||||
UITypes.Geometry, |
||||
].includes(uiDatatype) |
||||
const isManualSaved = [ |
||||
UITypes.Currency, |
||||
UITypes.Year, |
||||
UITypes.Time, |
||||
UITypes.Duration, |
||||
].includes(uiDatatype) |
||||
|
||||
return { |
||||
abstractType, |
||||
dataTypeLow, |
||||
isBoolean, |
||||
isString, |
||||
isTextArea, |
||||
isInt, |
||||
isFloat, |
||||
isDate, |
||||
isTime, |
||||
isDateTime, |
||||
isJSON, |
||||
isEnum, |
||||
isSet, |
||||
isURL, |
||||
isEmail, |
||||
isAttachment, |
||||
isRating, |
||||
isCurrency, |
||||
isDuration, |
||||
isAutoSaved, |
||||
isManualSaved, |
||||
} |
||||
} |
@ -0,0 +1,9 @@
|
||||
<script setup lang="ts"> |
||||
</script> |
||||
|
||||
<template> |
||||
<v-layout> |
||||
<v-app-bar color="" /> |
||||
<slot /> |
||||
</v-layout> |
||||
</template> |
Loading…
Reference in new issue