Browse Source

chore(gui-v2): type and code-style fixes

Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
pull/2716/head
Braks 2 years ago committed by Pranav C
parent
commit
917309a619
  1. 16
      packages/nc-gui-v2/components/cell/Attachment.vue
  2. 21
      packages/nc-gui-v2/components/cell/Checkbox.vue
  3. 35
      packages/nc-gui-v2/components/cell/DatePicker.vue
  4. 36
      packages/nc-gui-v2/components/cell/DateTimePicker.vue
  5. 28
      packages/nc-gui-v2/components/cell/Email.vue
  6. 18
      packages/nc-gui-v2/components/cell/Float.vue
  7. 16
      packages/nc-gui-v2/components/cell/Integer.vue
  8. 30
      packages/nc-gui-v2/components/cell/MultiSelect.vue
  9. 65
      packages/nc-gui-v2/components/cell/Rating.vue
  10. 29
      packages/nc-gui-v2/components/cell/SingleSelect.vue
  11. 23
      packages/nc-gui-v2/components/cell/Text.vue
  12. 31
      packages/nc-gui-v2/components/cell/TextArea.vue
  13. 4
      packages/nc-gui-v2/components/cell/Url.vue
  14. 20
      packages/nc-gui-v2/components/smartsheet/Cell.vue
  15. 2
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  16. 25
      packages/nc-gui-v2/components/smartsheet/Pagination.vue
  17. 9
      packages/nc-gui-v2/components/smartsheet/VirtualCell.vue
  18. 10
      packages/nc-gui-v2/components/tabs/Smartsheet.vue
  19. 3
      packages/nc-gui-v2/components/virtual-cell/Formula.vue
  20. 1
      packages/nc-gui-v2/components/virtual-cell/HasMany.vue
  21. 25
      packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue

16
packages/nc-gui-v2/components/cell/Attachment.vue

@ -1,24 +1,26 @@
<script setup lang="ts">
import { inject, watchEffect } from '@vue/runtime-core'
import type { ColumnType, TableType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { useToast } from 'vue-toastification'
import { inject, ref, watchEffect } from '#imports'
import { useNuxtApp } from '#app'
import { ColumnInj, MetaInj } from '~/components'
import useProject from '~/composables/useProject'
// import FileSaver from "file-saver";
import { isImage } from '~/utils/fileUtils'
import MaterialPlusIcon from '~icons/mdi/plus'
import MaterialArrowExpandIcon from '~icons/mdi/arrow-expand'
const { modelValue } = defineProps<{ modelValue: string | Array<any> | null }>()
interface Props {
modelValue: string | any[] | null
}
const { modelValue } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const isPublicForm = inject<boolean>('isPublicForm', false)
const isForm = inject<boolean>('isForm', false)
const meta = inject(MetaInj)
const column = inject<ColumnType>(ColumnInj)
const editEnabled = inject<boolean>('editEnabled')
const column = inject(ColumnInj)
const editEnabled = inject<boolean>('editEnabled', false)
const localFilesState = reactive([])
const attachments = ref([])

21
packages/nc-gui-v2/components/cell/Checkbox.vue

@ -1,8 +1,12 @@
<script setup lang="ts">
import { computed, inject } from 'vue'
import { computed, inject } from '#imports'
import { ColumnInj } from '~/components'
const { modelValue: value } = defineProps<{ modelValue: any }>()
interface Props {
modelValue: boolean
}
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const column = inject(ColumnInj)
const isForm = inject<boolean>('isForm')
@ -17,19 +21,12 @@ const checkboxMeta = computed(() => {
...(column?.meta || {}),
}
})
const localState = computed({
get() {
return value
},
set(val) {
emit('update:modelValue', val)
},
get: () => value,
set: (val) => emit('update:modelValue', val),
})
const toggle = () => {
localState.value = !localState.value
}
// const checkedIcon = computed(() => {
// return defineAsyncComponent( ()=>import('~icons/material-symbols/'+checkboxMeta?.value?.icon?.checked))
// });

35
packages/nc-gui-v2/components/cell/DatePicker.vue

