1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-17 10:22:36 +00:00

Add style for collection fields

This commit is contained in:
Jérémie Pardou 2024-07-30 08:15:47 +00:00
parent 1f2b7eae5e
commit a5148fa1fa
47 changed files with 564 additions and 211 deletions

View file

@ -258,7 +258,7 @@ class CollectionFieldSerializer(serializers.ModelSerializer):
object.
"""
default_allowed_fields = ["name", "type", "id", "uid"]
default_allowed_fields = ["name", "type", "id", "uid", "styles"]
config = serializers.DictField(
required=False,

View file

@ -698,6 +698,12 @@ class CollectionField(models.Model):
help_text="The configuration of the field.",
)
styles = models.JSONField(
default=dict,
help_text="The theme overrides for this field",
null=True, # TODO zdm remove me after 1.27
)
def get_type(self):
"""Returns the type for this model instance"""

View file

@ -368,6 +368,7 @@ class CollectionFieldType(
"uid": str(instance.uid),
"name": instance.name,
"type": instance.type,
"styles": instance.styles,
"config": serialized_config,
}
@ -440,6 +441,7 @@ class CollectionFieldType(
"uid": serialized_values.get("uid", RandomUUID()),
"config": deserialized_config,
"type": serialized_values["type"],
"styles": serialized_values.get("styles", {}),
"name": serialized_values["name"],
}

View file

@ -0,0 +1,19 @@
# Generated by Django 4.2.13 on 2024-07-19 10:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("builder", "0032_tablethemeconfigblock"),
]
operations = [
migrations.AddField(
model_name="collectionfield",
name="styles",
field=models.JSONField(
default=dict, help_text="The theme overrides for this field", null=True
),
),
]

View file

@ -87,7 +87,13 @@ def test_can_update_a_table_element_fields(api_client, data_fixture):
{key: value for key, value in f.items() if key not in ["id"]}
for f in response.json()["fields"]
] == [
{"name": "Name", "type": "text", "value": "get('test1')", "uid": uuids[0]},
{
"name": "Name",
"type": "text",
"value": "get('test1')",
"uid": uuids[0],
"styles": {},
},
{
"name": "Color",
"type": "link",
@ -95,9 +101,16 @@ def test_can_update_a_table_element_fields(api_client, data_fixture):
"navigate_to_url": "get('test2')",
"link_name": "get('test3')",
"target": "self",
"styles": {},
"uid": uuids[1],
},
{"name": "Question", "type": "text", "value": "get('test3')", "uid": uuids[2]},
{
"name": "Question",
"type": "text",
"value": "get('test3')",
"uid": uuids[2],
"styles": {},
},
]

View file

@ -459,6 +459,7 @@ def test_builder_application_export(data_fixture):
"type": f.type,
"config": f.config,
"uid": str(f.uid),
"styles": {},
}
for f in element4.fields.all()
],
@ -855,18 +856,21 @@ def test_builder_application_export(data_fixture):
"uid": "447cbec7-c422-42eb-bd50-204b53453330",
"name": "Field 1",
"type": "text",
"styles": {},
"config": {"value": "get('test1')"},
},
{
"uid": "44446a1c-841f-47ba-b1df-e902cc50c6ed",
"name": "Field 2",
"type": "text",
"styles": {},
"config": {"value": "get('test2')"},
},
{
"uid": "960aef1f-a894-4003-8cf2-36da3b9c798b",
"name": "Field 3",
"type": "text",
"styles": {},
"config": {"value": "get('test3')"},
},
],
@ -1111,12 +1115,14 @@ IMPORT_REFERENCE = {
{
"name": "F 1",
"type": "text",
"styles": {},
"config": {"value": "get('current_record.field_25')"},
"uid": str(uuid.uuid4()),
},
{
"name": "F 2",
"type": "link",
"styles": {},
"uid": str(uuid.uuid4()),
"config": {
"page_parameters": [],

View file

@ -0,0 +1,7 @@
{
"type": "feature",
"message": "[Builder] Allow to configure table field styles",
"issue_number": 2803,
"bullet_points": [],
"created_at": "2024-07-23"
}

View file

@ -17,6 +17,7 @@ import {
import resolveElementUrl from '@baserow/modules/builder/utils/urlResolution'
import { pathParametersInError } from '@baserow/modules/builder/utils/params'
import { ClickEvent } from '@baserow/modules/builder/eventTypes'
import { ThemeConfigBlockType } from '@baserow/modules/builder/themeConfigBlockTypes'
export class CollectionFieldType extends Registerable {
get name() {
@ -51,6 +52,15 @@ export class CollectionFieldType extends Registerable {
isInError({ field, builder }) {
return false
}
getStyleOverride({ colorVariables, field, theme }) {
return ThemeConfigBlockType.getAllStyles(
this.app.$registry.getOrderedList('themeConfigBlock'),
field.styles?.cell || {},
colorVariables,
theme
)
}
}
export class BooleanCollectionFieldType extends CollectionFieldType {

View file

@ -1,5 +1,5 @@
<template>
<p class="ab-paragraph">
<p class="ab-text">
<slot />
</p>
</template>

View file

@ -0,0 +1,58 @@
<template>
<BaserowTable
:fields="fields"
:rows="rows"
class="ab-table"
:orientation="orientation"
>
<template #field-name="{ field }">
<th :key="field.__id__" class="ab-table__header-cell">
<slot name="field-name" :field="field">{{ field.name }}</slot>
</th>
</template>
<template #cell-content="{ rowIndex, value, field }">
<slot
name="cell-content"
:value="value"
:field="field"
:row-index="rowIndex"
>
<td :key="field.id" class="ab-table__cell">
<div class="ab-table__cell-content">
{{ value }}
</div>
</td>
</slot>
</template>
<template #empty-state>
<div class="ab-table__empty-message">
{{ $t('abTable.empty') }}
</div>
</template>
</BaserowTable>
</template>
<script>
import { TABLE_ORIENTATION } from '@baserow/modules/builder/enums'
import BaserowTable from '@baserow/modules/builder/components/elements/components/BaserowTable'
export default {
name: 'ABTable',
components: { BaserowTable },
props: {
fields: {
type: Array,
required: true,
},
rows: {
type: Array,
required: true,
},
orientation: {
type: String,
required: false,
default: TABLE_ORIENTATION.HORIZONTAL,
},
},
}
</script>

View file

@ -0,0 +1,17 @@
<template>
<span class="ab-tag" :style="{ ['--tag-background-color']: color }">
<slot />
</span>
</template>
<script>
export default {
name: 'ABTag',
props: {
color: {
type: String,
required: true,
},
},
}
</script>

View file

@ -4,13 +4,13 @@
<template v-if="orientation === TABLE_ORIENTATION.HORIZONTAL">
<thead>
<tr class="baserow-table__row">
<th
v-for="field in fields"
:key="field.__id__"
class="baserow-table__header-cell"
>
<slot name="field-name" :field="field">{{ field.name }}</slot>
</th>
<template v-for="field in fields">
<slot name="field-name" :field="field">
<th :key="field.__id__" class="baserow-table__header-cell">
{{ field.name }}
</th>
</slot>
</template>
</tr>
</thead>
<tbody v-if="rows.length">
@ -19,20 +19,18 @@
:key="row.__id__"
class="baserow-table__row"
>
<td
v-for="field in fields"
:key="field.id"
class="baserow-table__cell"
>
<template v-for="field in fields">
<slot
name="cell-content"
:value="row[field.name]"
:field="field"
:row-index="index"
>
{{ row[field.name] }}
<td :key="field.id" class="baserow-table__cell">
{{ row[field.name] }}
</td>
</slot>
</td>
</template>
</tr>
</tbody>
</template>
@ -47,29 +45,27 @@
v-for="(field, fieldIndex) in fields"
:key="`${row.__id__}_${field.id}`"
>
<th
class="baserow-table__header-cell"
:class="{
'baserow-table__separator': fieldIndex === fields.length - 1,
}"
<slot name="field-name" :field="field">
<th :key="field.__id__" class="baserow-table__header-cell">
{{ field.name }}
</th>
</slot>
<slot
name="cell-content"
:value="row[field.name]"
:field="field"
:row-index="rowIndex"
>
{{ field.name }}
</th>
<td
class="baserow-table__cell"
:class="{
'baserow-table__separator': fieldIndex === fields.length - 1,
}"
>
<slot
name="cell-content"
:value="row[field.name]"
:field="field"
:row-index="rowIndex"
<td
class="baserow-table__cell"
:class="{
'baserow-table__separator':
fieldIndex === fields.length - 1,
}"
>
{{ value }}
</slot>
</td>
</td>
</slot>
</tr>
</tbody>
</template>
@ -105,9 +101,6 @@ export default {
default: TABLE_ORIENTATION.HORIZONTAL,
},
},
data() {
return {}
},
computed: {
TABLE_ORIENTATION() {
return TABLE_ORIENTATION

View file

@ -1,31 +1,46 @@
<template>
<div class="table-element">
<BaserowTable
:fields="element.fields"
<ABTable
:fields="fields"
:rows="rows"
:orientation="orientation"
class="ab-table"
:style="getStyleOverride('table')"
:orientation="orientation"
>
<template #cell-content="{ rowIndex, field, value }">
<component
:is="collectionFieldTypes[field.type].component"
:element="element"
:field="field"
:application-context-additions="{
recordIndex: rowIndex,
recordIndexPath: [...applicationContext.recordIndexPath, rowIndex],
field,
<!--
-- We force-self-aligment to `auto` here to prevent some self-positionning
-- like in buttons or links. we want to position the content through the table
-- style to be able to override it later. Otherwise we have a conflict between
-- these two aligments and only the more specific one (the field one)
-- is respected even if it comes from the main theme.
-->
<td
:key="field.id"
class="ab-table__cell"
:style="{
'--force-self-alignment': 'auto',
...fieldOverrides[field.id],
}"
v-bind="value"
/>
>
<div class="ab-table__cell-content">
<component
:is="collectionFieldTypes[field.type].component"
:element="element"
:field="field"
:application-context-additions="{
recordIndex: rowIndex,
recordIndexPath: [
...applicationContext.recordIndexPath,
rowIndex,
],
field,
}"
v-bind="value"
/>
</div>
</td>
</template>
<template #empty-state>
<div class="table-element__empty-message">
{{ $t('tableElement.empty') }}
</div>
</template>
</BaserowTable>
</ABTable>
<div class="table-element__footer">
<ABButton
v-if="hasMorePage"
@ -97,6 +112,22 @@ export default {
return newRow
})
},
fieldOverrides() {
return Object.fromEntries(
this.element.fields.map((field) => {
const fieldType = this.collectionFieldTypes[field.type]
return [
field.id,
fieldType.getStyleOverride({
colorVariables: this.colorVariables,
field,
theme: this.builder.theme,
}),
]
})
)
},
collectionFieldTypes() {
return this.$registry.getAll('collectionField')
},

View file

@ -97,7 +97,7 @@ export default {
return renderer.renderToken(tokens, idx, options)
},
paragraph_open: (tokens, idx, options, env, renderer) => {
tokens[idx].attrJoin('class', 'ab-paragraph')
tokens[idx].attrJoin('class', 'ab-text')
return renderer.renderToken(tokens, idx, options)
},
table_open: (tokens, idx, options, env, renderer) => {

View file

@ -4,10 +4,12 @@
<script>
import ABCheckbox from '@baserow/modules/builder/components/elements/baseComponents/ABCheckbox'
import collectionField from '@baserow/modules/builder/mixins/collectionField'
export default {
name: 'BooleanField',
components: { ABCheckbox },
mixins: [collectionField],
props: {
value: {
type: Boolean,

View file

@ -8,24 +8,16 @@
</template>
<script>
import element from '@baserow/modules/builder/mixins/element'
import collectionField from '@baserow/modules/builder/mixins/collectionField'
export default {
name: 'ButtonField',
mixins: [element],
mixins: [collectionField],
props: {
element: {
type: Object,
required: true,
},
label: {
type: String,
required: true,
},
field: {
type: Object,
required: true,
},
},
computed: {
eventName() {

View file

@ -1,15 +1,15 @@
<template>
<div class="link-field">
<ABLink variant="button" :url="url" :target="target">
{{ realLinkName }}
</ABLink>
</div>
<ABLink variant="button" :url="url" :target="target">
{{ realLinkName }}
</ABLink>
</template>
<script>
import collectionField from '@baserow/modules/builder/mixins/collectionField'
export default {
name: 'LinkField',
inject: ['mode', 'builder'],
mixins: [collectionField],
props: {
url: {
type: String,

View file

@ -1,23 +1,21 @@
<template>
<div class="ab-tag-group">
<div
<div class="tag-field">
<ABTag
v-for="(tag, index) in tags"
:key="`${index}-${tag.value}`"
class="ab-tag"
:style="`background-color: ${resolveColor(tag.color, colorVariables)}`"
:color="resolveColor(tag.color, colorVariables)"
>
{{ tag.value }}
</div>
</ABTag>
</div>
</template>
<script>
import { resolveColor } from '@baserow/modules/core/utils/colors'
import { themeToColorVariables } from '@baserow/modules/builder/utils/theme'
import collectionField from '@baserow/modules/builder/mixins/collectionField'
export default {
name: 'TagsField',
inject: ['builder'],
mixins: [collectionField],
props: {
tags: {
type: Array,
@ -25,11 +23,5 @@ export default {
default: () => [],
},
},
computed: {
colorVariables() {
return themeToColorVariables(this.builder.theme)
},
},
methods: { resolveColor },
}
</script>

View file

@ -1,10 +1,13 @@
<template>
<span>{{ value }}</span>
<span class="ab-text">{{ value }}</span>
</template>
<script>
import collectionField from '@baserow/modules/builder/mixins/collectionField'
export default {
name: 'TextField',
mixins: [collectionField],
props: {
value: {
type: String,

View file

@ -11,6 +11,16 @@
v-model="values.value"
:placeholder="$t('textFieldForm.fieldValuePlaceholder')"
/>
<template #after-input>
<CustomStyle
v-model="values.styles"
style-key="cell"
:config-block-types="['table']"
:theme="baseTheme"
:extra-args="{ onlyCell: true, noAlignment: true }"
variant="normal"
/>
</template>
</FormGroup>
</form>
</template>
@ -18,16 +28,18 @@
<script>
import collectionFieldForm from '@baserow/modules/builder/mixins/collectionFieldForm'
import InjectedFormulaInput from '@baserow/modules/core/components/formula/InjectedFormulaInput'
import CustomStyle from '@baserow/modules/builder/components/elements/components/forms/style/CustomStyle'
export default {
name: 'BooleanFieldForm',
components: { InjectedFormulaInput },
components: { InjectedFormulaInput, CustomStyle },
mixins: [collectionFieldForm],
data() {
return {
allowedValues: ['value'],
allowedValues: ['value', 'styles'],
values: {
value: '',
styles: {},
},
}
},

View file

@ -11,6 +11,16 @@
v-model="values.label"
:placeholder="$t('buttonFieldForm.labelPlaceholder')"
/>
<template #after-input>
<CustomStyle
v-model="values.styles"
style-key="cell"
:config-block-types="['table', 'button']"
:theme="baseTheme"
:extra-args="{ onlyCell: true, noAlignment: true }"
variant="normal"
/>
</template>
</FormGroup>
<Alert>
{{ $t('buttonFieldForm.infoMessage') }}
@ -21,16 +31,18 @@
<script>
import collectionFieldForm from '@baserow/modules/builder/mixins/collectionFieldForm'
import InjectedFormulaInput from '@baserow/modules/core/components/formula/InjectedFormulaInput'
import CustomStyle from '@baserow/modules/builder/components/elements/components/forms/style/CustomStyle'
export default {
name: 'ButtonFieldForm',
components: { InjectedFormulaInput },
components: { InjectedFormulaInput, CustomStyle },
mixins: [collectionFieldForm],
data() {
return {
allowedValues: ['label'],
allowedValues: ['label', 'styles'],
values: {
label: '',
styles: {},
},
}
},

View file

@ -11,6 +11,16 @@
v-model="values.link_name"
:placeholder="$t('linkFieldForm.fieldLinkNamePlaceholder')"
/>
<template #after-input>
<CustomStyle
v-model="values.styles"
style-key="cell"
:config-block-types="['table', 'button']"
:theme="baseTheme"
:extra-args="{ onlyCell: true, noAlignment: true }"
variant="normal"
/>
</template>
</FormGroup>
<LinkNavigationSelectionForm
:default-values="defaultValues"
@ -22,20 +32,23 @@
<script>
import collectionFieldForm from '@baserow/modules/builder/mixins/collectionFieldForm'
import InjectedFormulaInput from '@baserow/modules/core/components/formula/InjectedFormulaInput'
import CustomStyle from '@baserow/modules/builder/components/elements/components/forms/style/CustomStyle'
import LinkNavigationSelectionForm from '@baserow/modules/builder/components/elements/components/forms/general/LinkNavigationSelectionForm'
export default {
name: 'TextField',
components: {
InjectedFormulaInput,
CustomStyle,
LinkNavigationSelectionForm,
},
mixins: [collectionFieldForm],
data() {
return {
allowedValues: ['link_name'],
allowedValues: ['link_name', 'styles'],
values: {
link_name: '',
styles: {},
},
}
},

View file

@ -11,6 +11,16 @@
v-model="values.values"
:placeholder="$t('tagsFieldForm.fieldValuesPlaceholder')"
/>
<template #after-input>
<CustomStyle
v-model="values.styles"
style-key="cell"
:config-block-types="['table', 'typography']"
:theme="baseTheme"
:extra-args="{ onlyCell: true, onlyBody: true, noAlignment: true }"
variant="normal"
/>
</template>
</FormGroup>
<div>
@ -61,18 +71,20 @@
<script>
import InjectedFormulaInput from '@baserow/modules/core/components/formula/InjectedFormulaInput'
import collectionFieldForm from '@baserow/modules/builder/mixins/collectionFieldForm'
import CustomStyle from '@baserow/modules/builder/components/elements/components/forms/style/CustomStyle'
export default {
name: 'TagsField',
components: { InjectedFormulaInput },
components: { InjectedFormulaInput, CustomStyle },
mixins: [collectionFieldForm],
data() {
return {
allowedValues: ['values', 'colors', 'colors_is_formula'],
allowedValues: ['values', 'colors', 'colors_is_formula', 'styles'],
values: {
values: '',
colors: '#acc8f8',
colors_is_formula: false,
styles: {},
},
}
},

View file

@ -11,6 +11,16 @@
v-model="values.value"
:placeholder="$t('textFieldForm.fieldValuePlaceholder')"
/>
<template #after-input>
<CustomStyle
v-model="values.styles"
style-key="cell"
:config-block-types="['table', 'typography']"
:theme="baseTheme"
:extra-args="{ onlyCell: true, onlyBody: true, noAlignment: true }"
variant="normal"
/>
</template>
</FormGroup>
</form>
</template>
@ -18,16 +28,18 @@
<script>
import collectionFieldForm from '@baserow/modules/builder/mixins/collectionFieldForm'
import InjectedFormulaInput from '@baserow/modules/core/components/formula/InjectedFormulaInput'
import CustomStyle from '@baserow/modules/builder/components/elements/components/forms/style/CustomStyle'
export default {
name: 'TextField',
components: { InjectedFormulaInput },
components: { InjectedFormulaInput, CustomStyle },
mixins: [collectionFieldForm],
data() {
return {
allowedValues: ['value'],
allowedValues: ['value', 'styles'],
values: {
value: '',
styles: {},
},
}
},

View file

@ -3,8 +3,9 @@
<CustomStyle
v-model="values.styles"
style-key="table"
:config-block-types="['table']"
:config-block-types="['table', 'typography']"
:theme="builder.theme"
:extra-args="{ onlyBody: true, noAlignment: true }"
/>
<FormGroup
class="margin-bottom-2"
@ -46,7 +47,6 @@
:config-block-types="['button']"
:theme="builder.theme"
/>
<FormGroup
small-label
:label="$t('tableElementForm.buttonLoadMoreLabel')"
@ -161,6 +161,7 @@
:is="collectionTypes[field.type].formComponent"
:element="element"
:default-values="field"
:base-theme="collectionFieldBaseTheme"
:application-context-additions="{
collectionField: field,
}"
@ -267,6 +268,9 @@ export default {
collectionTypes() {
return this.$registry.getAll('collectionField')
},
collectionFieldBaseTheme() {
return { ...this.builder.theme, ...this.values.styles?.table }
},
errorMessageItemsPerPage() {
return this.$v.values.items_per_page.$dirty &&
!this.$v.values.items_per_page.required

View file

@ -1,6 +1,15 @@
<template>
<div class="custom-style">
<div class="custom-style" :class="`custom-style--${variant}`">
<ButtonText
v-if="variant === 'float'"
v-tooltip="$t('customStyle.configureThemeOverrides')"
class="custom-style__button"
icon="baserow-icon-settings"
tooltip-position="bottom-left"
@click="openPanel()"
/>
<ButtonIcon
v-else
v-tooltip="$t('customStyle.configureThemeOverrides')"
class="custom-style__button"
icon="baserow-icon-settings"
@ -14,30 +23,29 @@
{{ $t('customStyle.themeOverrides') }}
</div>
</div>
<div v-auto-overflow-scroll class="custom-style__config-blocks">
<div
v-for="(themeConfigBlock, index) in themeConfigBlocks"
<Tabs class="custom-style__config-blocks">
<Tab
v-for="themeConfigBlock in themeConfigBlocks"
:key="themeConfigBlock.getType()"
:title="themeConfigBlock.label"
class="custom-style__config-block"
>
<h2
v-if="themeConfigBlocks.length > 1"
class="custom-style__config-block-title"
<div
v-auto-overflow-scroll
class="custom-style__config-block-content"
>
{{ themeConfigBlock.label }}
</h2>
<ThemeConfigBlock
ref="configBlocks"
:theme="theme"
:default-values="value?.[styleKey]"
:preview="false"
:theme-config-block-type="themeConfigBlock"
:class="{ 'margin-top-3': index >= 1 }"
:extra-args="extraArgs"
@values-changed="onValuesChanged($event)"
/>
</div>
</div>
<ThemeConfigBlock
ref="configBlocks"
:theme="theme"
:default-values="value?.[styleKey]"
:preview="false"
:theme-config-block-type="themeConfigBlock"
:extra-args="extraArgs"
@values-changed="onValuesChanged($event)"
/>
</div>
</Tab>
</Tabs>
</Context>
</div>
</template>
@ -55,6 +63,14 @@ export default {
required: false,
default: () => {},
},
variant: {
required: false,
type: String,
default: 'float',
validator: function (value) {
return ['float', 'normal'].includes(value)
},
},
theme: { type: Object, required: true },
configBlockTypes: {
type: Array,

View file

@ -18,7 +18,7 @@
</template>
</FormGroup>
<FormGroup
v-if="values.button_width === 'auto'"
v-if="values.button_width === 'auto' && !extraArgs?.noAlignment"
horizontal-narrow
small-label
required
@ -34,7 +34,7 @@
</template>
</FormGroup>
<FormGroup
v-else
v-if="values.button_width === 'full'"
horizontal-narrow
small-label
required

View file

@ -18,6 +18,7 @@
</template>
</FormGroup>
<FormGroup
v-if="!extraArgs?.noAlignment"
horizontal-narrow
small-label
required

View file

@ -1,6 +1,9 @@
<template>
<div>
<ThemeConfigBlockSection :title="$t('tableThemeConfigBlock.table')">
<ThemeConfigBlockSection
v-if="!onlyCell"
:title="$t('tableThemeConfigBlock.table')"
>
<template #default>
<FormGroup
horizontal-narrow
@ -54,12 +57,14 @@
</FormGroup>
</template>
<template #preview>
<BaserowTable :fields="fields" :rows="rows" class="ab-table">
</BaserowTable>
<ABTable :fields="fields" :rows="rows" />
</template>
</ThemeConfigBlockSection>
<ThemeConfigBlockSection :title="$t('tableThemeConfigBlock.header')">
<ThemeConfigBlockSection
v-if="!onlyCell"
:title="$t('tableThemeConfigBlock.header')"
>
<template #default>
<FormGroup
horizontal-narrow
@ -148,8 +153,7 @@
</FormGroup>
</template>
<template #preview>
<BaserowTable :fields="fields" :rows="rows" class="ab-table">
</BaserowTable>
<ABTable :fields="fields" :rows="rows" />
</template>
</ThemeConfigBlockSection>
@ -232,11 +236,13 @@
</FormGroup>
</template>
<template #preview>
<BaserowTable :fields="fields" :rows="rows" class="ab-table">
</BaserowTable>
<ABTable :fields="fields" :rows="rows" />
</template>
</ThemeConfigBlockSection>
<ThemeConfigBlockSection :title="$t('tableThemeConfigBlock.separators')">
<ThemeConfigBlockSection
v-if="!onlyCell"
:title="$t('tableThemeConfigBlock.separators')"
>
<template #default>
<FormGroup
horizontal-narrow
@ -310,8 +316,7 @@
</FormGroup>
</template>
<template #preview>
<BaserowTable :fields="fields" :rows="rows" class="ab-table">
</BaserowTable>
<ABTable :fields="fields" :rows="rows" />
</template>
</ThemeConfigBlockSection>
</div>
@ -397,6 +402,9 @@ export default {
this.values.table_cell_horizontal_padding = newValue.horizontal
},
},
onlyCell() {
return this.extraArgs?.onlyCell
},
},
methods: {
isAllowedKey(key) {

View file

@ -20,6 +20,7 @@
</template>
</FormGroup>
<FormGroup
v-if="!extraArgs?.noAlignment"
horizontal-narrow
small-label
class="margin-bottom-2"

View file

@ -383,6 +383,7 @@
"table": "Table"
},
"colorThemeConfigBlock": {
"transparent": "Transparent",
"primaryColor": "Primary",
"secondaryColor": "Secondary",
"borderColor": "Border",
@ -464,7 +465,7 @@
},
"tableThemeConfigBlock": {
"borderColor": "Border color",
"backgroundAlternateColor": "Alternate color",
"backgroundAlternateColor": "Even rows color",
"backgroundColor": "Background color",
"textColor": "Text color",
"borderSize": "Border size",
@ -531,9 +532,11 @@
"buttonLoadMoreLabel": "Show more label"
},
"tableElement": {
"empty": "No items have been found.",
"showMore": "Show more"
},
"abTable": {
"empty": "No items have been found."
},
"repeatElement": {
"empty": "No items have been found.",
"showMore": "Show more",

View file

@ -1,6 +1,8 @@
import { mapGetters } from 'vuex'
import applicationContextMixin from '@baserow/modules/builder/mixins/applicationContext'
export default {
mixins: [applicationContextMixin],
computed: {
dataSources() {
return this.$store.getters['dataSource/getPageDataSources'](this.page)

View file

@ -0,0 +1,35 @@
import { resolveColor } from '@baserow/modules/core/utils/colors'
import { ThemeConfigBlockType } from '@baserow/modules/builder/themeConfigBlockTypes'
import applicationContextMixin from '@baserow/modules/builder/mixins/applicationContext'
export default {
inject: ['workspace', 'builder', 'page', 'mode'],
mixins: [applicationContextMixin],
props: {
element: {
type: Object,
required: true,
},
field: {
type: Object,
required: true,
},
},
computed: {
elementType() {
return this.$registry.get('element', this.element.type)
},
themeConfigBlocks() {
return this.$registry.getOrderedList('themeConfigBlock')
},
colorVariables() {
return ThemeConfigBlockType.getAllColorVariables(
this.themeConfigBlocks,
this.builder.theme
)
},
},
methods: {
resolveColor,
},
}

View file

@ -8,5 +8,9 @@ export default {
type: Object,
required: true,
},
baseTheme: {
type: Object,
required: true,
},
},
}

View file

@ -11,6 +11,8 @@ import ABCheckbox from '@baserow/modules/builder/components/elements/baseCompone
import ABRadio from '@baserow/modules/builder/components/elements/baseComponents/ABRadio.vue'
import ABImage from '@baserow/modules/builder/components/elements/baseComponents/ABImage.vue'
import ABParagraph from '@baserow/modules/builder/components/elements/baseComponents/ABParagraph.vue'
import ABTag from '@baserow/modules/builder/components/elements/baseComponents/ABTag.vue'
import ABTable from '@baserow/modules/builder/components/elements/baseComponents/ABTable.vue'
function setupVueForAB(Vue) {
Vue.component('ABButton', ABButton)
@ -24,6 +26,8 @@ function setupVueForAB(Vue) {
Vue.component('ABRadio', ABRadio)
Vue.component('ABImage', ABImage)
Vue.component('ABParagraph', ABParagraph)
Vue.component('ABTag', ABTag)
Vue.component('ABTable', ABTable)
}
setupVueForAB(Vue)

View file

@ -283,6 +283,7 @@ export class TypographyThemeConfigBlockType extends ThemeConfigBlockType {
style.addColorIfExists(theme, `body_text_color`)
style.addIfExists(theme, `body_text_alignment`)
style.addFontFamilyIfExists(theme, `body_font_family`)
return style.toObject()
}
@ -606,11 +607,31 @@ export class TableThemeConfigBlockType extends ThemeConfigBlockType {
style.addIfExists(theme, `table_header_text_alignment`)
style.addColorIfExists(theme, 'table_cell_background_color')
style.addColorIfExists(theme, 'table_cell_alternate_background_color')
if (
Object.prototype.hasOwnProperty.call(
theme,
'table_cell_alternate_background_color'
) &&
theme.table_cell_alternate_background_color !== 'transparent'
) {
// We want to set the alternate color only if defined
style.addColorIfExists(theme, 'table_cell_alternate_background_color')
}
style.addColorIfExists(theme, 'table_cell_text_color')
style.addFontFamilyIfExists(theme, `table_cell_font_family`)
style.addPixelValueIfExists(theme, `table_cell_font_size`)
style.addIfExists(theme, `table_cell_alignment`)
style.addIfExists(
theme,
'table_cell_alignment',
null,
(v) =>
({
[HORIZONTAL_ALIGNMENTS.LEFT]: 'flex-start',
[HORIZONTAL_ALIGNMENTS.CENTER]: 'center',
[HORIZONTAL_ALIGNMENTS.RIGHT]: 'flex-end',
}[v])
)
style.addPixelValueIfExists(theme, `table_cell_vertical_padding`)
style.addPixelValueIfExists(theme, `table_cell_horizontal_padding`)

View file

@ -11,7 +11,7 @@
border-radius: var(--button-border-radius, 4px);
width: var(--button-width, auto);
text-align: var(--button-text-alignment, center);
align-self: var(--button-alignment, flex-start);
align-self: var(--force-self-alignment, var(--button-alignment, initial));
font-family: var(--button-font-family, Inter);
padding: var(--button-vertical-padding, 4px)
var(--button-horizontal-padding, 12px);

View file

@ -2,7 +2,7 @@
font-size: var(--link-font-size, 14px);
text-decoration: var(--link-text-decoration, underline);
color: var(--link-text-color, $black);
align-self: var(--link-text-alignment, flex-start);
align-self: var(--force-self-alignment, var(--link-text-alignment, initial));
font-family: var(--link-font-family, Inter);
&:hover,

View file

@ -1,24 +1,17 @@
.ab-table {
border: var(--table-border-size, 1px) solid var(--table-border-color, $black);
text-align: var(--table-cell-alignment, left);
font-size: var(--body-font-size, 13px);
font-size: var(--body-font-size, 12px);
border-radius: var(--table-border-radius, 0);
background-color: var(--table-cell-background-color, $palette-neutral-200);
background-color: var(--table-cell-background-color, transparent);
.baserow-table__header-cell,
.baserow-table__cell {
padding: var(--table-cell-vertical-padding, 10px)
var(--table-cell-horizontal-padding, 20px);
}
.baserow-table__cell {
.ab-table__cell {
font-size: var(--body-font-size, 13px);
color: var(--body-text-color, $color-neutral-900);
font-family: var(--body-font-family, Inter);
text-align: var(--table-cell-alignment, left);
background-color: var(--table-cell-background-color, transparent);
}
.baserow-table__header-cell {
.ab-table__header-cell {
font-weight: 600;
background-color: var(
--table-header-background-color,
@ -28,54 +21,89 @@
color: var(--table-header-text-color, $color-neutral-900);
font-family: var(--table-header-font-family, Inter);
text-align: var(--table-header-text-alignment, left);
padding: var(--table-cell-vertical-padding, 10px)
var(--table-cell-horizontal-padding, 20px);
border-right: var(--table-vertical-separator-size, 1px) solid
var(--table-vertical-separator-color, $black);
}
& .baserow-table__row:not(:last-child) .baserow-table__separator {
border-bottom: var(--table-horizontal-separator-size, 1px) solid
var(--table-horizontal-separator-color, $black);
.baserow-table__row:nth-child(even) .ab-table__cell {
background-color: var(
--table-cell-alternate-background-color,
var(--table-cell-background-color, transparent)
);
}
.baserow-table--vertical {
.baserow-table__header-cell {
border-right: var(--table-vertical-separator-size, 1px) solid
var(--table-vertical-separator-color, $black);
}
tbody.baserow-table__row:nth-child(even) {
background-color: var(
--table-cell-alternate-background-color,
transparent
);
}
.baserow-table__empty-message {
background-color: var(--table-cell-background-color, transparent);
font-size: var(--body-font-size, 13px);
color: var(--body-text-color, $color-neutral-900);
font-family: var(--body-font-family, Inter);
}
.baserow-table--horizontal {
.baserow-table__header-cell,
.baserow-table__cell {
border-bottom: var(--table-horizontal-separator-size, 1px) solid
var(--table-horizontal-separator-color, $black);
border-right: var(--table-vertical-separator-size, 1px) solid
var(--table-vertical-separator-color, $black);
}
.baserow-table__row {
.baserow-table__cell:last-child,
.baserow-table__header-cell:last-child {
.ab-table__header-cell,
.ab-table__cell {
border-bottom: var(--table-horizontal-separator-size, 1px) solid
var(--table-horizontal-separator-color, $black);
}
.ab-table__cell {
border-right: var(--table-vertical-separator-size, 1px) solid
var(--table-vertical-separator-color, $black);
}
.ab-table__cell:last-child,
.ab-table__header-cell:last-child {
border-right: none;
}
}
.baserow-table__row:last-child {
.baserow-table__cell {
.ab-table__cell {
border-bottom: none;
}
}
tbody .baserow-table__row:nth-child(even) {
.baserow-table__row:nth-child(even) .ab-table__cell {
background-color: var(
--table-cell-alternate-background-color,
transparent
var(--table-cell-background-color, transparent)
);
}
}
.baserow-table--vertical {
.baserow-table__row tr:last-child {
.ab-table__header-cell,
.ab-table__cell {
border-bottom: var(--table-horizontal-separator-size, 1px) solid
var(--table-horizontal-separator-color, $black);
}
}
.baserow-table__row:last-child tr {
.ab-table__header-cell,
.ab-table__cell {
border-bottom: none;
}
}
}
}
.ab-table__cell-content {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: var(--table-cell-alignment, flex-start);
padding: var(--table-cell-vertical-padding, 10px)
var(--table-cell-horizontal-padding, 20px);
}
.ab-table__empty-message {
text-align: center;
padding: 50px 0;
font-weight: 600;
}

View file

@ -1,14 +1,13 @@
.ab-tag-group {
overflow: hidden;
display: flex;
flex-flow: row;
flex-wrap: nowrap;
align-items: flex-start;
gap: 8px;
}
.ab-tag {
@include select-option-style(inline-flex, true);
@extend %ellipsis;
overflow: hidden;
display: inline-block;
max-width: 140px;
font-size: var(--body-font-size, 14px);
color: var(--body-text-color, $black);
font-family: var(--body-font-family, Inter);
background-color: var(--tag-background-color, transparent);
border-radius: 1em;
padding: 0.4em 0.5em;
}

View file

@ -1,4 +1,4 @@
.ab-paragraph {
.ab-text {
font-size: var(--body-font-size, 14px);
margin: 0;
color: var(--body-text-color, $black);

View file

@ -6,7 +6,7 @@
@import 'ab_heading';
@import 'ab_input';
@import 'ab_link';
@import 'ab_paragraph';
@import 'ab_text';
@import 'ab_tag';
@import 'ab_radio';
@import 'ab_image';

View file

@ -1,6 +1,5 @@
@import 'forms/all';
@import 'ab_components/all';
@import 'collectionFields/all';
@import 'link_element';
@import 'heading_element';
@import 'image_element';
@ -11,3 +10,4 @@
@import 'choice_element';
@import 'iframe_element';
@import 'repeat_element';
@import 'tag_field';

View file

@ -1,5 +0,0 @@
.link-field {
width: 100%;
display: flex;
flex-direction: column;
}

View file

@ -0,0 +1,7 @@
.tag-field {
overflow: hidden;
display: flex;
flex-flow: row;
flex-wrap: nowrap;
gap: 4px;
}

View file

@ -2,10 +2,12 @@
position: relative;
}
.custom-style__button {
position: absolute;
right: 4px;
top: -6px;
.custom-style--float {
.custom-style__button {
position: absolute;
right: 4px;
top: -6px;
}
}
.custom-style__context {
@ -20,7 +22,7 @@
height: 40px;
align-items: center;
position: relative;
border-bottom: 1px solid $color-neutral-200;
border-bottom: 1px solid $color-neutral-100;
cursor: pointer;
}
@ -37,12 +39,24 @@
}
.custom-style__config-blocks {
width: 358px;
padding: 20px 20px 5px;
overflow-y: auto;
// 100% - top bar height
height: calc(100% - 40px);
overflow: hidden;
}
.custom-style__config-block-title {
padding: 0 10px;
}
.custom-style__config-block {
// Width of sidebar - 2 x padding - left border
width: calc(400px - 40px - 1px);
overflow-y: auto;
// 100% - tab header height
height: calc(100% - 40px);
}
.custom-style__context .tabs__header {
background-color: $color-neutral-50;
}