mirror of https://github.com/nocodb/nocodb
Browse Source
* wip: rating cell ui implementation Signed-off-by: Pranav C <pranavxc@gmail.com> * wip: migration for meta column data field Signed-off-by: Pranav C <pranavxc@gmail.com> * wip: rating column options Signed-off-by: Pranav C <pranavxc@gmail.com> * feat: rating cell creation and mapping metadata Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: change default type to int for rating Signed-off-by: Pranav C <pranavxc@gmail.com> * feat: add email cell Signed-off-by: Pranav C <pranavxc@gmail.com> * feat: accept invalid values for columns if validation disabled Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: remove additional options Signed-off-by: Pranav C <pranavxc@gmail.com> * feat: checkbox customization Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: move validate checkbox to advance option Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: add tick icon in checkbox Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: start rating max value from 1 Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: minor ui corrections Signed-off-by: Pranav C <pranavxc@gmail.com> * fix: load rating component in gallery view Signed-off-by: Pranav C <pranavxc@gmail.com> * fix: use default icon and color if meta missing(checkbox and rating) re #2036 Signed-off-by: Pranav C <pranavxc@gmail.com> * fix: cache column with parsed metadata re #2038 Signed-off-by: Pranav C <pranavxc@gmail.com> * fix: convert old rating column to number type Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: migration filename correction Signed-off-by: Pranav C <pranavxc@gmail.com> * refactor: component name Signed-off-by: Pranav C <pranavxc@gmail.com>pull/2071/head
Pranav C
3 years ago
committed by
GitHub
22 changed files with 728 additions and 237 deletions
@ -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,98 @@
|
||||
<template> |
||||
<div> |
||||
<div class="nc-rating-wrapper ui-type"> |
||||
<v-select |
||||
v-model="colMeta.icon" |
||||
label="Icon" |
||||
:items="icons" |
||||
dense |
||||
outlined |
||||
class="caption" |
||||
:item-value="v=>v" |
||||
:value-comparator="(a,b) => a && b && a.full === b.full && a.empty === b.empty" |
||||
> |
||||
<template #item="{ item }"> |
||||
<v-icon small :color="colMeta.color"> |
||||
{{ item.full }} |
||||
</v-icon> |
||||
<v-icon class="ml-2" small :color="colMeta.color"> |
||||
{{ item.empty }} |
||||
</v-icon> |
||||
</template> |
||||
<template #selection="{ item }"> |
||||
<v-icon small :color="colMeta.color"> |
||||
{{ item.full }} |
||||
</v-icon> |
||||
<v-icon class="ml-2" small :color="colMeta.color"> |
||||
{{ item.empty }} |
||||
</v-icon> |
||||
</template> |
||||
</v-select> |
||||
<v-select |
||||
v-model="colMeta.max" |
||||
label="Max" |
||||
:items="[1,2,3,4,5,6,7,8,9,10]" |
||||
dense |
||||
outlined |
||||
class="caption" |
||||
/> |
||||
</div> |
||||
<v-color-picker |
||||
v-model="colMeta.color" |
||||
class="mx-auto" |
||||
hide-inputs |
||||
/> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
export default { |
||||
name: 'RatingOptions', |
||||
props: ['column', 'meta', 'value'], |
||||
data: () => ({ |
||||
colMeta: { |
||||
icon: { |
||||
full: 'mdi-star', |
||||
empty: 'mdi-star-outline' |
||||
}, |
||||
color: '#fcb401', |
||||
max: 5 |
||||
}, |
||||
icons: [{ |
||||
full: 'mdi-star', |
||||
empty: 'mdi-star-outline' |
||||
}, { |
||||
full: 'mdi-heart', |
||||
empty: 'mdi-heart-outline' |
||||
}, { |
||||
full: 'mdi-moon-full', |
||||
empty: 'mdi-moon-new' |
||||
}, { |
||||
full: 'mdi-thumb-up', |
||||
empty: 'mdi-thumb-up-outline' |
||||
}, { |
||||
full: 'mdi-flag', |
||||
empty: 'mdi-flag-outline' |
||||
}] |
||||
}), |
||||
watch: { |
||||
value() { |
||||
this.colMeta = this.value || {} |
||||
}, |
||||
colMeta(v) { |
||||
this.$emit('input', v) |
||||
} |
||||
}, |
||||
created() { |
||||
this.colMeta = this.value || { ...this.colMeta } |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.nc-rating-wrapper { |
||||
display: flex; |
||||
gap: 16px |
||||
} |
||||
</style> |
@ -0,0 +1,95 @@
|
||||
<template> |
||||
<div> |
||||
<div class="nc-rating-wrapper ui-type"> |
||||
<v-select |
||||
v-model="colMeta.icon" |
||||
label="Icon" |
||||
:items="icons" |
||||
dense |
||||
outlined |
||||
class="caption" |
||||
:item-value="v=>v" |
||||
:value-comparator="(a,b) => a && b && a.checked === b.checked && a.unchecked === b.unchecked" |
||||
> |
||||
<template #item="{ item }"> |
||||
<v-icon small :color="colMeta.color"> |
||||
{{ item.checked }} |
||||
</v-icon> |
||||
<v-icon class="ml-2" small :color="colMeta.color"> |
||||
{{ item.unchecked }} |
||||
</v-icon> |
||||
</template> |
||||
<template #selection="{ item }"> |
||||
<v-icon small :color="colMeta.color"> |
||||
{{ item.checked }} |
||||
</v-icon> |
||||
<v-icon class="ml-2" small :color="colMeta.color"> |
||||
{{ item.unchecked }} |
||||
</v-icon> |
||||
</template> |
||||
</v-select> |
||||
</div> |
||||
<v-color-picker |
||||
v-model="colMeta.color" |
||||
class="mx-auto" |
||||
hide-inputs |
||||
/> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
|
||||
export default { |
||||
name: 'CheckboxOptions', |
||||
props: ['column', 'meta', 'value'], |
||||
data: () => ({ |
||||
colMeta: { |
||||
icon: { |
||||
checked: 'mdi-check-bold', |
||||
unchecked: 'mdi-crop-square' |
||||
}, |
||||
color: '#777' |
||||
}, |
||||
icons: [{ |
||||
checked: 'mdi-check-bold', |
||||
unchecked: 'mdi-crop-square' |
||||
}, { |
||||
checked: 'mdi-check-circle-outline', |
||||
unchecked: 'mdi-checkbox-blank-circle-outline' |
||||
}, { |
||||
checked: 'mdi-star', |
||||
unchecked: 'mdi-star-outline' |
||||
}, { |
||||
checked: 'mdi-heart', |
||||
unchecked: 'mdi-heart-outline' |
||||
}, { |
||||
checked: 'mdi-moon-full', |
||||
unchecked: 'mdi-moon-new' |
||||
}, { |
||||
checked: 'mdi-thumb-up', |
||||
unchecked: 'mdi-thumb-up-outline' |
||||
}, { |
||||
checked: 'mdi-flag', |
||||
unchecked: 'mdi-flag-outline' |
||||
}] |
||||
}), |
||||
watch: { |
||||
value() { |
||||
this.colMeta = this.value || {} |
||||
}, |
||||
colMeta(v) { |
||||
this.$emit('input', v) |
||||
} |
||||
}, |
||||
created() { |
||||
this.colMeta = this.value || { ...this.colMeta } |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.nc-rating-wrapper { |
||||
display: flex; |
||||
gap: 16px |
||||
} |
||||
</style> |
@ -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 |
||||
:disabled="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,38 @@
|
||||
import Knex from 'knex'; |
||||
import { MetaTable } from '../../utils/globals'; |
||||
|
||||
const up = async (knex: Knex) => { |
||||
await knex.schema.alterTable(MetaTable.COLUMNS, table => { |
||||
table.text('meta'); |
||||
}); |
||||
}; |
||||
|
||||
const down = async knex => { |
||||
await knex.schema.alterTable(MetaTable.COLUMNS, table => { |
||||
table.dropColumn('meta'); |
||||
}); |
||||
}; |
||||
|
||||
export { up, down }; |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2022, 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/>. |
||||
* |
||||
*/ |
Loading…
Reference in new issue