@ -1,8 +1,13 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { computed } from '@vue/reactivity'
import { computed } from '#imports'
interface Props {
modelValue: string
}
const { modelValue } = defineProps<Props>()
const { modelValue } = defineProps<{ modelValue: any }>()
const emit = defineEmits(['update:modelValue'])
const localState = computed({
@ -13,7 +18,7 @@ const localState = computed({
return (/^\d+$/.test(modelValue) ? dayjs(+modelValue) : dayjs(modelValue)).format('YYYY-MM-DD')
},
set(val) {
set(val?: string) {
if (dayjs(val).isValid()) {
emit('update:modelValue', val && dayjs(val).format('YYYY-MM-DD'))
}
@ -84,27 +89,3 @@ export default {
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/>.
*
*/
-->

36
packages/nc-gui-v2/components/cell/DateTimePicker.vue

@ -1,10 +1,16 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { computed } from '@vue/reactivity'
import { computed, ref } from '#imports'
import useProject from '~/composables/useProject'
const { modelValue } = defineProps<{ modelValue: any }>()
interface Props {
modelValue: string
}
const { modelValue } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const { isMysql } = useProject()
const showMessage = ref(false)
@ -21,7 +27,7 @@ const localState = computed({
showMessage.value = true
}
},
set(value) {
set(value?: string) {
if (isMysql) {
emit('update:modelValue', value && dayjs(value).format('YYYY-MM-DD HH:mm:ss'))
} else {
@ -139,27 +145,3 @@ export default {
/* 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/>.
*
*/
-->

28
packages/nc-gui-v2/components/cell/Email.vue

@ -1,20 +1,24 @@
<script>
import { isEmail } from '~/helpers'
<script lang="ts" setup>
import { computed } from '#imports'
import { isEmail } from '~/utils/validation'
interface Props {
modelValue: string
}
const { modelValue } = defineProps<Props>()
const validEmail = computed(() => isEmail(modelValue))
</script>
<script lang="ts">
export default {
name: 'EmailCell',
props: ['value'],
computed: {
isEmail() {
return isEmail(this.value || '')
},
},
}
</script>
<template>
<a v-if="isEmail" :href="`mailto:${value}`" target="_blank">{{ value }}</a>
<span v-else>{{ value }}</span>
<a v-if="validEmail" :href="`mailto:${modelValue}`" target="_blank">{{ modelValue }}</a>
<span v-else>{{ modelValue }}</span>
</template>
<style scoped></style>

18
packages/nc-gui-v2/components/cell/Float.vue

@ -1,21 +1,21 @@
<script lang="ts" setup>
import { computed } from '@vue/reactivity'
import { inject, onMounted } from 'vue'
import { computed, inject, onMounted, ref } from '#imports'
const { modelValue: value } = defineProps<{ modelValue: any }>()
interface Props {
modelValue: number
}
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const editEnabled = inject<boolean>('editEnabled')
const root = ref<HTMLInputElement>()
const localState = computed({
get() {
return value
},
set(val) {
emit('update:modelValue', val)
},
get: () => value,
set: (val) => emit('update:modelValue', val),
})
onMounted(() => {

16
packages/nc-gui-v2/components/cell/Integer.vue

@ -1,21 +1,19 @@
<script setup lang="ts">
import { computed } from '@vue/reactivity'
import { inject, onMounted } from 'vue'
interface Props {
modelValue: number
}
const { modelValue: value } = defineProps<{ modelValue: any }>()
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const editEnabled = inject<boolean>('editEnabled')
const root = ref<HTMLInputElement>()
const localState = computed({
get() {
return value
},
set(val) {
emit('update:modelValue', val)
},
get: () => value,
set: (val) => emit('update:modelValue', val),
})
onMounted(() => {

30
packages/nc-gui-v2/components/cell/MultiSelect.vue

@ -1,25 +1,27 @@
<script lang="ts" setup>
import { computed } from '@vue/reactivity'
import type { ColumnType } from 'nocodb-sdk'
import { Ref, inject } from 'vue'
import { enumColor } from '~/utils/colorsUtils'
import { computed, inject } from '#imports'
import { ColumnInj } from '~/components'
interface Props {
modelValue: string
}
const { modelValue } = defineProps<Props>()
const { modelValue } = defineProps<{ modelValue: any }>()
const emit = defineEmits(['update:modelValue'])
const column = inject<ColumnType>('column')
const isForm = inject<boolean>('isForm')
const editEnabled = inject<boolean>('editEnabled')
const options = computed<string[]>(() => {
return column?.dtxp?.split(',').map((v) => v.replace(/\\'/g, "'").replace(/^'|'$/g, '')) || []
})
const column = inject(ColumnInj)
const isForm = inject<boolean>('isForm', false)
const editEnabled = inject<boolean>('editEnabled', false)
const options = computed(() => column?.dtxp?.split(',').map((v) => v.replace(/\\'/g, "'").replace(/^'|'$/g, '')) || [])
const localState = computed({
get() {
return modelValue?.match(/(?:[^',]|\\')+(?='?(?:,|$))/g).map((v: string) => v.replace(/\\'/g, "'"))
return modelValue?.match(/(?:[^',]|\\')+(?='?(?:,|$))/g)?.map((v: string) => v.replace(/\\'/g, "'"))
},
set(val) {
emit('update:modelValue', val.filter((v: string) => options.value.includes(v)).join(','))
set(val?: string[]) {
emit('update:modelValue', val?.filter((v) => options.value.includes(v)).join(','))
},
})

65
packages/nc-gui-v2/components/cell/Rating.vue

@ -1,11 +1,15 @@
<script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk'
import { computed, inject } from 'vue'
import { computed, inject } from '#imports'
import { ColumnInj } from '~/components'
import MdiStarIcon from '~icons/mdi/star'
import MdiStarOutlineIcon from '~icons/mdi/star-outline'
const { modelValue: value } = defineProps<{ modelValue: any }>()
interface Props {
modelValue: string | number
readOnly: boolean
}
const { modelValue: value, readOnly } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const column = inject(ColumnInj)
const isForm = inject<boolean>('isForm')
@ -22,68 +26,19 @@ const ratingMeta = computed(() => {
}
})
const localState = computed({
get() {
return value
},
set(val) {
emit('update:modelValue', val)
},
get: () => value,
set: (val) => emit('update:modelValue', val),
})
const toggle = () => {
localState.value = !localState.value
}
/* import { inject } from "vue";
const editEnabled = inject<boolean>("editEnabled");
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>
<template>
<div class="d-100 h-100" :class="{ 'nc-cell-hover-show': localState == 0 || !localState }">
<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">
<MdiStarIcon />
<!-- {{ fullIcon }} -->
</v-icon>
<v-icon v-else :color="ratingMeta.color" :size="15" class="nc-cell-hover-show" @click="click">
<!-- {{ emptyIcon }} -->
<MdiStarOutlineIcon />
</v-icon>
</template>

29
packages/nc-gui-v2/components/cell/SingleSelect.vue

@ -1,28 +1,25 @@
<script lang="ts" setup>
import { computed } from '@vue/reactivity'
import type { ColumnType } from 'nocodb-sdk'
import { Ref, inject } from 'vue'
import { computed, inject } from '#imports'
import { ColumnInj } from '~/components'
const { modelValue } = defineProps<{ modelValue: any }>()
interface Props {
modelValue: string
}
const { modelValue } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
// import {enumColor}from "~/utils/colorsUtils";
const column = inject(ColumnInj)
const isForm = inject<boolean>('isForm')
const editEnabled = inject<boolean>('editEnabled')
const isForm = inject<boolean>('isForm', false)
const editEnabled = inject<boolean>('editEnabled', false)
const localState = computed({
get() {
return modelValue?.replace(/\\'/g, "'").replace(/^'|'$/g, '')
},
set(val) {
emit('update:modelValue', val)
},
get: () => modelValue?.replace(/\\'/g, "'").replace(/^'|'$/g, ''),
set: (val) => emit('update:modelValue', val),
})
const options = computed<string[]>(() => {
return column?.dtxp?.split(',').map((v) => v.replace(/\\'/g, "'").replace(/^'|'$/g, '')) || []
})
const options = computed(() => column?.dtxp?.split(',').map((v) => v.replace(/\\'/g, "'").replace(/^'|'$/g, '')) || [])
/* import colors from '@/mixins/colors'

23
packages/nc-gui-v2/components/cell/Text.vue

@ -1,25 +1,24 @@
<script setup lang="ts">
import { computed } from '@vue/reactivity'
import { watchEffect } from '@vue/runtime-core'
import { inject, onMounted } from 'vue'
import { computed, inject, onMounted, ref } from '#imports'
const { modelValue: value } = defineProps<{ modelValue: any }>()
interface Props {
modelValue: any
}
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const editEnabled = inject<boolean>('editEnabled')
const editEnabled = inject<boolean>('editEnabled', false)
const root = ref<HTMLInputElement>()
const localState = computed({
get() {
return value
},
set(val) {
emit('update:modelValue', val)
},
get: () => value,
set: (val) => emit('update:modelValue', val),
})
watchEffect(() => {
onMounted(() => {
root.value?.focus()
})

31
packages/nc-gui-v2/components/cell/TextArea.vue

@ -1,26 +1,27 @@
<script setup lang="ts">
import { computed } from '@vue/reactivity'
import { inject, onMounted } from 'vue'
import { computed, inject, onMounted, ref } from '#imports'
const { modelValue: value } = defineProps<{ modelValue: any }>()
interface Props {
modelValue: string
}
const { modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const editEnabled = inject<boolean>('editEnabled')
const editEnabled = inject<boolean>('editEnabled', false)
const root = ref<HTMLInputElement>()
const localState = computed({
get() {
return value
},
set(val) {
emit('update:modelValue', val)
},
get: () => value,
set: (val) => emit('update:modelValue', val),
})
onMounted(() => {
root.value?.focus()
})
/* export default {
name: 'TextAreaCell',
props: {
@ -58,15 +59,7 @@ onMounted(() => {
</script>
<template>
<textarea
v-if="editEnabled"
ref="root"
v-model="localState"
rows="4"
v-on="parentListeners"
@keydown.alt.enter.stop
@keydown.shift.enter.stop
/>
<textarea v-if="editEnabled" ref="root" v-model="localState" rows="4" @keydown.alt.enter.stop @keydown.shift.enter.stop />
<span v-else>{{ localState }}</span>
</template>

4
packages/nc-gui-v2/components/cell/Url.vue

@ -1,5 +1,5 @@
<script>
const editEnabled = inject < boolean > 'editEnabled'
<script lang="ts">
const editEnabled = inject<boolean>('editEnabled')
/*
import { isValidURL } from '@/helpers'

20
packages/nc-gui-v2/components/smartsheet/Cell.vue

@ -1,28 +1,30 @@
<script setup lang="ts">
import { computed } from '@vue/reactivity'
import { watchEffect } from '@vue/runtime-core'
import type { ColumnType } from 'nocodb-sdk'
import { provide } from 'vue'
import { computed } from '#imports'
import { ColumnInj } from '~/components'
import useColumn from '~/composables/useColumn'
const { column, modelValue: value, editEnabled } = defineProps<{ column: ColumnType; modelValue: any; editEnabled: boolean }>()
interface Props {
column: ColumnType
modelValue: any
editEnabled: boolean
}
const { column, modelValue: value, editEnabled } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
provide(ColumnInj, column)
provide(
'editEnabled',
computed(() => editEnabled),
)
const localState = computed({
get() {
return value
},
set(val) {
emit('update:modelValue', val)
},
get: () => value,
set: (val) => emit('update:modelValue', val),
})
const {

2
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { inject, onMounted } from 'vue'
import { isVirtualCol } from 'nocodb-sdk'
import { inject, onKeyStroke, onMounted, provide } from '#imports'
import { ChangePageInj, IsFormInj, IsGridInj, MetaInj, PaginationDataInj } from '~/components'
import useViewData from '~/composables/useViewData'

25
packages/nc-gui-v2/components/smartsheet/Pagination.vue

@ -4,20 +4,16 @@ import { ChangePageInj, PaginationDataInj } from '~/components'
import MdiKeyboardIcon from '~icons/mdi/keyboard-return'
const paginatedData = inject(PaginationDataInj)
const changePage = inject(ChangePageInj)
const count = computed<number>(() => {
return paginatedData?.value?.totalRows ?? Infinity
})
const size = computed<number>(() => {
return paginatedData?.value?.pageSize ?? 25
})
const page = computed<number>({
get() {
return paginatedData?.value?.page ?? 1
},
set(p) {
changePage?.(p)
},
const count = computed(() => paginatedData?.value?.totalRows ?? Infinity)
const size = computed(() => paginatedData?.value?.pageSize ?? 25)
const page = computed({
get: () => paginatedData?.value?.page ?? 1,
set: (p) => changePage?.(p),
})
/*
export default {
@ -75,7 +71,6 @@ export default {
>
<template #append>
<MdiKeyboardIcon small icon.class="mt-1" @click="changePage(page)" />
<!-- <x-icon tooltip="Change page" small icon.class="mt-1" @click="changePage(page)"> mdi-keyboard-return </x-icon> -->
</template>
</v-text-field>
</div>
@ -84,5 +79,3 @@ export default {
<v-spacer />
</div>
</template>
<style scoped></style>

9
packages/nc-gui-v2/components/smartsheet/VirtualCell.vue

@ -1,9 +1,16 @@
<script setup lang="ts">
import type { ColumnType } from 'nocodb-sdk'
import { provide } from '#imports'
import { ColumnInj } from '~/components'
import useVirtualCell from '~/composables/useVirtualCell'
const { column, modelValue: value } = defineProps<{ column: ColumnType; modelValue: any; editEnabled: boolean }>()
interface Props {
column: ColumnType
modelValue: any
editEnabled: boolean
}
const { column, modelValue: value } = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])

10
packages/nc-gui-v2/components/tabs/Smartsheet.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, onMounted, provide, watch } from 'vue'
import { computed, onMounted, provide, watch } from '#imports'
import { MetaInj, TabMetaInj } from '~/components'
import useMetas from '~/composables/useMetas'
@ -9,9 +9,7 @@ const { tabMeta } = defineProps({
const { getMeta, metas } = useMetas()
const meta = computed(() => {
return metas.value?.[tabMeta?.id]
})
const meta = computed(() => metas.value?.[tabMeta?.id])
onMounted(async () => {
await getMeta(tabMeta?.id)
@ -30,11 +28,9 @@ watch(
<template>
<div class="overflow-auto">
<v-toolbar dense class="nc-table-toolbar elevation-0 xc-toolbar xc-border-bottom mx-1" style="z-index: 7"> </v-toolbar>
<v-toolbar dense class="nc-table-toolbar elevation-0 xc-toolbar xc-border-bottom mx-1 z-7" />
<template v-if="meta && tabMeta">
<SmartsheetGrid />
</template>
</div>
</template>
<style scoped></style>

3
packages/nc-gui-v2/components/virtual-cell/Formula.vue

@ -1,6 +1,5 @@
<script lang="ts" setup>
import type { ColumnType } from 'nocodb-sdk'
import { computed } from 'vue'
import { computed } from '#imports'
import { ColumnInj } from '~/components'
import { handleTZ } from '~/utils/dateTimeUtils'
import { replaceUrlsWithLink } from '~/utils/urlUtils'

1
packages/nc-gui-v2/components/virtual-cell/HasMany.vue

@ -1,5 +1,4 @@
<script setup lang="ts">
import { computed } from '@vue/reactivity'
import type { ColumnType } from 'nocodb-sdk'
import ItemChip from './components/ItemChip.vue'
import { ColumnInj } from '~/components'

25
packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue

@ -1,21 +1,14 @@
<script setup lang="ts">
import MdiCloseThickIcon from '~icons/mdi/close-thick'
/* export default {
name: 'ItemChip',
props: {
value: [String, Number, Boolean],
active: Boolean,
item: Object,
readonly: Boolean,
},
} */
const { value, active, item, readonly } = defineProps({
value: [String, Number, Boolean],
active: Boolean,
item: Object,
readonly: Boolean,
})
interface Props {
modelValue: string | number | boolean
active: boolean
item: any
readonly: boolean
}
const { modelValue, active, item, readonly } = defineProps<Props>()
</script>
<template>
@ -23,7 +16,7 @@ const { value, active, item, readonly } = defineProps({
<!--
:color="isDark ? '' : 'primary lighten-5'"
@click="!readonly && active && $emit('edit', item)" -->
<span class="name" :title="value">{{ value }}</span>
<span class="name" :title="modelValue">{{ modelValue }}</span>
<!-- && _isUIAllowed('xcDatatableEditable') -->
<div v-show="active" v-if="!readonly" class="mr-n1 ml-2">

Loading…
Cancel
Save