mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-14 17:18:33 +00:00
Merge branch '2388-3-add-new-styles' into 'develop'
Improve AB styling v3 - Add a lot of new styles Closes #2388 See merge request baserow/baserow!2476
This commit is contained in:
commit
d61ad81a91
77 changed files with 3176 additions and 1072 deletions
backend
src/baserow
tests/baserow/contrib/builder
changelog/entries/unreleased/feature
web-frontend/modules
builder
components
FontFamilySelector.vuePaddingSelector.vuePixelValueSelector.vue
elementTypes.jsenums.jsfontFamilyTypes.jselements
baseComponents
components/forms
page
theme
locales
mixins
plugin.jsthemeConfigBlockTypes.jscore
assets/scss/components
builder
color_input.scsscolor_picker.scsscolor_picker_context.scssform.scssform_input.scssimage_input.scsscomponents
enums.jslocales
mixins
pages
plugins
|
@ -11,12 +11,13 @@ from baserow.api.app_auth_providers.serializers import (
|
||||||
)
|
)
|
||||||
from baserow.api.polymorphic import PolymorphicSerializer
|
from baserow.api.polymorphic import PolymorphicSerializer
|
||||||
from baserow.api.services.serializers import PublicServiceSerializer
|
from baserow.api.services.serializers import PublicServiceSerializer
|
||||||
from baserow.api.user_files.serializers import UserFileSerializer
|
from baserow.api.user_files.serializers import UserFileField, UserFileSerializer
|
||||||
from baserow.contrib.builder.api.pages.serializers import PathParamSerializer
|
from baserow.contrib.builder.api.pages.serializers import PathParamSerializer
|
||||||
from baserow.contrib.builder.api.theme.serializers import (
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
CombinedThemeConfigBlocksSerializer,
|
CombinedThemeConfigBlocksSerializer,
|
||||||
serialize_builder_theme,
|
serialize_builder_theme,
|
||||||
)
|
)
|
||||||
|
from baserow.contrib.builder.api.validators import image_file_validation
|
||||||
from baserow.contrib.builder.data_sources.models import DataSource
|
from baserow.contrib.builder.data_sources.models import DataSource
|
||||||
from baserow.contrib.builder.domains.models import Domain
|
from baserow.contrib.builder.domains.models import Domain
|
||||||
from baserow.contrib.builder.domains.registries import domain_type_registry
|
from baserow.contrib.builder.domains.registries import domain_type_registry
|
||||||
|
@ -98,6 +99,12 @@ class PublicElementSerializer(serializers.ModelSerializer):
|
||||||
def get_type(self, instance):
|
def get_type(self, instance):
|
||||||
return element_type_registry.get_by_model(instance.specific_class).type
|
return element_type_registry.get_by_model(instance.specific_class).type
|
||||||
|
|
||||||
|
style_background_file = UserFileField(
|
||||||
|
allow_null=True,
|
||||||
|
help_text="The background image file",
|
||||||
|
validators=[image_file_validation],
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Element
|
model = Element
|
||||||
fields = (
|
fields = (
|
||||||
|
@ -111,17 +118,23 @@ class PublicElementSerializer(serializers.ModelSerializer):
|
||||||
"style_border_top_color",
|
"style_border_top_color",
|
||||||
"style_border_top_size",
|
"style_border_top_size",
|
||||||
"style_padding_top",
|
"style_padding_top",
|
||||||
|
"style_margin_top",
|
||||||
"style_border_bottom_color",
|
"style_border_bottom_color",
|
||||||
"style_border_bottom_size",
|
"style_border_bottom_size",
|
||||||
"style_padding_bottom",
|
"style_padding_bottom",
|
||||||
|
"style_margin_bottom",
|
||||||
"style_border_left_color",
|
"style_border_left_color",
|
||||||
"style_border_left_size",
|
"style_border_left_size",
|
||||||
"style_padding_left",
|
"style_padding_left",
|
||||||
|
"style_margin_left",
|
||||||
"style_border_right_color",
|
"style_border_right_color",
|
||||||
"style_border_right_size",
|
"style_border_right_size",
|
||||||
"style_padding_right",
|
"style_padding_right",
|
||||||
|
"style_margin_right",
|
||||||
"style_background",
|
"style_background",
|
||||||
"style_background_color",
|
"style_background_color",
|
||||||
|
"style_background_file",
|
||||||
|
"style_background_mode",
|
||||||
"style_width",
|
"style_width",
|
||||||
"role_type",
|
"role_type",
|
||||||
"roles",
|
"roles",
|
||||||
|
|
|
@ -7,6 +7,8 @@ from drf_spectacular.utils import extend_schema_field, extend_schema_serializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
from baserow.api.user_files.serializers import UserFileField
|
||||||
|
from baserow.contrib.builder.api.validators import image_file_validation
|
||||||
from baserow.contrib.builder.api.workflow_actions.serializers import (
|
from baserow.contrib.builder.api.workflow_actions.serializers import (
|
||||||
BuilderWorkflowActionSerializer,
|
BuilderWorkflowActionSerializer,
|
||||||
)
|
)
|
||||||
|
@ -42,6 +44,12 @@ class ElementSerializer(serializers.ModelSerializer):
|
||||||
def get_type(self, instance):
|
def get_type(self, instance):
|
||||||
return element_type_registry.get_by_model(instance.specific_class).type
|
return element_type_registry.get_by_model(instance.specific_class).type
|
||||||
|
|
||||||
|
style_background_file = UserFileField(
|
||||||
|
allow_null=True,
|
||||||
|
help_text="The background image file",
|
||||||
|
validators=[image_file_validation],
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Element
|
model = Element
|
||||||
fields = (
|
fields = (
|
||||||
|
@ -56,17 +64,23 @@ class ElementSerializer(serializers.ModelSerializer):
|
||||||
"style_border_top_color",
|
"style_border_top_color",
|
||||||
"style_border_top_size",
|
"style_border_top_size",
|
||||||
"style_padding_top",
|
"style_padding_top",
|
||||||
|
"style_margin_top",
|
||||||
"style_border_bottom_color",
|
"style_border_bottom_color",
|
||||||
"style_border_bottom_size",
|
"style_border_bottom_size",
|
||||||
"style_padding_bottom",
|
"style_padding_bottom",
|
||||||
|
"style_margin_bottom",
|
||||||
"style_border_left_color",
|
"style_border_left_color",
|
||||||
"style_border_left_size",
|
"style_border_left_size",
|
||||||
"style_padding_left",
|
"style_padding_left",
|
||||||
|
"style_margin_left",
|
||||||
"style_border_right_color",
|
"style_border_right_color",
|
||||||
"style_border_right_size",
|
"style_border_right_size",
|
||||||
"style_padding_right",
|
"style_padding_right",
|
||||||
|
"style_margin_right",
|
||||||
"style_background",
|
"style_background",
|
||||||
"style_background_color",
|
"style_background_color",
|
||||||
|
"style_background_file",
|
||||||
|
"style_background_mode",
|
||||||
"style_width",
|
"style_width",
|
||||||
"role_type",
|
"role_type",
|
||||||
"roles",
|
"roles",
|
||||||
|
@ -102,6 +116,12 @@ class CreateElementSerializer(serializers.ModelSerializer):
|
||||||
"the given id.",
|
"the given id.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
style_background_file = UserFileField(
|
||||||
|
allow_null=True,
|
||||||
|
help_text="The background image file",
|
||||||
|
validators=[image_file_validation],
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Element
|
model = Element
|
||||||
fields = (
|
fields = (
|
||||||
|
@ -111,28 +131,42 @@ class CreateElementSerializer(serializers.ModelSerializer):
|
||||||
"parent_element_id",
|
"parent_element_id",
|
||||||
"place_in_container",
|
"place_in_container",
|
||||||
"visibility",
|
"visibility",
|
||||||
|
"styles",
|
||||||
"style_border_top_color",
|
"style_border_top_color",
|
||||||
"style_border_top_size",
|
"style_border_top_size",
|
||||||
"style_padding_top",
|
"style_padding_top",
|
||||||
|
"style_margin_top",
|
||||||
"style_border_bottom_color",
|
"style_border_bottom_color",
|
||||||
"style_border_bottom_size",
|
"style_border_bottom_size",
|
||||||
"style_padding_bottom",
|
"style_padding_bottom",
|
||||||
|
"style_margin_bottom",
|
||||||
"style_border_left_color",
|
"style_border_left_color",
|
||||||
"style_border_left_size",
|
"style_border_left_size",
|
||||||
"style_padding_left",
|
"style_padding_left",
|
||||||
|
"style_margin_left",
|
||||||
"style_border_right_color",
|
"style_border_right_color",
|
||||||
"style_border_right_size",
|
"style_border_right_size",
|
||||||
"style_padding_right",
|
"style_padding_right",
|
||||||
|
"style_margin_right",
|
||||||
"style_background",
|
"style_background",
|
||||||
"style_background_color",
|
"style_background_color",
|
||||||
|
"style_background_file",
|
||||||
|
"style_background_mode",
|
||||||
"style_width",
|
"style_width",
|
||||||
)
|
)
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"visibility": {"default": Element.VISIBILITY_TYPES.ALL},
|
"visibility": {"default": Element.VISIBILITY_TYPES.ALL},
|
||||||
|
"styles": {"default": dict},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UpdateElementSerializer(serializers.ModelSerializer):
|
class UpdateElementSerializer(serializers.ModelSerializer):
|
||||||
|
style_background_file = UserFileField(
|
||||||
|
allow_null=True,
|
||||||
|
help_text="The background image file",
|
||||||
|
validators=[image_file_validation],
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Element
|
model = Element
|
||||||
fields = (
|
fields = (
|
||||||
|
@ -141,17 +175,23 @@ class UpdateElementSerializer(serializers.ModelSerializer):
|
||||||
"style_border_top_color",
|
"style_border_top_color",
|
||||||
"style_border_top_size",
|
"style_border_top_size",
|
||||||
"style_padding_top",
|
"style_padding_top",
|
||||||
|
"style_margin_top",
|
||||||
"style_border_bottom_color",
|
"style_border_bottom_color",
|
||||||
"style_border_bottom_size",
|
"style_border_bottom_size",
|
||||||
"style_padding_bottom",
|
"style_padding_bottom",
|
||||||
|
"style_margin_bottom",
|
||||||
"style_border_left_color",
|
"style_border_left_color",
|
||||||
"style_border_left_size",
|
"style_border_left_size",
|
||||||
"style_padding_left",
|
"style_padding_left",
|
||||||
|
"style_margin_left",
|
||||||
"style_border_right_color",
|
"style_border_right_color",
|
||||||
"style_border_right_size",
|
"style_border_right_size",
|
||||||
"style_padding_right",
|
"style_padding_right",
|
||||||
|
"style_margin_right",
|
||||||
"style_background",
|
"style_background",
|
||||||
"style_background_color",
|
"style_background_color",
|
||||||
|
"style_background_file",
|
||||||
|
"style_background_mode",
|
||||||
"style_width",
|
"style_width",
|
||||||
"role_type",
|
"role_type",
|
||||||
"roles",
|
"roles",
|
||||||
|
|
|
@ -5,6 +5,49 @@ from baserow.contrib.builder.models import Builder
|
||||||
from baserow.contrib.builder.theme.registries import theme_config_block_registry
|
from baserow.contrib.builder.theme.registries import theme_config_block_registry
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicConfigBlockSerializer(serializers.Serializer):
|
||||||
|
"""
|
||||||
|
Style overrides for this element.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
property_name=None,
|
||||||
|
theme_config_block_type_name=None,
|
||||||
|
serializer_kwargs=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
if property_name is None:
|
||||||
|
raise ValueError("Missing property_name parameter")
|
||||||
|
if theme_config_block_type_name is None:
|
||||||
|
raise ValueError("Missing theme_block_type parameter")
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if serializer_kwargs is None:
|
||||||
|
serializer_kwargs = {}
|
||||||
|
|
||||||
|
if not isinstance(property_name, list):
|
||||||
|
property_name = [property_name]
|
||||||
|
|
||||||
|
if not isinstance(theme_config_block_type_name, list):
|
||||||
|
theme_config_block_type_name = [theme_config_block_type_name]
|
||||||
|
|
||||||
|
for prop, type_name in zip(property_name, theme_config_block_type_name):
|
||||||
|
theme_config_block_type = theme_config_block_registry.get(type_name)
|
||||||
|
self.fields[prop] = theme_config_block_type.get_serializer_class()(
|
||||||
|
**({"help_text": f"Styles overrides for {prop}"} | serializer_kwargs)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dynamically create the Meta class with ref name to prevent collision
|
||||||
|
class DynamicMeta:
|
||||||
|
type_names = "".join([p.capitalize() for p in theme_config_block_type_name])
|
||||||
|
ref_name = f"{type_names}ConfigBlockSerializer"
|
||||||
|
|
||||||
|
self.Meta = DynamicMeta
|
||||||
|
|
||||||
|
|
||||||
def serialize_builder_theme(builder: Builder) -> dict:
|
def serialize_builder_theme(builder: Builder) -> dict:
|
||||||
"""
|
"""
|
||||||
A helper function that serializes all theme properties of the provided builder.
|
A helper function that serializes all theme properties of the provided builder.
|
||||||
|
@ -35,7 +78,7 @@ def get_combined_theme_config_blocks_serializer_class() -> serializers.Serialize
|
||||||
:return: The generated serializer.
|
:return: The generated serializer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if hasattr(get_combined_theme_config_blocks_serializer_class, "cache"):
|
if hasattr(get_combined_theme_config_blocks_serializer_class, "cached_class"):
|
||||||
return get_combined_theme_config_blocks_serializer_class.cached_class
|
return get_combined_theme_config_blocks_serializer_class.cached_class
|
||||||
|
|
||||||
if len(theme_config_block_registry.registry.values()) == 0:
|
if len(theme_config_block_registry.registry.values()) == 0:
|
||||||
|
|
|
@ -56,7 +56,9 @@ class ThemeView(APIView):
|
||||||
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
|
ApplicationDoesNotExist: ERROR_APPLICATION_DOES_NOT_EXIST,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@validate_body(CombinedThemeConfigBlocksSerializer, return_validated=True)
|
@validate_body(
|
||||||
|
CombinedThemeConfigBlocksSerializer, return_validated=True, partial=True
|
||||||
|
)
|
||||||
def patch(self, request, data: Dict, builder_id: int):
|
def patch(self, request, data: Dict, builder_id: int):
|
||||||
builder = BuilderHandler().get_builder(builder_id)
|
builder = BuilderHandler().get_builder(builder_id)
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,7 @@ class BuilderConfig(AppConfig):
|
||||||
ColorThemeConfigBlockType,
|
ColorThemeConfigBlockType,
|
||||||
ImageThemeConfigBlockType,
|
ImageThemeConfigBlockType,
|
||||||
LinkThemeConfigBlockType,
|
LinkThemeConfigBlockType,
|
||||||
|
PageThemeConfigBlockType,
|
||||||
TypographyThemeConfigBlockType,
|
TypographyThemeConfigBlockType,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -248,6 +249,7 @@ class BuilderConfig(AppConfig):
|
||||||
theme_config_block_registry.register(ButtonThemeConfigBlockType())
|
theme_config_block_registry.register(ButtonThemeConfigBlockType())
|
||||||
theme_config_block_registry.register(LinkThemeConfigBlockType())
|
theme_config_block_registry.register(LinkThemeConfigBlockType())
|
||||||
theme_config_block_registry.register(ImageThemeConfigBlockType())
|
theme_config_block_registry.register(ImageThemeConfigBlockType())
|
||||||
|
theme_config_block_registry.register(PageThemeConfigBlockType())
|
||||||
|
|
||||||
from .workflow_actions.registries import builder_workflow_action_type_registry
|
from .workflow_actions.registries import builder_workflow_action_type_registry
|
||||||
from .workflow_actions.workflow_action_types import (
|
from .workflow_actions.workflow_action_types import (
|
||||||
|
|
|
@ -18,3 +18,9 @@ class VerticalAlignments(models.TextChoices):
|
||||||
class WIDTHS(models.TextChoices):
|
class WIDTHS(models.TextChoices):
|
||||||
AUTO = "auto"
|
AUTO = "auto"
|
||||||
FULL = "full"
|
FULL = "full"
|
||||||
|
|
||||||
|
|
||||||
|
class BACKGROUND_IMAGE_MODES(models.TextChoices):
|
||||||
|
TILE = "tile"
|
||||||
|
FILL = "fill"
|
||||||
|
FIT = "fit"
|
||||||
|
|
|
@ -161,6 +161,12 @@ class FormContainerElementType(ContainerElementTypeMixin, ElementType):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer_field_overrides(self):
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
ButtonThemeConfigBlockType,
|
||||||
|
)
|
||||||
from baserow.core.formula.serializers import FormulaSerializerField
|
from baserow.core.formula.serializers import FormulaSerializerField
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -184,6 +190,12 @@ class FormContainerElementType(ContainerElementTypeMixin, ElementType):
|
||||||
).help_text,
|
).help_text,
|
||||||
required=False,
|
required=False,
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="button",
|
||||||
|
theme_config_block_type_name=ButtonThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -232,6 +244,13 @@ class TableElementType(CollectionElementWithFieldsTypeMixin, ElementType):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer_field_overrides(self):
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
ButtonThemeConfigBlockType,
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
**super().serializer_field_overrides,
|
**super().serializer_field_overrides,
|
||||||
"orientation": serializers.JSONField(
|
"orientation": serializers.JSONField(
|
||||||
|
@ -239,6 +258,12 @@ class TableElementType(CollectionElementWithFieldsTypeMixin, ElementType):
|
||||||
default=get_default_table_orientation,
|
default=get_default_table_orientation,
|
||||||
help_text=TableElement._meta.get_field("orientation").help_text,
|
help_text=TableElement._meta.get_field("orientation").help_text,
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="button",
|
||||||
|
theme_config_block_type_name=ButtonThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_pytest_params(self, pytest_data_fixture) -> Dict[str, Any]:
|
def get_pytest_params(self, pytest_data_fixture) -> Dict[str, Any]:
|
||||||
|
@ -276,6 +301,25 @@ class RepeatElementType(
|
||||||
orientation: str
|
orientation: str
|
||||||
items_per_row: dict
|
items_per_row: dict
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
ButtonThemeConfigBlockType,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
**super().serializer_field_overrides,
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="button",
|
||||||
|
theme_config_block_type_name=ButtonThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
def import_context_addition(self, instance, id_mapping):
|
def import_context_addition(self, instance, id_mapping):
|
||||||
return {"data_source_id": instance.data_source_id}
|
return {"data_source_id": instance.data_source_id}
|
||||||
|
|
||||||
|
@ -305,6 +349,12 @@ class HeadingElementType(ElementType):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer_field_overrides(self):
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
TypographyThemeConfigBlockType,
|
||||||
|
)
|
||||||
from baserow.core.formula.serializers import FormulaSerializerField
|
from baserow.core.formula.serializers import FormulaSerializerField
|
||||||
|
|
||||||
overrides = {
|
overrides = {
|
||||||
|
@ -326,6 +376,12 @@ class HeadingElementType(ElementType):
|
||||||
allow_blank=True,
|
allow_blank=True,
|
||||||
help_text="Heading font color.",
|
help_text="Heading font color.",
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="typography",
|
||||||
|
theme_config_block_type_name=TypographyThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return overrides
|
return overrides
|
||||||
|
@ -379,6 +435,12 @@ class TextElementType(ElementType):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer_field_overrides(self):
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
TypographyThemeConfigBlockType,
|
||||||
|
)
|
||||||
from baserow.core.formula.serializers import FormulaSerializerField
|
from baserow.core.formula.serializers import FormulaSerializerField
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -393,6 +455,12 @@ class TextElementType(ElementType):
|
||||||
default=TextElement.TEXT_FORMATS.PLAIN,
|
default=TextElement.TEXT_FORMATS.PLAIN,
|
||||||
help_text=TextElement._meta.get_field("format").help_text,
|
help_text=TextElement._meta.get_field("format").help_text,
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="typography",
|
||||||
|
theme_config_block_type_name=TypographyThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
def deserialize_property(
|
def deserialize_property(
|
||||||
|
@ -636,6 +704,13 @@ class LinkElementType(ElementType):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer_field_overrides(self):
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
ButtonThemeConfigBlockType,
|
||||||
|
LinkThemeConfigBlockType,
|
||||||
|
)
|
||||||
from baserow.core.formula.serializers import FormulaSerializerField
|
from baserow.core.formula.serializers import FormulaSerializerField
|
||||||
|
|
||||||
overrides = (
|
overrides = (
|
||||||
|
@ -669,8 +744,18 @@ class LinkElementType(ElementType):
|
||||||
default="primary",
|
default="primary",
|
||||||
help_text="Button color.",
|
help_text="Button color.",
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name=["button", "link"],
|
||||||
|
theme_config_block_type_name=[
|
||||||
|
ButtonThemeConfigBlockType.type,
|
||||||
|
LinkThemeConfigBlockType.type,
|
||||||
|
],
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return overrides
|
return overrides
|
||||||
|
|
||||||
def get_pytest_params(self, pytest_data_fixture):
|
def get_pytest_params(self, pytest_data_fixture):
|
||||||
|
@ -753,6 +838,12 @@ class ImageElementType(ElementType):
|
||||||
@property
|
@property
|
||||||
def serializer_field_overrides(self):
|
def serializer_field_overrides(self):
|
||||||
from baserow.api.user_files.serializers import UserFileSerializer
|
from baserow.api.user_files.serializers import UserFileSerializer
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
ImageThemeConfigBlockType,
|
||||||
|
)
|
||||||
from baserow.core.formula.serializers import FormulaSerializerField
|
from baserow.core.formula.serializers import FormulaSerializerField
|
||||||
|
|
||||||
overrides = {
|
overrides = {
|
||||||
|
@ -769,6 +860,12 @@ class ImageElementType(ElementType):
|
||||||
allow_blank=True,
|
allow_blank=True,
|
||||||
default="",
|
default="",
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="image",
|
||||||
|
theme_config_block_type_name=ImageThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
overrides.update(super().serializer_field_overrides)
|
overrides.update(super().serializer_field_overrides)
|
||||||
|
@ -777,7 +874,13 @@ class ImageElementType(ElementType):
|
||||||
@property
|
@property
|
||||||
def request_serializer_field_overrides(self):
|
def request_serializer_field_overrides(self):
|
||||||
from baserow.api.user_files.serializers import UserFileField
|
from baserow.api.user_files.serializers import UserFileField
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
from baserow.contrib.builder.api.validators import image_file_validation
|
from baserow.contrib.builder.api.validators import image_file_validation
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
ImageThemeConfigBlockType,
|
||||||
|
)
|
||||||
|
|
||||||
overrides = {
|
overrides = {
|
||||||
"image_file": UserFileField(
|
"image_file": UserFileField(
|
||||||
|
@ -798,6 +901,12 @@ class ImageElementType(ElementType):
|
||||||
default=ImageElement._meta.get_field("style_max_width").default,
|
default=ImageElement._meta.get_field("style_max_width").default,
|
||||||
help_text=ImageElement._meta.get_field("style_max_width").help_text,
|
help_text=ImageElement._meta.get_field("style_max_width").help_text,
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="image",
|
||||||
|
theme_config_block_type_name=ImageThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
if super().request_serializer_field_overrides is not None:
|
if super().request_serializer_field_overrides is not None:
|
||||||
overrides.update(super().request_serializer_field_overrides)
|
overrides.update(super().request_serializer_field_overrides)
|
||||||
|
@ -1014,6 +1123,12 @@ class ButtonElementType(ElementType):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer_field_overrides(self):
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.contrib.builder.api.theme.serializers import (
|
||||||
|
DynamicConfigBlockSerializer,
|
||||||
|
)
|
||||||
|
from baserow.contrib.builder.theme.theme_config_block_types import (
|
||||||
|
ButtonThemeConfigBlockType,
|
||||||
|
)
|
||||||
from baserow.core.formula.serializers import FormulaSerializerField
|
from baserow.core.formula.serializers import FormulaSerializerField
|
||||||
|
|
||||||
overrides = {
|
overrides = {
|
||||||
|
@ -1039,6 +1154,12 @@ class ButtonElementType(ElementType):
|
||||||
default="primary",
|
default="primary",
|
||||||
help_text="Button color.",
|
help_text="Button color.",
|
||||||
),
|
),
|
||||||
|
"styles": DynamicConfigBlockSerializer(
|
||||||
|
required=False,
|
||||||
|
property_name="button",
|
||||||
|
theme_config_block_type_name=ButtonThemeConfigBlockType.type,
|
||||||
|
serializer_kwargs={"required": False},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return overrides
|
return overrides
|
||||||
|
|
|
@ -35,22 +35,28 @@ class ElementHandler:
|
||||||
allowed_fields_create = [
|
allowed_fields_create = [
|
||||||
"parent_element_id",
|
"parent_element_id",
|
||||||
"place_in_container",
|
"place_in_container",
|
||||||
"styles",
|
|
||||||
"visibility",
|
"visibility",
|
||||||
|
"styles",
|
||||||
"style_border_top_color",
|
"style_border_top_color",
|
||||||
"style_border_top_size",
|
"style_border_top_size",
|
||||||
"style_padding_top",
|
"style_padding_top",
|
||||||
|
"style_margin_top",
|
||||||
"style_border_bottom_color",
|
"style_border_bottom_color",
|
||||||
"style_border_bottom_size",
|
"style_border_bottom_size",
|
||||||
"style_padding_bottom",
|
"style_padding_bottom",
|
||||||
|
"style_margin_bottom",
|
||||||
"style_border_left_color",
|
"style_border_left_color",
|
||||||
"style_border_left_size",
|
"style_border_left_size",
|
||||||
"style_padding_left",
|
"style_padding_left",
|
||||||
|
"style_margin_left",
|
||||||
"style_border_right_color",
|
"style_border_right_color",
|
||||||
"style_border_right_size",
|
"style_border_right_size",
|
||||||
"style_padding_right",
|
"style_padding_right",
|
||||||
|
"style_margin_right",
|
||||||
"style_background",
|
"style_background",
|
||||||
"style_background_color",
|
"style_background_color",
|
||||||
|
"style_background_file",
|
||||||
|
"style_background_mode",
|
||||||
"style_width",
|
"style_width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -62,17 +68,23 @@ class ElementHandler:
|
||||||
"style_border_top_color",
|
"style_border_top_color",
|
||||||
"style_border_top_size",
|
"style_border_top_size",
|
||||||
"style_padding_top",
|
"style_padding_top",
|
||||||
|
"style_margin_top",
|
||||||
"style_border_bottom_color",
|
"style_border_bottom_color",
|
||||||
"style_border_bottom_size",
|
"style_border_bottom_size",
|
||||||
"style_padding_bottom",
|
"style_padding_bottom",
|
||||||
|
"style_margin_bottom",
|
||||||
"style_border_left_color",
|
"style_border_left_color",
|
||||||
"style_border_left_size",
|
"style_border_left_size",
|
||||||
"style_padding_left",
|
"style_padding_left",
|
||||||
|
"style_margin_left",
|
||||||
"style_border_right_color",
|
"style_border_right_color",
|
||||||
"style_border_right_size",
|
"style_border_right_size",
|
||||||
"style_padding_right",
|
"style_padding_right",
|
||||||
|
"style_margin_right",
|
||||||
"style_background",
|
"style_background",
|
||||||
"style_background_color",
|
"style_background_color",
|
||||||
|
"style_background_file",
|
||||||
|
"style_background_mode",
|
||||||
"style_width",
|
"style_width",
|
||||||
"role_type",
|
"role_type",
|
||||||
"roles",
|
"roles",
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.db import models
|
||||||
from django.db.models import SET_NULL, QuerySet
|
from django.db.models import SET_NULL, QuerySet
|
||||||
|
|
||||||
from baserow.contrib.builder.constants import (
|
from baserow.contrib.builder.constants import (
|
||||||
|
BACKGROUND_IMAGE_MODES,
|
||||||
WIDTHS,
|
WIDTHS,
|
||||||
HorizontalAlignments,
|
HorizontalAlignments,
|
||||||
VerticalAlignments,
|
VerticalAlignments,
|
||||||
|
@ -29,10 +30,12 @@ if TYPE_CHECKING:
|
||||||
class BackgroundTypes(models.TextChoices):
|
class BackgroundTypes(models.TextChoices):
|
||||||
NONE = "none"
|
NONE = "none"
|
||||||
COLOR = "color"
|
COLOR = "color"
|
||||||
|
IMAGE = "image"
|
||||||
|
|
||||||
|
|
||||||
class WidthTypes(models.TextChoices):
|
class WidthTypes(models.TextChoices):
|
||||||
FULL = "full"
|
FULL = "full"
|
||||||
|
FULL_WIDTH = "full-width"
|
||||||
NORMAL = "normal"
|
NORMAL = "normal"
|
||||||
MEDIUM = "medium"
|
MEDIUM = "medium"
|
||||||
SMALL = "small"
|
SMALL = "small"
|
||||||
|
@ -155,6 +158,11 @@ class Element(
|
||||||
style_padding_top = models.PositiveIntegerField(
|
style_padding_top = models.PositiveIntegerField(
|
||||||
default=10, help_text="Padding size of the top border."
|
default=10, help_text="Padding size of the top border."
|
||||||
)
|
)
|
||||||
|
style_margin_top = models.PositiveIntegerField(
|
||||||
|
default=0,
|
||||||
|
help_text="Margin size of the top border.",
|
||||||
|
null=True, # TODO zdm remove me after v1.26
|
||||||
|
)
|
||||||
|
|
||||||
style_border_bottom_color = models.CharField(
|
style_border_bottom_color = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
|
@ -168,6 +176,11 @@ class Element(
|
||||||
style_padding_bottom = models.PositiveIntegerField(
|
style_padding_bottom = models.PositiveIntegerField(
|
||||||
default=10, help_text="Padding size of the bottom border."
|
default=10, help_text="Padding size of the bottom border."
|
||||||
)
|
)
|
||||||
|
style_margin_bottom = models.PositiveIntegerField(
|
||||||
|
default=0,
|
||||||
|
help_text="Margin size of the bottom border.",
|
||||||
|
null=True, # TODO zdm remove me after v1.26
|
||||||
|
)
|
||||||
|
|
||||||
style_border_left_color = models.CharField(
|
style_border_left_color = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
|
@ -181,6 +194,11 @@ class Element(
|
||||||
style_padding_left = models.PositiveIntegerField(
|
style_padding_left = models.PositiveIntegerField(
|
||||||
default=20, help_text="Padding size of the left border."
|
default=20, help_text="Padding size of the left border."
|
||||||
)
|
)
|
||||||
|
style_margin_left = models.PositiveIntegerField(
|
||||||
|
default=0,
|
||||||
|
help_text="Margin size of the left border.",
|
||||||
|
null=True, # TODO zdm remove me after v1.26
|
||||||
|
)
|
||||||
|
|
||||||
style_border_right_color = models.CharField(
|
style_border_right_color = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
|
@ -194,6 +212,11 @@ class Element(
|
||||||
style_padding_right = models.PositiveIntegerField(
|
style_padding_right = models.PositiveIntegerField(
|
||||||
default=20, help_text="Padding size of the right border."
|
default=20, help_text="Padding size of the right border."
|
||||||
)
|
)
|
||||||
|
style_margin_right = models.PositiveIntegerField(
|
||||||
|
default=0,
|
||||||
|
help_text="Margin size of the right border.",
|
||||||
|
null=True, # TODO zdm remove me after v1.26
|
||||||
|
)
|
||||||
|
|
||||||
style_background = models.CharField(
|
style_background = models.CharField(
|
||||||
choices=BackgroundTypes.choices,
|
choices=BackgroundTypes.choices,
|
||||||
|
@ -208,6 +231,22 @@ class Element(
|
||||||
help_text="The background color if `style_background` is color.",
|
help_text="The background color if `style_background` is color.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
style_background_file = models.ForeignKey(
|
||||||
|
UserFile,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="element_background_image_file",
|
||||||
|
help_text="An image file uploaded by the user to be used as element background",
|
||||||
|
)
|
||||||
|
|
||||||
|
style_background_mode = models.CharField(
|
||||||
|
help_text="The mode of the background image",
|
||||||
|
choices=BACKGROUND_IMAGE_MODES.choices,
|
||||||
|
max_length=32,
|
||||||
|
default=BACKGROUND_IMAGE_MODES.FILL,
|
||||||
|
null=True, # TODO zdm remove me after v1.26
|
||||||
|
)
|
||||||
|
|
||||||
style_width = models.CharField(
|
style_width = models.CharField(
|
||||||
choices=WidthTypes.choices,
|
choices=WidthTypes.choices,
|
||||||
default=WidthTypes.NORMAL,
|
default=WidthTypes.NORMAL,
|
||||||
|
|
|
@ -18,6 +18,7 @@ from baserow.core.registry import (
|
||||||
ModelRegistryMixin,
|
ModelRegistryMixin,
|
||||||
Registry,
|
Registry,
|
||||||
)
|
)
|
||||||
|
from baserow.core.user_files.handler import UserFileHandler
|
||||||
from baserow.core.user_sources.constants import DEFAULT_USER_ROLE_PREFIX
|
from baserow.core.user_sources.constants import DEFAULT_USER_ROLE_PREFIX
|
||||||
from baserow.core.user_sources.handler import UserSourceHandler
|
from baserow.core.user_sources.handler import UserSourceHandler
|
||||||
|
|
||||||
|
@ -233,6 +234,14 @@ class ElementType(
|
||||||
if prop_name == "order":
|
if prop_name == "order":
|
||||||
return str(element.order)
|
return str(element.order)
|
||||||
|
|
||||||
|
if prop_name == "style_background_file_id":
|
||||||
|
return UserFileHandler().export_user_file(
|
||||||
|
element.style_background_file,
|
||||||
|
files_zip=files_zip,
|
||||||
|
storage=storage,
|
||||||
|
cache=cache,
|
||||||
|
)
|
||||||
|
|
||||||
return super().serialize_property(
|
return super().serialize_property(
|
||||||
element, prop_name, files_zip=files_zip, storage=storage, cache=cache
|
element, prop_name, files_zip=files_zip, storage=storage, cache=cache
|
||||||
)
|
)
|
||||||
|
@ -268,6 +277,14 @@ class ElementType(
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if prop_name == "style_background_file_id":
|
||||||
|
user_file = UserFileHandler().import_user_file(
|
||||||
|
value, files_zip=files_zip, storage=storage
|
||||||
|
)
|
||||||
|
if user_file:
|
||||||
|
return user_file.id
|
||||||
|
return None
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|
|
@ -0,0 +1,299 @@
|
||||||
|
# Generated by Django 4.2.13 on 2024-07-02 16:58
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import baserow.core.fields
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_element_styles(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
Migrates on model element styles into the style property.
|
||||||
|
"""
|
||||||
|
|
||||||
|
Element = apps.get_model("builder", "element")
|
||||||
|
|
||||||
|
# Set default values for element styles
|
||||||
|
Element.objects.all().update(
|
||||||
|
style_margin_left=0,
|
||||||
|
style_margin_right=0,
|
||||||
|
style_margin_top=0,
|
||||||
|
style_margin_bottom=0,
|
||||||
|
style_background_mode="fill",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("core", "0088_remove_blacklistedtoken_user"),
|
||||||
|
("builder", "0026_add_more_style_properties"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_border_color",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="border",
|
||||||
|
help_text="The border color of buttons",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_border_radius",
|
||||||
|
field=models.SmallIntegerField(default=4, help_text="Button border radius"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_border_size",
|
||||||
|
field=models.SmallIntegerField(default=0, help_text="Button border size"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_font_size",
|
||||||
|
field=models.SmallIntegerField(default=13),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_horizontal_padding",
|
||||||
|
field=models.SmallIntegerField(
|
||||||
|
default=12, help_text="Button horizontal padding"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_hover_border_color",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="border",
|
||||||
|
help_text="The border color of buttons when hovered",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_hover_text_color",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="#ffffffff",
|
||||||
|
help_text="The text color of buttons when hovered",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_text_color",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="#ffffffff",
|
||||||
|
help_text="The text color of buttons",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="buttonthemeconfigblock",
|
||||||
|
name="button_vertical_padding",
|
||||||
|
field=models.SmallIntegerField(
|
||||||
|
default=12, help_text="Button vertical padding"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="colorthemeconfigblock",
|
||||||
|
name="main_error_color",
|
||||||
|
field=models.CharField(default="#FF5A4A", max_length=9),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="colorthemeconfigblock",
|
||||||
|
name="main_success_color",
|
||||||
|
field=models.CharField(default="#12D452", max_length=9),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="colorthemeconfigblock",
|
||||||
|
name="main_warning_color",
|
||||||
|
field=models.CharField(default="#FCC74A", max_length=9),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_background_file",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
help_text="An image file uploaded by the user to be used as element background",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="element_background_image_file",
|
||||||
|
to="core.userfile",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_background_mode",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[("tile", "Tile"), ("fill", "Fill"), ("fit", "Fit")],
|
||||||
|
default="fill",
|
||||||
|
help_text="The mode of the background image",
|
||||||
|
max_length=32,
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_margin_bottom",
|
||||||
|
field=models.PositiveIntegerField(
|
||||||
|
default=0, help_text="Margin size of the bottom border.", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_margin_left",
|
||||||
|
field=models.PositiveIntegerField(
|
||||||
|
default=0, help_text="Margin size of the left border.", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_margin_right",
|
||||||
|
field=models.PositiveIntegerField(
|
||||||
|
default=0, help_text="Margin size of the right border.", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_margin_top",
|
||||||
|
field=models.PositiveIntegerField(
|
||||||
|
default=0, help_text="Margin size of the top border.", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="linkthemeconfigblock",
|
||||||
|
name="link_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="linkthemeconfigblock",
|
||||||
|
name="link_font_size",
|
||||||
|
field=models.SmallIntegerField(default=13),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="typographythemeconfigblock",
|
||||||
|
name="body_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="typographythemeconfigblock",
|
||||||
|
name="heading_1_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="typographythemeconfigblock",
|
||||||
|
name="heading_2_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="typographythemeconfigblock",
|
||||||
|
name="heading_3_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="typographythemeconfigblock",
|
||||||
|
name="heading_4_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="typographythemeconfigblock",
|
||||||
|
name="heading_5_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="typographythemeconfigblock",
|
||||||
|
name="heading_6_font_family",
|
||||||
|
field=models.CharField(default="inter", max_length=250),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_background",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[("none", "None"), ("color", "Color"), ("image", "Image")],
|
||||||
|
default="none",
|
||||||
|
help_text="What type of background the element should have.",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="element",
|
||||||
|
name="style_width",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("full", "Full"),
|
||||||
|
("full-width", "Full Width"),
|
||||||
|
("normal", "Normal"),
|
||||||
|
("medium", "Medium"),
|
||||||
|
("small", "Small"),
|
||||||
|
],
|
||||||
|
default="normal",
|
||||||
|
help_text="Indicates the width of the element.",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="PageThemeConfigBlock",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"page_background_color",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="#ffffffff",
|
||||||
|
help_text="The background color of the page",
|
||||||
|
max_length=20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"page_background_mode",
|
||||||
|
models.CharField(
|
||||||
|
choices=[("tile", "Tile"), ("fill", "Fill"), ("fit", "Fit")],
|
||||||
|
default="tile",
|
||||||
|
help_text="The mode of the background image",
|
||||||
|
max_length=32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"builder",
|
||||||
|
baserow.core.fields.AutoOneToOneField(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="%(class)s",
|
||||||
|
to="builder.builder",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"page_background_file",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="An image file uploaded by the user to be used as page background",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="page_background_image_file",
|
||||||
|
to="core.userfile",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
migrate_element_styles, reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,8 +1,13 @@
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from baserow.contrib.builder.constants import WIDTHS, HorizontalAlignments
|
from baserow.contrib.builder.constants import (
|
||||||
|
BACKGROUND_IMAGE_MODES,
|
||||||
|
WIDTHS,
|
||||||
|
HorizontalAlignments,
|
||||||
|
)
|
||||||
from baserow.core.fields import AutoOneToOneField
|
from baserow.core.fields import AutoOneToOneField
|
||||||
|
from baserow.core.user_files.models import UserFile
|
||||||
|
|
||||||
|
|
||||||
class ThemeConfigBlock(models.Model):
|
class ThemeConfigBlock(models.Model):
|
||||||
|
@ -33,9 +38,16 @@ class ColorThemeConfigBlock(ThemeConfigBlock):
|
||||||
primary_color = models.CharField(max_length=9, default="#5190efff")
|
primary_color = models.CharField(max_length=9, default="#5190efff")
|
||||||
secondary_color = models.CharField(max_length=9, default="#0eaa42ff")
|
secondary_color = models.CharField(max_length=9, default="#0eaa42ff")
|
||||||
border_color = models.CharField(max_length=9, default="#d7d8d9ff")
|
border_color = models.CharField(max_length=9, default="#d7d8d9ff")
|
||||||
|
main_success_color = models.CharField(max_length=9, default="#12D452")
|
||||||
|
main_warning_color = models.CharField(max_length=9, default="#FCC74A")
|
||||||
|
main_error_color = models.CharField(max_length=9, default="#FF5A4A")
|
||||||
|
|
||||||
|
|
||||||
class TypographyThemeConfigBlock(ThemeConfigBlock):
|
class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
|
body_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
body_font_size = models.SmallIntegerField(default=14)
|
body_font_size = models.SmallIntegerField(default=14)
|
||||||
body_text_color = models.CharField(max_length=9, default="#070810ff")
|
body_text_color = models.CharField(max_length=9, default="#070810ff")
|
||||||
body_text_alignment = models.CharField(
|
body_text_alignment = models.CharField(
|
||||||
|
@ -43,6 +55,10 @@ class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
max_length=10,
|
max_length=10,
|
||||||
default=HorizontalAlignments.LEFT,
|
default=HorizontalAlignments.LEFT,
|
||||||
)
|
)
|
||||||
|
heading_1_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
heading_1_font_size = models.SmallIntegerField(default=24)
|
heading_1_font_size = models.SmallIntegerField(default=24)
|
||||||
heading_1_text_color = models.CharField(max_length=9, default="#070810ff")
|
heading_1_text_color = models.CharField(max_length=9, default="#070810ff")
|
||||||
heading_1_text_alignment = models.CharField(
|
heading_1_text_alignment = models.CharField(
|
||||||
|
@ -50,6 +66,10 @@ class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
max_length=10,
|
max_length=10,
|
||||||
default=HorizontalAlignments.LEFT,
|
default=HorizontalAlignments.LEFT,
|
||||||
)
|
)
|
||||||
|
heading_2_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
heading_2_font_size = models.SmallIntegerField(default=20)
|
heading_2_font_size = models.SmallIntegerField(default=20)
|
||||||
heading_2_text_color = models.CharField(max_length=9, default="#070810ff")
|
heading_2_text_color = models.CharField(max_length=9, default="#070810ff")
|
||||||
heading_2_text_alignment = models.CharField(
|
heading_2_text_alignment = models.CharField(
|
||||||
|
@ -57,6 +77,10 @@ class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
max_length=10,
|
max_length=10,
|
||||||
default=HorizontalAlignments.LEFT,
|
default=HorizontalAlignments.LEFT,
|
||||||
)
|
)
|
||||||
|
heading_3_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
heading_3_font_size = models.SmallIntegerField(default=16)
|
heading_3_font_size = models.SmallIntegerField(default=16)
|
||||||
heading_3_text_color = models.CharField(max_length=9, default="#070810ff")
|
heading_3_text_color = models.CharField(max_length=9, default="#070810ff")
|
||||||
heading_3_text_alignment = models.CharField(
|
heading_3_text_alignment = models.CharField(
|
||||||
|
@ -64,6 +88,10 @@ class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
max_length=10,
|
max_length=10,
|
||||||
default=HorizontalAlignments.LEFT,
|
default=HorizontalAlignments.LEFT,
|
||||||
)
|
)
|
||||||
|
heading_4_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
heading_4_font_size = models.SmallIntegerField(default=16)
|
heading_4_font_size = models.SmallIntegerField(default=16)
|
||||||
heading_4_text_color = models.CharField(max_length=9, default="#070810ff")
|
heading_4_text_color = models.CharField(max_length=9, default="#070810ff")
|
||||||
heading_4_text_alignment = models.CharField(
|
heading_4_text_alignment = models.CharField(
|
||||||
|
@ -71,6 +99,10 @@ class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
max_length=10,
|
max_length=10,
|
||||||
default=HorizontalAlignments.LEFT,
|
default=HorizontalAlignments.LEFT,
|
||||||
)
|
)
|
||||||
|
heading_5_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
heading_5_font_size = models.SmallIntegerField(default=14)
|
heading_5_font_size = models.SmallIntegerField(default=14)
|
||||||
heading_5_text_color = models.CharField(max_length=9, default="#070810ff")
|
heading_5_text_color = models.CharField(max_length=9, default="#070810ff")
|
||||||
heading_5_text_alignment = models.CharField(
|
heading_5_text_alignment = models.CharField(
|
||||||
|
@ -78,6 +110,10 @@ class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
max_length=10,
|
max_length=10,
|
||||||
default=HorizontalAlignments.LEFT,
|
default=HorizontalAlignments.LEFT,
|
||||||
)
|
)
|
||||||
|
heading_6_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
heading_6_font_size = models.SmallIntegerField(default=14)
|
heading_6_font_size = models.SmallIntegerField(default=14)
|
||||||
heading_6_text_color = models.CharField(max_length=9, default="#202128")
|
heading_6_text_color = models.CharField(max_length=9, default="#202128")
|
||||||
heading_6_text_alignment = models.CharField(
|
heading_6_text_alignment = models.CharField(
|
||||||
|
@ -88,6 +124,11 @@ class TypographyThemeConfigBlock(ThemeConfigBlock):
|
||||||
|
|
||||||
|
|
||||||
class ButtonThemeConfigBlock(ThemeConfigBlock):
|
class ButtonThemeConfigBlock(ThemeConfigBlock):
|
||||||
|
button_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
|
button_font_size = models.SmallIntegerField(default=13)
|
||||||
button_alignment = models.CharField(
|
button_alignment = models.CharField(
|
||||||
choices=HorizontalAlignments.choices,
|
choices=HorizontalAlignments.choices,
|
||||||
max_length=10,
|
max_length=10,
|
||||||
|
@ -109,15 +150,56 @@ class ButtonThemeConfigBlock(ThemeConfigBlock):
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="The background color of buttons",
|
help_text="The background color of buttons",
|
||||||
)
|
)
|
||||||
|
button_text_color = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
default="#ffffffff",
|
||||||
|
blank=True,
|
||||||
|
help_text="The text color of buttons",
|
||||||
|
)
|
||||||
|
button_border_color = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
default="border",
|
||||||
|
blank=True,
|
||||||
|
help_text="The border color of buttons",
|
||||||
|
)
|
||||||
|
button_border_size = models.SmallIntegerField(
|
||||||
|
default=0, help_text="Button border size"
|
||||||
|
)
|
||||||
|
button_border_radius = models.SmallIntegerField(
|
||||||
|
default=4, help_text="Button border radius"
|
||||||
|
)
|
||||||
|
button_vertical_padding = models.SmallIntegerField(
|
||||||
|
default=12, help_text="Button vertical padding"
|
||||||
|
)
|
||||||
|
button_horizontal_padding = models.SmallIntegerField(
|
||||||
|
default=12, help_text="Button horizontal padding"
|
||||||
|
)
|
||||||
button_hover_background_color = models.CharField(
|
button_hover_background_color = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
default="#96baf6ff",
|
default="#96baf6ff",
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="The background color of buttons when hovered",
|
help_text="The background color of buttons when hovered",
|
||||||
)
|
)
|
||||||
|
button_hover_text_color = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
default="#ffffffff",
|
||||||
|
blank=True,
|
||||||
|
help_text="The text color of buttons when hovered",
|
||||||
|
)
|
||||||
|
button_hover_border_color = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
default="border",
|
||||||
|
blank=True,
|
||||||
|
help_text="The border color of buttons when hovered",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LinkThemeConfigBlock(ThemeConfigBlock):
|
class LinkThemeConfigBlock(ThemeConfigBlock):
|
||||||
|
link_font_family = models.CharField(
|
||||||
|
max_length=250,
|
||||||
|
default="inter",
|
||||||
|
)
|
||||||
|
link_font_size = models.SmallIntegerField(default=13)
|
||||||
link_text_alignment = models.CharField(
|
link_text_alignment = models.CharField(
|
||||||
choices=HorizontalAlignments.choices,
|
choices=HorizontalAlignments.choices,
|
||||||
max_length=10,
|
max_length=10,
|
||||||
|
@ -173,3 +255,31 @@ class ImageThemeConfigBlock(ThemeConfigBlock):
|
||||||
max_length=32,
|
max_length=32,
|
||||||
default=IMAGE_CONSTRAINT_TYPES.CONTAIN,
|
default=IMAGE_CONSTRAINT_TYPES.CONTAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PageThemeConfigBlock(ThemeConfigBlock):
|
||||||
|
"""
|
||||||
|
Theme for pages.
|
||||||
|
"""
|
||||||
|
|
||||||
|
page_background_color = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
default="#ffffffff",
|
||||||
|
blank=True,
|
||||||
|
help_text="The background color of the page",
|
||||||
|
)
|
||||||
|
|
||||||
|
page_background_file = models.ForeignKey(
|
||||||
|
UserFile,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name="page_background_image_file",
|
||||||
|
help_text="An image file uploaded by the user to be used as page background",
|
||||||
|
)
|
||||||
|
|
||||||
|
page_background_mode = models.CharField(
|
||||||
|
help_text="The mode of the background image",
|
||||||
|
choices=BACKGROUND_IMAGE_MODES.choices,
|
||||||
|
max_length=32,
|
||||||
|
default=BACKGROUND_IMAGE_MODES.TILE,
|
||||||
|
)
|
||||||
|
|
|
@ -6,19 +6,18 @@ from django.db.models import QuerySet
|
||||||
from baserow.core.registry import (
|
from baserow.core.registry import (
|
||||||
CustomFieldsInstanceMixin,
|
CustomFieldsInstanceMixin,
|
||||||
CustomFieldsRegistryMixin,
|
CustomFieldsRegistryMixin,
|
||||||
ImportExportMixin,
|
EasyImportExportMixin,
|
||||||
Instance,
|
Instance,
|
||||||
Registry,
|
Registry,
|
||||||
)
|
)
|
||||||
from baserow.core.utils import extract_allowed
|
from baserow.core.utils import extract_allowed
|
||||||
|
|
||||||
from .models import ThemeConfigBlock
|
|
||||||
from .types import ThemeConfigBlockSubClass
|
from .types import ThemeConfigBlockSubClass
|
||||||
|
|
||||||
|
|
||||||
class ThemeConfigBlockType(
|
class ThemeConfigBlockType(
|
||||||
Instance,
|
Instance,
|
||||||
ImportExportMixin[ThemeConfigBlock],
|
EasyImportExportMixin,
|
||||||
CustomFieldsInstanceMixin,
|
CustomFieldsInstanceMixin,
|
||||||
ABC,
|
ABC,
|
||||||
):
|
):
|
||||||
|
@ -33,6 +32,31 @@ class ThemeConfigBlockType(
|
||||||
polymorphic.
|
polymorphic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
parent_property_name = "builder"
|
||||||
|
|
||||||
|
def get_property_names(self):
|
||||||
|
"""
|
||||||
|
We want all properties here to make it easier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return [
|
||||||
|
f.name
|
||||||
|
for f in self.model_class._meta.get_fields()
|
||||||
|
if f.name not in ["builder", "id"]
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def allowed_fields(self):
|
||||||
|
return [
|
||||||
|
f.name
|
||||||
|
for f in self.model_class._meta.get_fields()
|
||||||
|
if f.name not in ["id", "builder"]
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serializer_field_names(self):
|
||||||
|
return self.allowed_fields
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def related_name_in_builder_model(self) -> str:
|
def related_name_in_builder_model(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -42,15 +66,6 @@ class ThemeConfigBlockType(
|
||||||
|
|
||||||
return self.model_class.__name__.lower()
|
return self.model_class.__name__.lower()
|
||||||
|
|
||||||
def export_serialized(self, instance):
|
|
||||||
return {field: getattr(instance, field) for field in self.allowed_fields}
|
|
||||||
|
|
||||||
def import_serialized(self, parent, serialized_values, id_mapping):
|
|
||||||
allowed_values = extract_allowed(serialized_values, self.allowed_fields)
|
|
||||||
theme_config_block = self.model_class(builder=parent, **allowed_values)
|
|
||||||
theme_config_block.save()
|
|
||||||
return theme_config_block
|
|
||||||
|
|
||||||
def update_properties(
|
def update_properties(
|
||||||
self, builder, **kwargs: dict
|
self, builder, **kwargs: dict
|
||||||
) -> Type[ThemeConfigBlockSubClass]:
|
) -> Type[ThemeConfigBlockSubClass]:
|
||||||
|
@ -64,10 +79,14 @@ class ThemeConfigBlockType(
|
||||||
|
|
||||||
instance = getattr(builder, self.related_name_in_builder_model)
|
instance = getattr(builder, self.related_name_in_builder_model)
|
||||||
allowed_values = extract_allowed(kwargs, self.allowed_fields)
|
allowed_values = extract_allowed(kwargs, self.allowed_fields)
|
||||||
|
|
||||||
for key, value in allowed_values.items():
|
for key, value in allowed_values.items():
|
||||||
setattr(instance, key, value)
|
setattr(instance, key, value)
|
||||||
|
|
||||||
instance.save()
|
instance.save()
|
||||||
|
|
||||||
setattr(builder, self.related_name_in_builder_model, instance)
|
setattr(builder, self.related_name_in_builder_model, instance)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
from baserow.core.user_files.handler import UserFileHandler
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
ButtonThemeConfigBlock,
|
ButtonThemeConfigBlock,
|
||||||
ColorThemeConfigBlock,
|
ColorThemeConfigBlock,
|
||||||
ImageThemeConfigBlock,
|
ImageThemeConfigBlock,
|
||||||
LinkThemeConfigBlock,
|
LinkThemeConfigBlock,
|
||||||
|
PageThemeConfigBlock,
|
||||||
|
ThemeConfigBlock,
|
||||||
TypographyThemeConfigBlock,
|
TypographyThemeConfigBlock,
|
||||||
)
|
)
|
||||||
from .registries import ThemeConfigBlockType
|
from .registries import ThemeConfigBlockType
|
||||||
|
@ -11,67 +15,11 @@ from .registries import ThemeConfigBlockType
|
||||||
class ColorThemeConfigBlockType(ThemeConfigBlockType):
|
class ColorThemeConfigBlockType(ThemeConfigBlockType):
|
||||||
type = "color"
|
type = "color"
|
||||||
model_class = ColorThemeConfigBlock
|
model_class = ColorThemeConfigBlock
|
||||||
allowed_fields = [
|
|
||||||
"primary_color",
|
|
||||||
"secondary_color",
|
|
||||||
"border_color",
|
|
||||||
]
|
|
||||||
serializer_field_names = [
|
|
||||||
"primary_color",
|
|
||||||
"secondary_color",
|
|
||||||
"border_color",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class TypographyThemeConfigBlockType(ThemeConfigBlockType):
|
class TypographyThemeConfigBlockType(ThemeConfigBlockType):
|
||||||
type = "typography"
|
type = "typography"
|
||||||
model_class = TypographyThemeConfigBlock
|
model_class = TypographyThemeConfigBlock
|
||||||
allowed_fields = [
|
|
||||||
"body_font_size",
|
|
||||||
"body_text_color",
|
|
||||||
"body_text_alignment",
|
|
||||||
"heading_1_font_size",
|
|
||||||
"heading_1_text_color",
|
|
||||||
"heading_1_text_alignment",
|
|
||||||
"heading_2_font_size",
|
|
||||||
"heading_2_text_color",
|
|
||||||
"heading_2_text_alignment",
|
|
||||||
"heading_3_font_size",
|
|
||||||
"heading_3_text_color",
|
|
||||||
"heading_3_text_alignment",
|
|
||||||
"heading_4_font_size",
|
|
||||||
"heading_4_text_color",
|
|
||||||
"heading_4_text_alignment",
|
|
||||||
"heading_5_font_size",
|
|
||||||
"heading_5_text_color",
|
|
||||||
"heading_5_text_alignment",
|
|
||||||
"heading_6_font_size",
|
|
||||||
"heading_6_text_color",
|
|
||||||
"heading_6_text_alignment",
|
|
||||||
]
|
|
||||||
serializer_field_names = [
|
|
||||||
"body_font_size",
|
|
||||||
"body_text_color",
|
|
||||||
"body_text_alignment",
|
|
||||||
"heading_1_font_size",
|
|
||||||
"heading_1_text_color",
|
|
||||||
"heading_1_text_alignment",
|
|
||||||
"heading_2_font_size",
|
|
||||||
"heading_2_text_color",
|
|
||||||
"heading_2_text_alignment",
|
|
||||||
"heading_3_font_size",
|
|
||||||
"heading_3_text_color",
|
|
||||||
"heading_3_text_alignment",
|
|
||||||
"heading_4_font_size",
|
|
||||||
"heading_4_text_color",
|
|
||||||
"heading_4_text_alignment",
|
|
||||||
"heading_5_font_size",
|
|
||||||
"heading_5_text_color",
|
|
||||||
"heading_5_text_alignment",
|
|
||||||
"heading_6_font_size",
|
|
||||||
"heading_6_text_color",
|
|
||||||
"heading_6_text_alignment",
|
|
||||||
]
|
|
||||||
|
|
||||||
def import_serialized(self, parent, serialized_values, id_mapping):
|
def import_serialized(self, parent, serialized_values, id_mapping):
|
||||||
# Translate from old color property names to new names for compat with templates
|
# Translate from old color property names to new names for compat with templates
|
||||||
|
@ -87,49 +35,91 @@ class TypographyThemeConfigBlockType(ThemeConfigBlockType):
|
||||||
class ButtonThemeConfigBlockType(ThemeConfigBlockType):
|
class ButtonThemeConfigBlockType(ThemeConfigBlockType):
|
||||||
type = "button"
|
type = "button"
|
||||||
model_class = ButtonThemeConfigBlock
|
model_class = ButtonThemeConfigBlock
|
||||||
allowed_fields = [
|
|
||||||
"button_background_color",
|
|
||||||
"button_hover_background_color",
|
|
||||||
"button_text_alignment",
|
|
||||||
"button_alignment",
|
|
||||||
"button_width",
|
|
||||||
]
|
|
||||||
serializer_field_names = [
|
|
||||||
"button_background_color",
|
|
||||||
"button_hover_background_color",
|
|
||||||
"button_text_alignment",
|
|
||||||
"button_alignment",
|
|
||||||
"button_width",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class LinkThemeConfigBlockType(ThemeConfigBlockType):
|
class LinkThemeConfigBlockType(ThemeConfigBlockType):
|
||||||
type = "link"
|
type = "link"
|
||||||
model_class = LinkThemeConfigBlock
|
model_class = LinkThemeConfigBlock
|
||||||
allowed_fields = [
|
|
||||||
"link_text_alignment",
|
|
||||||
"link_text_color",
|
|
||||||
"link_hover_text_color",
|
|
||||||
]
|
|
||||||
serializer_field_names = [
|
|
||||||
"link_text_alignment",
|
|
||||||
"link_text_color",
|
|
||||||
"link_hover_text_color",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ImageThemeConfigBlockType(ThemeConfigBlockType):
|
class ImageThemeConfigBlockType(ThemeConfigBlockType):
|
||||||
type = "image"
|
type = "image"
|
||||||
model_class = ImageThemeConfigBlock
|
model_class = ImageThemeConfigBlock
|
||||||
allowed_fields = [
|
|
||||||
"image_alignment",
|
|
||||||
"image_max_width",
|
class PageThemeConfigBlockType(ThemeConfigBlockType):
|
||||||
"image_max_height",
|
type = "page"
|
||||||
"image_constraint",
|
model_class = PageThemeConfigBlock
|
||||||
]
|
|
||||||
serializer_field_names = [
|
def get_property_names(self):
|
||||||
"image_alignment",
|
"""
|
||||||
"image_max_width",
|
Let's replace the page_background_file property with page_background_file_id.
|
||||||
"image_max_height",
|
"""
|
||||||
"image_constraint",
|
|
||||||
]
|
return [
|
||||||
|
n if n != "page_background_file" else "page_background_file_id"
|
||||||
|
for n in super().get_property_names()
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serializer_field_overrides(self):
|
||||||
|
from baserow.api.user_files.serializers import UserFileField
|
||||||
|
from baserow.contrib.builder.api.validators import image_file_validation
|
||||||
|
|
||||||
|
return {
|
||||||
|
"page_background_file": UserFileField(
|
||||||
|
allow_null=True,
|
||||||
|
required=False,
|
||||||
|
help_text="The image file",
|
||||||
|
validators=[image_file_validation],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
def serialize_property(
|
||||||
|
self,
|
||||||
|
theme_config_block: ThemeConfigBlock,
|
||||||
|
prop_name: str,
|
||||||
|
files_zip=None,
|
||||||
|
storage=None,
|
||||||
|
cache=None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
You can customize the behavior of the serialization of a property with this
|
||||||
|
hook.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if prop_name == "page_background_file_id":
|
||||||
|
return UserFileHandler().export_user_file(
|
||||||
|
theme_config_block.page_background_file,
|
||||||
|
files_zip=files_zip,
|
||||||
|
storage=storage,
|
||||||
|
cache=cache,
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().serialize_property(
|
||||||
|
theme_config_block,
|
||||||
|
prop_name,
|
||||||
|
files_zip=files_zip,
|
||||||
|
storage=storage,
|
||||||
|
cache=cache,
|
||||||
|
)
|
||||||
|
|
||||||
|
def deserialize_property(
|
||||||
|
self,
|
||||||
|
prop_name: str,
|
||||||
|
value,
|
||||||
|
id_mapping,
|
||||||
|
files_zip=None,
|
||||||
|
storage=None,
|
||||||
|
cache=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
if prop_name == "page_background_file_id":
|
||||||
|
user_file = UserFileHandler().import_user_file(
|
||||||
|
value, files_zip=files_zip, storage=storage
|
||||||
|
)
|
||||||
|
if user_file:
|
||||||
|
return user_file.id
|
||||||
|
return None
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
|
@ -20,17 +20,23 @@ class ElementDict(TypedDict):
|
||||||
style_border_top_color: str
|
style_border_top_color: str
|
||||||
style_border_top_size: int
|
style_border_top_size: int
|
||||||
style_padding_top: int
|
style_padding_top: int
|
||||||
|
style_margin_top: int
|
||||||
style_border_bottom_color: str
|
style_border_bottom_color: str
|
||||||
style_border_bottom_size: int
|
style_border_bottom_size: int
|
||||||
style_padding_bottom: int
|
style_padding_bottom: int
|
||||||
|
style_margin_bottom: int
|
||||||
style_border_left_color: str
|
style_border_left_color: str
|
||||||
style_border_left_size: int
|
style_border_left_size: int
|
||||||
style_padding_left: int
|
style_padding_left: int
|
||||||
|
style_margin_left: int
|
||||||
style_border_right_color: str
|
style_border_right_color: str
|
||||||
style_border_right_size: int
|
style_border_right_size: int
|
||||||
style_padding_right: int
|
style_padding_right: int
|
||||||
|
style_margin_right: int
|
||||||
style_background: str
|
style_background: str
|
||||||
style_background_color: str
|
style_background_color: str
|
||||||
|
style_background_file_id: str
|
||||||
|
style_background_mode: str
|
||||||
style_width: str
|
style_width: str
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -389,8 +389,9 @@ class EasyImportExportMixin(Generic[T], ABC):
|
||||||
# The parent property name for the model
|
# The parent property name for the model
|
||||||
parent_property_name: str
|
parent_property_name: str
|
||||||
|
|
||||||
# The name of the id mapping used for import process
|
# The name of the id mapping used for import process. Let it None if you don't need
|
||||||
id_mapping_name: str
|
# this feature.
|
||||||
|
id_mapping_name: Optional[str] = None
|
||||||
|
|
||||||
# The model class to create
|
# The model class to create
|
||||||
model_class: Type[T]
|
model_class: Type[T]
|
||||||
|
@ -417,6 +418,16 @@ class EasyImportExportMixin(Generic[T], ABC):
|
||||||
|
|
||||||
return getattr(instance, prop_name)
|
return getattr(instance, prop_name)
|
||||||
|
|
||||||
|
def get_property_names(self):
|
||||||
|
"""
|
||||||
|
Returns a list of properties to export/import for this type. By default it uses
|
||||||
|
the SerializedDict properties.
|
||||||
|
|
||||||
|
:returns: a list of property names belonging to instances of this type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.SerializedDict.__annotations__.keys()
|
||||||
|
|
||||||
def export_serialized(
|
def export_serialized(
|
||||||
self,
|
self,
|
||||||
instance: T,
|
instance: T,
|
||||||
|
@ -432,9 +443,7 @@ class EasyImportExportMixin(Generic[T], ABC):
|
||||||
:return: The exported instance as serialized dict.
|
:return: The exported instance as serialized dict.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
property_names = self.SerializedDict.__annotations__.keys()
|
serialized = dict(
|
||||||
|
|
||||||
serialized = self.SerializedDict(
|
|
||||||
**{
|
**{
|
||||||
key: self.serialize_property(
|
key: self.serialize_property(
|
||||||
instance,
|
instance,
|
||||||
|
@ -443,7 +452,7 @@ class EasyImportExportMixin(Generic[T], ABC):
|
||||||
storage=storage,
|
storage=storage,
|
||||||
cache=cache,
|
cache=cache,
|
||||||
)
|
)
|
||||||
for key in property_names
|
for key in self.get_property_names()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -516,11 +525,11 @@ class EasyImportExportMixin(Generic[T], ABC):
|
||||||
:return: The created instance.
|
:return: The created instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.id_mapping_name not in id_mapping:
|
if self.id_mapping_name and self.id_mapping_name not in id_mapping:
|
||||||
id_mapping[self.id_mapping_name] = {}
|
id_mapping[self.id_mapping_name] = {}
|
||||||
|
|
||||||
deserialized_properties = {}
|
deserialized_properties = {}
|
||||||
for name in self.SerializedDict.__annotations__.keys():
|
for name in self.get_property_names():
|
||||||
if name in serialized_values and name != f"{self.parent_property_name}_id":
|
if name in serialized_values and name != f"{self.parent_property_name}_id":
|
||||||
deserialized_properties[name] = self.deserialize_property(
|
deserialized_properties[name] = self.deserialize_property(
|
||||||
name,
|
name,
|
||||||
|
@ -533,10 +542,11 @@ class EasyImportExportMixin(Generic[T], ABC):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Remove id key
|
# Remove id key
|
||||||
originale_instance_id = deserialized_properties.pop("id")
|
originale_instance_id = deserialized_properties.pop("id", 0)
|
||||||
|
|
||||||
# Remove type
|
# Remove type if any
|
||||||
deserialized_properties.pop("type")
|
if "type" in deserialized_properties:
|
||||||
|
deserialized_properties.pop("type")
|
||||||
|
|
||||||
# Add the parent
|
# Add the parent
|
||||||
deserialized_properties[self.parent_property_name] = parent
|
deserialized_properties[self.parent_property_name] = parent
|
||||||
|
@ -550,8 +560,11 @@ class EasyImportExportMixin(Generic[T], ABC):
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the created instance to the mapping
|
if self.id_mapping_name:
|
||||||
id_mapping[self.id_mapping_name][originale_instance_id] = created_instance.id
|
# Add the created instance to the mapping
|
||||||
|
id_mapping[self.id_mapping_name][
|
||||||
|
originale_instance_id
|
||||||
|
] = created_instance.id
|
||||||
|
|
||||||
return created_instance
|
return created_instance
|
||||||
|
|
||||||
|
|
|
@ -224,6 +224,25 @@ def test_update_element(api_client, data_fixture):
|
||||||
assert response.json()["level"] == 3
|
assert response.json()["level"] == 3
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_update_element_styles(api_client, data_fixture):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
page = data_fixture.create_builder_page(user=user)
|
||||||
|
element1 = data_fixture.create_builder_heading_element(page=page)
|
||||||
|
|
||||||
|
url = reverse("api:builder:element:item", kwargs={"element_id": element1.id})
|
||||||
|
response = api_client.patch(
|
||||||
|
url,
|
||||||
|
{"styles": {"typography": {"heading_1_text_color": "#CCCCCCCC"}}},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
assert response.status_code == HTTP_200_OK
|
||||||
|
assert response.json()["styles"] == {
|
||||||
|
"typography": {"heading_1_text_color": "#CCCCCCCC"}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_update_element_bad_request(api_client, data_fixture):
|
def test_update_element_bad_request(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
@ -256,6 +275,35 @@ def test_update_element_does_not_exist(api_client, data_fixture):
|
||||||
assert response.json()["error"] == "ERROR_ELEMENT_DOES_NOT_EXIST"
|
assert response.json()["error"] == "ERROR_ELEMENT_DOES_NOT_EXIST"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_update_element_bad_style_property(api_client, data_fixture):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
page = data_fixture.create_builder_page(user=user)
|
||||||
|
element1 = data_fixture.create_builder_heading_element(page=page)
|
||||||
|
|
||||||
|
url = reverse("api:builder:element:item", kwargs={"element_id": element1.id})
|
||||||
|
|
||||||
|
# Bad root property
|
||||||
|
response = api_client.patch(
|
||||||
|
url,
|
||||||
|
{"styles": {"typpography": {"heading_1_text_color": "#CCCCCCCC"}}},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
assert response.status_code == HTTP_200_OK
|
||||||
|
assert response.json()["styles"] == {}
|
||||||
|
|
||||||
|
# Bad theme property
|
||||||
|
response = api_client.patch(
|
||||||
|
url,
|
||||||
|
{"styles": {"typography": {"heading_25_text_color": "#CCCCCCCC"}}},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
assert response.status_code == HTTP_200_OK
|
||||||
|
assert response.json()["styles"] == {"typography": {}}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_move_element_empty_payload(api_client, data_fixture):
|
def test_move_element_empty_payload(api_client, data_fixture):
|
||||||
user, token = data_fixture.create_user_and_token()
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
|
|
@ -151,6 +151,11 @@ def test_get_builder_application(api_client, data_fixture):
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTP_200_OK
|
assert response.status_code == HTTP_200_OK
|
||||||
response_json = response.json()
|
response_json = response.json()
|
||||||
|
|
||||||
|
# Check we have the theme but don't want to check every single property
|
||||||
|
assert response_json["theme"]["body_text_color"] == "#070810ff"
|
||||||
|
del response_json["theme"]
|
||||||
|
|
||||||
assert response_json == {
|
assert response_json == {
|
||||||
"favicon_file": UserFileSerializer(application.favicon_file).data,
|
"favicon_file": UserFileSerializer(application.favicon_file).data,
|
||||||
"id": application.id,
|
"id": application.id,
|
||||||
|
@ -168,44 +173,6 @@ def test_get_builder_application(api_client, data_fixture):
|
||||||
"generative_ai_models_enabled": {},
|
"generative_ai_models_enabled": {},
|
||||||
},
|
},
|
||||||
"pages": [],
|
"pages": [],
|
||||||
"theme": {
|
|
||||||
"body_text_color": "#070810ff",
|
|
||||||
"body_font_size": 14,
|
|
||||||
"body_text_alignment": "left",
|
|
||||||
"primary_color": "#5190efff",
|
|
||||||
"secondary_color": "#0eaa42ff",
|
|
||||||
"border_color": "#d7d8d9ff",
|
|
||||||
"heading_1_font_size": 24,
|
|
||||||
"heading_1_text_color": "#070810ff",
|
|
||||||
"heading_1_text_alignment": "left",
|
|
||||||
"heading_2_font_size": 20,
|
|
||||||
"heading_2_text_color": "#070810ff",
|
|
||||||
"heading_2_text_alignment": "left",
|
|
||||||
"heading_3_font_size": 16,
|
|
||||||
"heading_3_text_color": "#070810ff",
|
|
||||||
"heading_3_text_alignment": "left",
|
|
||||||
"heading_4_font_size": 16,
|
|
||||||
"heading_4_text_color": "#070810ff",
|
|
||||||
"heading_4_text_alignment": "left",
|
|
||||||
"heading_5_font_size": 14,
|
|
||||||
"heading_5_text_color": "#070810ff",
|
|
||||||
"heading_5_text_alignment": "left",
|
|
||||||
"heading_6_font_size": 14,
|
|
||||||
"heading_6_text_color": "#202128",
|
|
||||||
"heading_6_text_alignment": "left",
|
|
||||||
"button_background_color": "primary",
|
|
||||||
"button_hover_background_color": "#96baf6ff",
|
|
||||||
"button_alignment": "left",
|
|
||||||
"button_text_alignment": "center",
|
|
||||||
"button_width": "auto",
|
|
||||||
"image_alignment": "left",
|
|
||||||
"image_constraint": "contain",
|
|
||||||
"image_max_height": None,
|
|
||||||
"image_max_width": 100,
|
|
||||||
"link_text_alignment": "left",
|
|
||||||
"link_text_color": "primary",
|
|
||||||
"link_hover_text_color": "#96baf6ff",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,6 +197,11 @@ def test_list_builder_applications(api_client, data_fixture):
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTP_200_OK
|
assert response.status_code == HTTP_200_OK
|
||||||
response_json = response.json()
|
response_json = response.json()
|
||||||
|
|
||||||
|
# Check we have the theme but don't want to check every single property
|
||||||
|
assert response_json[0]["theme"]["body_text_color"] == "#070810ff"
|
||||||
|
del response_json[0]["theme"]
|
||||||
|
|
||||||
assert response_json == [
|
assert response_json == [
|
||||||
{
|
{
|
||||||
"favicon_file": UserFileSerializer(application.favicon_file).data,
|
"favicon_file": UserFileSerializer(application.favicon_file).data,
|
||||||
|
@ -248,43 +220,5 @@ def test_list_builder_applications(api_client, data_fixture):
|
||||||
"generative_ai_models_enabled": {},
|
"generative_ai_models_enabled": {},
|
||||||
},
|
},
|
||||||
"pages": [],
|
"pages": [],
|
||||||
"theme": {
|
|
||||||
"body_text_color": "#070810ff",
|
|
||||||
"body_font_size": 14,
|
|
||||||
"body_text_alignment": "left",
|
|
||||||
"primary_color": "#5190efff",
|
|
||||||
"secondary_color": "#0eaa42ff",
|
|
||||||
"border_color": "#d7d8d9ff",
|
|
||||||
"heading_1_font_size": 24,
|
|
||||||
"heading_1_text_color": "#070810ff",
|
|
||||||
"heading_1_text_alignment": "left",
|
|
||||||
"heading_2_font_size": 20,
|
|
||||||
"heading_2_text_color": "#070810ff",
|
|
||||||
"heading_2_text_alignment": "left",
|
|
||||||
"heading_3_font_size": 16,
|
|
||||||
"heading_3_text_color": "#070810ff",
|
|
||||||
"heading_3_text_alignment": "left",
|
|
||||||
"heading_4_font_size": 16,
|
|
||||||
"heading_4_text_color": "#070810ff",
|
|
||||||
"heading_4_text_alignment": "left",
|
|
||||||
"heading_5_font_size": 14,
|
|
||||||
"heading_5_text_color": "#070810ff",
|
|
||||||
"heading_5_text_alignment": "left",
|
|
||||||
"heading_6_font_size": 14,
|
|
||||||
"heading_6_text_color": "#202128",
|
|
||||||
"heading_6_text_alignment": "left",
|
|
||||||
"button_background_color": "primary",
|
|
||||||
"button_hover_background_color": "#96baf6ff",
|
|
||||||
"button_alignment": "left",
|
|
||||||
"button_text_alignment": "center",
|
|
||||||
"button_width": "auto",
|
|
||||||
"image_alignment": "left",
|
|
||||||
"image_constraint": "contain",
|
|
||||||
"image_max_height": None,
|
|
||||||
"image_max_width": 100,
|
|
||||||
"link_text_alignment": "left",
|
|
||||||
"link_text_color": "primary",
|
|
||||||
"link_hover_text_color": "#96baf6ff",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -207,22 +207,28 @@ def test_builder_application_export(data_fixture):
|
||||||
"place_in_container": None,
|
"place_in_container": None,
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"font_color": "default",
|
"font_color": "default",
|
||||||
"style_background_color": "#ffffffff",
|
"styles": {},
|
||||||
"style_border_bottom_color": "border",
|
|
||||||
"style_border_bottom_size": 0,
|
|
||||||
"style_border_top_color": "border",
|
"style_border_top_color": "border",
|
||||||
"style_border_top_size": 0,
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
"style_border_left_color": "border",
|
"style_border_left_color": "border",
|
||||||
"style_border_left_size": 0,
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
"style_border_right_color": "border",
|
"style_border_right_color": "border",
|
||||||
"style_border_right_size": 0,
|
"style_border_right_size": 0,
|
||||||
"style_width": "normal",
|
|
||||||
"style_padding_top": 10,
|
|
||||||
"style_padding_bottom": 10,
|
|
||||||
"style_padding_left": 20,
|
|
||||||
"style_padding_right": 20,
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
"style_background": "none",
|
"style_background": "none",
|
||||||
"styles": {},
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
"value": element1.value,
|
"value": element1.value,
|
||||||
"level": element1.level,
|
"level": element1.level,
|
||||||
"alignment": "left",
|
"alignment": "left",
|
||||||
|
@ -236,22 +242,28 @@ def test_builder_application_export(data_fixture):
|
||||||
"parent_element_id": None,
|
"parent_element_id": None,
|
||||||
"place_in_container": None,
|
"place_in_container": None,
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"style_background_color": "#ffffffff",
|
"styles": {},
|
||||||
"style_border_bottom_color": "border",
|
|
||||||
"style_border_bottom_size": 0,
|
|
||||||
"style_border_top_color": "border",
|
"style_border_top_color": "border",
|
||||||
"style_border_top_size": 0,
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
"style_border_left_color": "border",
|
"style_border_left_color": "border",
|
||||||
"style_border_left_size": 0,
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
"style_border_right_color": "border",
|
"style_border_right_color": "border",
|
||||||
"style_border_right_size": 0,
|
"style_border_right_size": 0,
|
||||||
"style_width": "normal",
|
|
||||||
"style_padding_top": 10,
|
|
||||||
"style_padding_bottom": 10,
|
|
||||||
"style_padding_left": 20,
|
|
||||||
"style_padding_right": 20,
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
"style_background": "none",
|
"style_background": "none",
|
||||||
"styles": {},
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
"value": element2.value,
|
"value": element2.value,
|
||||||
"alignment": "left",
|
"alignment": "left",
|
||||||
"roles": [],
|
"roles": [],
|
||||||
|
@ -264,22 +276,28 @@ def test_builder_application_export(data_fixture):
|
||||||
"parent_element_id": None,
|
"parent_element_id": None,
|
||||||
"place_in_container": None,
|
"place_in_container": None,
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"style_background_color": "#ffffffff",
|
"styles": {},
|
||||||
"style_border_bottom_color": "border",
|
|
||||||
"style_border_bottom_size": 0,
|
|
||||||
"style_border_top_color": "border",
|
"style_border_top_color": "border",
|
||||||
"style_border_top_size": 0,
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
"style_border_left_color": "border",
|
"style_border_left_color": "border",
|
||||||
"style_border_left_size": 0,
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
"style_border_right_color": "border",
|
"style_border_right_color": "border",
|
||||||
"style_border_right_size": 0,
|
"style_border_right_size": 0,
|
||||||
"style_width": "normal",
|
|
||||||
"style_padding_top": 10,
|
|
||||||
"style_padding_bottom": 10,
|
|
||||||
"style_padding_left": 20,
|
|
||||||
"style_padding_right": 20,
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
"style_background": "none",
|
"style_background": "none",
|
||||||
"styles": {},
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
"order": str(element_container.order),
|
"order": str(element_container.order),
|
||||||
"column_amount": 3,
|
"column_amount": 3,
|
||||||
"column_gap": 50,
|
"column_gap": 50,
|
||||||
|
@ -293,22 +311,28 @@ def test_builder_application_export(data_fixture):
|
||||||
"parent_element_id": element_container.id,
|
"parent_element_id": element_container.id,
|
||||||
"place_in_container": "0",
|
"place_in_container": "0",
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"style_background_color": "#ffffffff",
|
"styles": {},
|
||||||
"style_border_bottom_color": "border",
|
|
||||||
"style_border_bottom_size": 0,
|
|
||||||
"style_border_top_color": "border",
|
"style_border_top_color": "border",
|
||||||
"style_border_top_size": 0,
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
"style_border_left_color": "border",
|
"style_border_left_color": "border",
|
||||||
"style_border_left_size": 0,
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
"style_border_right_color": "border",
|
"style_border_right_color": "border",
|
||||||
"style_border_right_size": 0,
|
"style_border_right_size": 0,
|
||||||
"style_width": "normal",
|
|
||||||
"style_padding_top": 10,
|
|
||||||
"style_padding_bottom": 10,
|
|
||||||
"style_padding_left": 20,
|
|
||||||
"style_padding_right": 20,
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
"style_background": "none",
|
"style_background": "none",
|
||||||
"styles": {},
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
"order": str(element_inside_container.order),
|
"order": str(element_inside_container.order),
|
||||||
"value": element_inside_container.value,
|
"value": element_inside_container.value,
|
||||||
"alignment": "left",
|
"alignment": "left",
|
||||||
|
@ -368,22 +392,28 @@ def test_builder_application_export(data_fixture):
|
||||||
"place_in_container": None,
|
"place_in_container": None,
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"font_color": "default",
|
"font_color": "default",
|
||||||
"style_background_color": "#ffffffff",
|
"styles": {},
|
||||||
"style_border_bottom_color": "border",
|
|
||||||
"style_border_bottom_size": 0,
|
|
||||||
"style_border_top_color": "border",
|
"style_border_top_color": "border",
|
||||||
"style_border_top_size": 0,
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
"style_border_left_color": "border",
|
"style_border_left_color": "border",
|
||||||
"style_border_left_size": 0,
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
"style_border_right_color": "border",
|
"style_border_right_color": "border",
|
||||||
"style_border_right_size": 0,
|
"style_border_right_size": 0,
|
||||||
"style_width": "normal",
|
|
||||||
"style_padding_top": 10,
|
|
||||||
"style_padding_bottom": 10,
|
|
||||||
"style_padding_left": 20,
|
|
||||||
"style_padding_right": 20,
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
"style_background": "none",
|
"style_background": "none",
|
||||||
"styles": {},
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
"value": element3.value,
|
"value": element3.value,
|
||||||
"level": element3.level,
|
"level": element3.level,
|
||||||
"alignment": "left",
|
"alignment": "left",
|
||||||
|
@ -406,22 +436,28 @@ def test_builder_application_export(data_fixture):
|
||||||
"parent_element_id": None,
|
"parent_element_id": None,
|
||||||
"place_in_container": None,
|
"place_in_container": None,
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"style_background_color": "#ffffffff",
|
"styles": {},
|
||||||
"style_border_bottom_color": "border",
|
|
||||||
"style_border_bottom_size": 0,
|
|
||||||
"style_border_top_color": "border",
|
"style_border_top_color": "border",
|
||||||
"style_border_top_size": 0,
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
"style_border_left_color": "border",
|
"style_border_left_color": "border",
|
||||||
"style_border_left_size": 0,
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
"style_border_right_color": "border",
|
"style_border_right_color": "border",
|
||||||
"style_border_right_size": 0,
|
"style_border_right_size": 0,
|
||||||
"style_width": "normal",
|
|
||||||
"style_padding_top": 10,
|
|
||||||
"style_padding_bottom": 10,
|
|
||||||
"style_padding_left": 20,
|
|
||||||
"style_padding_right": 20,
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
"style_background": "none",
|
"style_background": "none",
|
||||||
"styles": {},
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
"items_per_page": 42,
|
"items_per_page": 42,
|
||||||
"data_source_id": element4.data_source.id,
|
"data_source_id": element4.data_source.id,
|
||||||
"fields": [
|
"fields": [
|
||||||
|
@ -470,42 +506,67 @@ def test_builder_application_export(data_fixture):
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"theme": {
|
"theme": {
|
||||||
"body_text_color": "#070810ff",
|
|
||||||
"body_font_size": 14,
|
|
||||||
"body_text_alignment": "left",
|
|
||||||
"primary_color": "#5190efff",
|
"primary_color": "#5190efff",
|
||||||
"secondary_color": "#0eaa42ff",
|
"secondary_color": "#0eaa42ff",
|
||||||
"border_color": "#d7d8d9ff",
|
"border_color": "#d7d8d9ff",
|
||||||
|
"main_success_color": "#12D452",
|
||||||
|
"main_error_color": "#FF5A4A",
|
||||||
|
"main_warning_color": "#FCC74A",
|
||||||
|
"body_font_family": "inter",
|
||||||
|
"body_font_size": 14,
|
||||||
|
"body_text_color": "#070810ff",
|
||||||
|
"body_text_alignment": "left",
|
||||||
|
"heading_1_font_family": "inter",
|
||||||
"heading_1_font_size": 24,
|
"heading_1_font_size": 24,
|
||||||
"heading_1_text_color": "#070810ff",
|
"heading_1_text_color": "#070810ff",
|
||||||
"heading_1_text_alignment": "left",
|
"heading_1_text_alignment": "left",
|
||||||
|
"heading_2_font_family": "inter",
|
||||||
"heading_2_font_size": 20,
|
"heading_2_font_size": 20,
|
||||||
"heading_2_text_color": "#070810ff",
|
"heading_2_text_color": "#070810ff",
|
||||||
"heading_2_text_alignment": "left",
|
"heading_2_text_alignment": "left",
|
||||||
|
"heading_3_font_family": "inter",
|
||||||
"heading_3_font_size": 16,
|
"heading_3_font_size": 16,
|
||||||
"heading_3_text_color": "#070810ff",
|
"heading_3_text_color": "#070810ff",
|
||||||
"heading_3_text_alignment": "left",
|
"heading_3_text_alignment": "left",
|
||||||
|
"heading_4_font_family": "inter",
|
||||||
"heading_4_font_size": 16,
|
"heading_4_font_size": 16,
|
||||||
"heading_4_text_color": "#070810ff",
|
"heading_4_text_color": "#070810ff",
|
||||||
"heading_4_text_alignment": "left",
|
"heading_4_text_alignment": "left",
|
||||||
|
"heading_5_font_family": "inter",
|
||||||
"heading_5_font_size": 14,
|
"heading_5_font_size": 14,
|
||||||
"heading_5_text_color": "#070810ff",
|
"heading_5_text_color": "#070810ff",
|
||||||
"heading_5_text_alignment": "left",
|
"heading_5_text_alignment": "left",
|
||||||
|
"heading_6_font_family": "inter",
|
||||||
"heading_6_font_size": 14,
|
"heading_6_font_size": 14,
|
||||||
"heading_6_text_color": "#202128",
|
"heading_6_text_color": "#202128",
|
||||||
"heading_6_text_alignment": "left",
|
"heading_6_text_alignment": "left",
|
||||||
"button_background_color": "primary",
|
"button_font_family": "inter",
|
||||||
"button_hover_background_color": "#96baf6ff",
|
"button_font_size": 13,
|
||||||
"button_alignment": "left",
|
"button_alignment": "left",
|
||||||
"button_text_alignment": "center",
|
"button_text_alignment": "center",
|
||||||
"button_width": "auto",
|
"button_width": "auto",
|
||||||
"image_alignment": "left",
|
"button_background_color": "primary",
|
||||||
"image_constraint": "contain",
|
"button_text_color": "#ffffffff",
|
||||||
"image_max_height": None,
|
"button_border_color": "border",
|
||||||
"image_max_width": 100,
|
"button_border_size": 0,
|
||||||
|
"button_border_radius": 4,
|
||||||
|
"button_vertical_padding": 12,
|
||||||
|
"button_horizontal_padding": 12,
|
||||||
|
"button_hover_background_color": "#96baf6ff",
|
||||||
|
"button_hover_text_color": "#ffffffff",
|
||||||
|
"button_hover_border_color": "border",
|
||||||
|
"link_font_family": "inter",
|
||||||
|
"link_font_size": 13,
|
||||||
"link_text_alignment": "left",
|
"link_text_alignment": "left",
|
||||||
"link_text_color": "primary",
|
"link_text_color": "primary",
|
||||||
"link_hover_text_color": "#96baf6ff",
|
"link_hover_text_color": "#96baf6ff",
|
||||||
|
"image_alignment": "left",
|
||||||
|
"image_max_width": 100,
|
||||||
|
"image_max_height": None,
|
||||||
|
"image_constraint": "contain",
|
||||||
|
"page_background_color": "#ffffffff",
|
||||||
|
"page_background_file_id": None,
|
||||||
|
"page_background_mode": "tile",
|
||||||
},
|
},
|
||||||
"id": builder.id,
|
"id": builder.id,
|
||||||
"name": builder.name,
|
"name": builder.name,
|
||||||
|
@ -514,6 +575,433 @@ def test_builder_application_export(data_fixture):
|
||||||
"favicon_file": None,
|
"favicon_file": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test = {
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "search",
|
||||||
|
"order": 1,
|
||||||
|
"path": "search",
|
||||||
|
"path_params": {},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"order": "1.00000000000000000000",
|
||||||
|
"type": "heading",
|
||||||
|
"parent_element_id": None,
|
||||||
|
"place_in_container": None,
|
||||||
|
"visibility": "all",
|
||||||
|
"role_type": "allow_all",
|
||||||
|
"roles": [],
|
||||||
|
"styles": {},
|
||||||
|
"style_border_top_color": "border",
|
||||||
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
|
"style_border_left_color": "border",
|
||||||
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
|
"style_border_right_color": "border",
|
||||||
|
"style_border_right_size": 0,
|
||||||
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
|
"style_background": "none",
|
||||||
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
|
"value": "foo",
|
||||||
|
"font_color": "default",
|
||||||
|
"level": 2,
|
||||||
|
"alignment": "left",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"order": "2.00000000000000000000",
|
||||||
|
"type": "text",
|
||||||
|
"parent_element_id": None,
|
||||||
|
"place_in_container": None,
|
||||||
|
"visibility": "all",
|
||||||
|
"role_type": "allow_all",
|
||||||
|
"roles": [],
|
||||||
|
"styles": {},
|
||||||
|
"style_border_top_color": "border",
|
||||||
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
|
"style_border_left_color": "border",
|
||||||
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
|
"style_border_right_color": "border",
|
||||||
|
"style_border_right_size": 0,
|
||||||
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
|
"style_background": "none",
|
||||||
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
|
"value": "",
|
||||||
|
"alignment": "left",
|
||||||
|
"format": "plain",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"order": "3.00000000000000000000",
|
||||||
|
"type": "column",
|
||||||
|
"parent_element_id": None,
|
||||||
|
"place_in_container": None,
|
||||||
|
"visibility": "all",
|
||||||
|
"role_type": "allow_all",
|
||||||
|
"roles": [],
|
||||||
|
"styles": {},
|
||||||
|
"style_border_top_color": "border",
|
||||||
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
|
"style_border_left_color": "border",
|
||||||
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
|
"style_border_right_color": "border",
|
||||||
|
"style_border_right_size": 0,
|
||||||
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
|
"style_background": "none",
|
||||||
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
|
"column_amount": 3,
|
||||||
|
"column_gap": 50,
|
||||||
|
"alignment": "top",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"order": "4.00000000000000000000",
|
||||||
|
"type": "text",
|
||||||
|
"parent_element_id": 16,
|
||||||
|
"place_in_container": "0",
|
||||||
|
"visibility": "all",
|
||||||
|
"role_type": "allow_all",
|
||||||
|
"roles": [],
|
||||||
|
"styles": {},
|
||||||
|
"style_border_top_color": "border",
|
||||||
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
|
"style_border_left_color": "border",
|
||||||
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
|
"style_border_right_color": "border",
|
||||||
|
"style_border_right_size": 0,
|
||||||
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
|
"style_background": "none",
|
||||||
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
|
"value": "",
|
||||||
|
"alignment": "left",
|
||||||
|
"format": "plain",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"data_sources": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "source 1",
|
||||||
|
"order": "1.00000000000000000000",
|
||||||
|
"service": {
|
||||||
|
"id": 1,
|
||||||
|
"integration_id": 2,
|
||||||
|
"type": "local_baserow_get_row",
|
||||||
|
"table_id": None,
|
||||||
|
"view_id": None,
|
||||||
|
"filter_type": "AND",
|
||||||
|
"filters": [],
|
||||||
|
"row_id": "",
|
||||||
|
"search_query": "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"workflow_actions": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "notification",
|
||||||
|
"order": 0,
|
||||||
|
"page_id": 3,
|
||||||
|
"element_id": 13,
|
||||||
|
"event": "click",
|
||||||
|
"title": "there",
|
||||||
|
"description": "hello",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "index",
|
||||||
|
"order": 2,
|
||||||
|
"path": "index",
|
||||||
|
"path_params": {},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"order": "1.00000000000000000000",
|
||||||
|
"type": "heading",
|
||||||
|
"parent_element_id": None,
|
||||||
|
"place_in_container": None,
|
||||||
|
"visibility": "all",
|
||||||
|
"role_type": "allow_all",
|
||||||
|
"roles": [],
|
||||||
|
"styles": {},
|
||||||
|
"style_border_top_color": "border",
|
||||||
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
|
"style_border_left_color": "border",
|
||||||
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
|
"style_border_right_color": "border",
|
||||||
|
"style_border_right_size": 0,
|
||||||
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
|
"style_background": "none",
|
||||||
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
|
"value": "",
|
||||||
|
"font_color": "default",
|
||||||
|
"level": 1,
|
||||||
|
"alignment": "left",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"order": "2.00000000000000000000",
|
||||||
|
"type": "table",
|
||||||
|
"parent_element_id": None,
|
||||||
|
"place_in_container": None,
|
||||||
|
"visibility": "all",
|
||||||
|
"role_type": "allow_all",
|
||||||
|
"roles": [],
|
||||||
|
"styles": {},
|
||||||
|
"style_border_top_color": "border",
|
||||||
|
"style_border_top_size": 0,
|
||||||
|
"style_padding_top": 10,
|
||||||
|
"style_margin_top": 0,
|
||||||
|
"style_border_bottom_color": "border",
|
||||||
|
"style_border_bottom_size": 0,
|
||||||
|
"style_padding_bottom": 10,
|
||||||
|
"style_margin_bottom": 0,
|
||||||
|
"style_border_left_color": "border",
|
||||||
|
"style_border_left_size": 0,
|
||||||
|
"style_padding_left": 20,
|
||||||
|
"style_margin_left": 0,
|
||||||
|
"style_border_right_color": "border",
|
||||||
|
"style_border_right_size": 0,
|
||||||
|
"style_padding_right": 20,
|
||||||
|
"style_margin_right": 0,
|
||||||
|
"style_background": "none",
|
||||||
|
"style_background_color": "#ffffffff",
|
||||||
|
"style_background_file_id": None,
|
||||||
|
"style_background_mode": "fill",
|
||||||
|
"style_width": "normal",
|
||||||
|
"data_source_id": 3,
|
||||||
|
"items_per_page": 42,
|
||||||
|
"button_load_more_label": "",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"uid": "447cbec7-c422-42eb-bd50-204b53453330",
|
||||||
|
"name": "Field 1",
|
||||||
|
"type": "text",
|
||||||
|
"config": {"value": "get('test1')"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "44446a1c-841f-47ba-b1df-e902cc50c6ed",
|
||||||
|
"name": "Field 2",
|
||||||
|
"type": "text",
|
||||||
|
"config": {"value": "get('test2')"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "960aef1f-a894-4003-8cf2-36da3b9c798b",
|
||||||
|
"name": "Field 3",
|
||||||
|
"type": "text",
|
||||||
|
"config": {"value": "get('test3')"},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"button_color": "primary",
|
||||||
|
"orientation": {
|
||||||
|
"tablet": "horizontal",
|
||||||
|
"desktop": "horizontal",
|
||||||
|
"smartphone": "horizontal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"data_sources": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "source 2",
|
||||||
|
"order": "1.00000000000000000000",
|
||||||
|
"service": {
|
||||||
|
"id": 2,
|
||||||
|
"integration_id": 2,
|
||||||
|
"type": "local_baserow_get_row",
|
||||||
|
"table_id": None,
|
||||||
|
"view_id": None,
|
||||||
|
"filter_type": "AND",
|
||||||
|
"filters": [],
|
||||||
|
"row_id": "",
|
||||||
|
"search_query": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "source 3",
|
||||||
|
"order": "2.00000000000000000000",
|
||||||
|
"service": {
|
||||||
|
"id": 3,
|
||||||
|
"integration_id": 2,
|
||||||
|
"type": "local_baserow_list_rows",
|
||||||
|
"table_id": None,
|
||||||
|
"view_id": None,
|
||||||
|
"search_query": "",
|
||||||
|
"filter_type": "AND",
|
||||||
|
"filters": [],
|
||||||
|
"sortings": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"workflow_actions": [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"integrations": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "test",
|
||||||
|
"order": "1.00000000000000000000",
|
||||||
|
"type": "local_baserow",
|
||||||
|
"authorized_user": "jennifer92@example.net",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme": {
|
||||||
|
"id": 1,
|
||||||
|
"primary_color": "#5190efff",
|
||||||
|
"secondary_color": "#0eaa42ff",
|
||||||
|
"border_color": "#d7d8d9ff",
|
||||||
|
"main_success_color": "#12D452",
|
||||||
|
"main_error_color": "#FF5A4A",
|
||||||
|
"main_warning_color": "#FCC74A",
|
||||||
|
"body_font_family": "inter",
|
||||||
|
"body_font_size": 14,
|
||||||
|
"body_text_color": "#070810ff",
|
||||||
|
"body_text_alignment": "left",
|
||||||
|
"heading_1_font_family": "inter",
|
||||||
|
"heading_1_font_size": 24,
|
||||||
|
"heading_1_text_color": "#070810ff",
|
||||||
|
"heading_1_text_alignment": "left",
|
||||||
|
"heading_2_font_family": "inter",
|
||||||
|
"heading_2_font_size": 20,
|
||||||
|
"heading_2_text_color": "#070810ff",
|
||||||
|
"heading_2_text_alignment": "left",
|
||||||
|
"heading_3_font_family": "inter",
|
||||||
|
"heading_3_font_size": 16,
|
||||||
|
"heading_3_text_color": "#070810ff",
|
||||||
|
"heading_3_text_alignment": "left",
|
||||||
|
"heading_4_font_family": "inter",
|
||||||
|
"heading_4_font_size": 16,
|
||||||
|
"heading_4_text_color": "#070810ff",
|
||||||
|
"heading_4_text_alignment": "left",
|
||||||
|
"heading_5_font_family": "inter",
|
||||||
|
"heading_5_font_size": 14,
|
||||||
|
"heading_5_text_color": "#070810ff",
|
||||||
|
"heading_5_text_alignment": "left",
|
||||||
|
"heading_6_font_family": "inter",
|
||||||
|
"heading_6_font_size": 14,
|
||||||
|
"heading_6_text_color": "#202128",
|
||||||
|
"heading_6_text_alignment": "left",
|
||||||
|
"button_font_family": "inter",
|
||||||
|
"button_font_size": 13,
|
||||||
|
"button_alignment": "left",
|
||||||
|
"button_text_alignment": "center",
|
||||||
|
"button_width": "auto",
|
||||||
|
"button_background_color": "primary",
|
||||||
|
"button_text_color": "#ffffffff",
|
||||||
|
"button_border_color": "border",
|
||||||
|
"button_border_size": 0,
|
||||||
|
"button_border_radius": 4,
|
||||||
|
"button_vertical_padding": 12,
|
||||||
|
"button_horizontal_padding": 12,
|
||||||
|
"button_hover_background_color": "#96baf6ff",
|
||||||
|
"button_hover_text_color": "#ffffffff",
|
||||||
|
"button_hover_border_color": "border",
|
||||||
|
"link_font_family": "inter",
|
||||||
|
"link_font_size": 13,
|
||||||
|
"link_text_alignment": "left",
|
||||||
|
"link_text_color": "primary",
|
||||||
|
"link_hover_text_color": "#96baf6ff",
|
||||||
|
"image_alignment": "left",
|
||||||
|
"image_max_width": 100,
|
||||||
|
"image_max_height": None,
|
||||||
|
"image_constraint": "contain",
|
||||||
|
"page_background_color": "#ffffffff",
|
||||||
|
"page_background_file_id": None,
|
||||||
|
"page_background_mode": "tile",
|
||||||
|
},
|
||||||
|
"user_sources": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "",
|
||||||
|
"order": "1.00000000000000000000",
|
||||||
|
"type": "local_baserow",
|
||||||
|
"uid": "12345678123456781234567812345678",
|
||||||
|
"integration_id": 2,
|
||||||
|
"auth_providers": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "local_baserow_password",
|
||||||
|
"domain": None,
|
||||||
|
"enabled": True,
|
||||||
|
"password_field_id": None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table_id": None,
|
||||||
|
"email_field_id": None,
|
||||||
|
"name_field_id": None,
|
||||||
|
"role_field_id": None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"favicon_file": None,
|
||||||
|
"id": 5,
|
||||||
|
"name": "Monica Baldwin",
|
||||||
|
"order": 0,
|
||||||
|
"type": "builder",
|
||||||
|
}
|
||||||
|
|
||||||
assert serialized == reference
|
assert serialized == reference
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"type": "feature",
|
||||||
|
"message": "[Builder] More styles of the builder elements can be customized",
|
||||||
|
"issue_number": 2388,
|
||||||
|
"bullet_points": [],
|
||||||
|
"created_at": "2024-07-04"
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<Dropdown :value="value" fixed-items small @input="$emit('input', $event)">
|
||||||
|
<DropdownItem
|
||||||
|
v-for="fontFamily in fontFamilies"
|
||||||
|
:key="fontFamily.getType()"
|
||||||
|
:value="fontFamily.getType()"
|
||||||
|
:name="fontFamily.name"
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FontFamilySelector',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'Inter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fontFamilies() {
|
||||||
|
return Object.values(this.$registry.getAll('fontFamily')).sort((a, b) =>
|
||||||
|
a.name.localeCompare(b.name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
40
web-frontend/modules/builder/components/PaddingSelector.vue
Normal file
40
web-frontend/modules/builder/components/PaddingSelector.vue
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<div class="padding-selector">
|
||||||
|
<FormInput
|
||||||
|
small
|
||||||
|
:value="value.horizontal"
|
||||||
|
type="number"
|
||||||
|
remove-number-input-controls
|
||||||
|
:to-value="(val) => parseInt(val)"
|
||||||
|
class="padding-selector__input"
|
||||||
|
icon-right="iconoir-horizontal-split"
|
||||||
|
@input="$emit('input', { horizontal: $event, vertical: value.vertical })"
|
||||||
|
@blur="$emit('blur')"
|
||||||
|
/>
|
||||||
|
<FormInput
|
||||||
|
small
|
||||||
|
:value="value.vertical"
|
||||||
|
type="number"
|
||||||
|
remove-number-input-controls
|
||||||
|
:to-value="(val) => parseInt(val)"
|
||||||
|
class="padding-selector__input"
|
||||||
|
icon-right="iconoir-vertical-split"
|
||||||
|
@input="
|
||||||
|
$emit('input', { horizontal: value.horizontal, vertical: $event })
|
||||||
|
"
|
||||||
|
@blur="$emit('blur')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PaddingSelector',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<FormInput
|
||||||
|
small
|
||||||
|
:value="value"
|
||||||
|
type="number"
|
||||||
|
remove-number-input-controls
|
||||||
|
:to-value="(val) => parseInt(val)"
|
||||||
|
:style="{
|
||||||
|
width: '100px',
|
||||||
|
}"
|
||||||
|
@input="$emit('input', $event)"
|
||||||
|
@blur="$emit('blur')"
|
||||||
|
>
|
||||||
|
<template #suffix>px</template>
|
||||||
|
</FormInput>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PixelValueSelector',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -9,7 +9,7 @@
|
||||||
}"
|
}"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<span><slot></slot></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<form class="table-element-form" @submit.prevent @keydown.enter.prevent>
|
<form class="table-element-form" @submit.prevent @keydown.enter.prevent>
|
||||||
<CustomStyle
|
|
||||||
v-model="values.styles"
|
|
||||||
style-key="button"
|
|
||||||
:config-block-types="['button']"
|
|
||||||
:theme="builder.theme"
|
|
||||||
:element="values"
|
|
||||||
/>
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
class="margin-bottom-2"
|
class="margin-bottom-2"
|
||||||
small-label
|
small-label
|
||||||
|
@ -53,7 +46,6 @@
|
||||||
:config-block-types="['button']"
|
:config-block-types="['button']"
|
||||||
:theme="builder.theme"
|
:theme="builder.theme"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ApplicationBuilderFormulaInputGroup
|
<ApplicationBuilderFormulaInputGroup
|
||||||
v-model="values.button_load_more_label"
|
v-model="values.button_load_more_label"
|
||||||
:label="$t('tableElementForm.buttonLoadMoreLabel')"
|
:label="$t('tableElementForm.buttonLoadMoreLabel')"
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
style-key="typography"
|
style-key="typography"
|
||||||
:config-block-types="['typography']"
|
:config-block-types="['typography']"
|
||||||
:theme="builder.theme"
|
:theme="builder.theme"
|
||||||
|
:extra-args="{ onlyBody: values.format === TEXT_FORMAT_TYPES.PLAIN }"
|
||||||
/>
|
/>
|
||||||
<ApplicationBuilderFormulaInputGroup
|
<ApplicationBuilderFormulaInputGroup
|
||||||
v-model="values.value"
|
v-model="values.value"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
/>
|
/>
|
||||||
<Context ref="context">
|
<Context ref="context">
|
||||||
<div v-auto-overflow-scroll class="custom-style__config-blocks">
|
<div v-auto-overflow-scroll class="custom-style__config-blocks">
|
||||||
|
<h2>{{ $t('customStyle.themeOverrides') }}</h2>
|
||||||
<div
|
<div
|
||||||
v-for="(themeConfigBlock, index) in themeConfigBlocks"
|
v-for="(themeConfigBlock, index) in themeConfigBlocks"
|
||||||
:key="themeConfigBlock.getType()"
|
:key="themeConfigBlock.getType()"
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
</h2>
|
</h2>
|
||||||
<ThemeConfigBlock
|
<ThemeConfigBlock
|
||||||
:theme="theme"
|
:theme="theme"
|
||||||
:default-values="value[styleKey] || {}"
|
:default-values="value?.[styleKey]"
|
||||||
:preview="false"
|
:preview="false"
|
||||||
:theme-config-block-type="themeConfigBlock"
|
:theme-config-block-type="themeConfigBlock"
|
||||||
:class="{ 'margin-top-3': index >= 1 }"
|
:class="{ 'margin-top-3': index >= 1 }"
|
||||||
|
|
|
@ -1,61 +1,97 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit.prevent>
|
<form @submit.prevent>
|
||||||
<StyleBoxForm
|
<FormSection v-if="isStyleAllowed('style_background')">
|
||||||
v-for="{ name, label } in borders"
|
<FormGroup
|
||||||
:key="name"
|
v-if="isStyleAllowed('style_background')"
|
||||||
v-model="boxStyles[name]"
|
:label="$t('defaultStyleForm.backgroundLabel')"
|
||||||
:label="label"
|
small-label
|
||||||
:padding-is-allowed="isStyleAllowed(`style_padding_${name}`)"
|
required
|
||||||
:border-is-allowed="isStyleAllowed(`style_border_${name}`)"
|
class="margin-bottom-1"
|
||||||
/>
|
>
|
||||||
<FormGroup
|
<RadioGroup
|
||||||
v-if="isStyleAllowed('style_background')"
|
v-model="values.style_background"
|
||||||
class="margin-bottom-2"
|
type="button"
|
||||||
small-label
|
:options="backgroundTypes"
|
||||||
required
|
|
||||||
:label="$t('defaultStyleForm.backgroundLabel')"
|
|
||||||
>
|
|
||||||
<Dropdown v-model="values.style_background">
|
|
||||||
<DropdownItem
|
|
||||||
v-for="type in Object.values(BACKGROUND_TYPES)"
|
|
||||||
:key="type.value"
|
|
||||||
:name="$t(type.name)"
|
|
||||||
:value="type.value"
|
|
||||||
/>
|
/>
|
||||||
</Dropdown>
|
</FormGroup>
|
||||||
<ColorInputGroup
|
<FormGroup
|
||||||
v-if="
|
v-if="
|
||||||
values.style_background === BACKGROUND_TYPES.COLOR.value &&
|
values.style_background === BACKGROUND_TYPES.COLOR &&
|
||||||
isStyleAllowed('style_background_color')
|
isStyleAllowed('style_background_color')
|
||||||
"
|
"
|
||||||
v-model="values.style_background_color"
|
class="margin-bottom-1"
|
||||||
label-after
|
small-label
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<ColorInput
|
||||||
|
v-model="values.style_background_color"
|
||||||
|
small
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
v-if="isStyleAllowed('style_background_file')"
|
||||||
|
:label="$t('defaultStyleForm.backgroundImage')"
|
||||||
|
small-label
|
||||||
|
required
|
||||||
class="margin-top-2"
|
class="margin-top-2"
|
||||||
:label="$t('defaultStyleForm.backgroundColor')"
|
>
|
||||||
:color-variables="colorVariables"
|
<ImageInput v-model="values.style_background_file" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
v-if="
|
||||||
|
isStyleAllowed('style_background_mode') &&
|
||||||
|
values.style_background_file
|
||||||
|
"
|
||||||
|
:label="$t('defaultStyleForm.backgroundImageMode')"
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<RadioGroup
|
||||||
|
v-model="values.style_background_mode"
|
||||||
|
type="button"
|
||||||
|
:options="backgroundModes"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</FormSection>
|
||||||
|
<FormSection v-if="isStyleAllowed('style_width')">
|
||||||
|
<FormGroup
|
||||||
|
:label="$t('defaultStyleForm.widthLabel')"
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
class="margin-bottom-1"
|
||||||
|
>
|
||||||
|
<Dropdown v-model="values.style_width">
|
||||||
|
<DropdownItem
|
||||||
|
v-for="type in Object.values(WIDTH_TYPES)"
|
||||||
|
:key="type.value"
|
||||||
|
:name="$t(type.name)"
|
||||||
|
:value="type.value"
|
||||||
|
>
|
||||||
|
</DropdownItem>
|
||||||
|
</Dropdown>
|
||||||
|
</FormGroup>
|
||||||
|
</FormSection>
|
||||||
|
<FormSection v-for="{ name, label } in borders" :key="name" :title="label">
|
||||||
|
<StyleBoxForm
|
||||||
|
v-model="boxStyles[name]"
|
||||||
|
:padding-is-allowed="isStyleAllowed(`style_padding_${name}`)"
|
||||||
|
:border-is-allowed="isStyleAllowed(`style_border_${name}`)"
|
||||||
|
:margin-is-allowed="isStyleAllowed(`style_margin_${name}`)"
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormSection>
|
||||||
<FormGroup
|
|
||||||
v-if="isStyleAllowed('style_width')"
|
|
||||||
:label="$t('defaultStyleForm.widthLabel')"
|
|
||||||
small-label
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<Dropdown v-model="values.style_width">
|
|
||||||
<DropdownItem
|
|
||||||
v-for="type in Object.values(WIDTH_TYPES)"
|
|
||||||
:key="type.value"
|
|
||||||
:name="$t(type.name)"
|
|
||||||
:value="type.value"
|
|
||||||
></DropdownItem> </Dropdown
|
|
||||||
></FormGroup>
|
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import StyleBoxForm from '@baserow/modules/builder/components/elements/components/forms/style/StyleBoxForm'
|
import StyleBoxForm from '@baserow/modules/builder/components/elements/components/forms/style/StyleBoxForm'
|
||||||
import styleForm from '@baserow/modules/builder/mixins/styleForm'
|
import styleForm from '@baserow/modules/builder/mixins/styleForm'
|
||||||
import { BACKGROUND_TYPES, WIDTH_TYPES } from '@baserow/modules/builder/enums'
|
import {
|
||||||
|
BACKGROUND_TYPES,
|
||||||
|
WIDTH_TYPES,
|
||||||
|
BACKGROUND_MODES,
|
||||||
|
} from '@baserow/modules/builder/enums'
|
||||||
|
import { IMAGE_FILE_TYPES } from '@baserow/modules/core/enums'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { StyleBoxForm },
|
components: { StyleBoxForm },
|
||||||
|
@ -63,6 +99,37 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
BACKGROUND_TYPES: () => BACKGROUND_TYPES,
|
BACKGROUND_TYPES: () => BACKGROUND_TYPES,
|
||||||
WIDTH_TYPES: () => WIDTH_TYPES,
|
WIDTH_TYPES: () => WIDTH_TYPES,
|
||||||
|
backgroundTypes() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundTypes.none'),
|
||||||
|
value: BACKGROUND_TYPES.NONE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundTypes.color'),
|
||||||
|
value: BACKGROUND_TYPES.COLOR,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
backgroundModes() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundModes.fill'),
|
||||||
|
value: BACKGROUND_MODES.FILL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundModes.fit'),
|
||||||
|
value: BACKGROUND_MODES.FIT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundModes.tile'),
|
||||||
|
value: BACKGROUND_MODES.TILE,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
IMAGE_FILE_TYPES() {
|
||||||
|
return IMAGE_FILE_TYPES
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,53 +1,52 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit.prevent>
|
<form @submit.prevent>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
class="style-box-form__control margin-bottom-2"
|
v-if="borderIsAllowed"
|
||||||
:label="label"
|
horizontal
|
||||||
|
class="margin-bottom-1"
|
||||||
small-label
|
small-label
|
||||||
required
|
required
|
||||||
:error="error"
|
:label="$t('styleBoxForm.borderColor')"
|
||||||
>
|
>
|
||||||
<div
|
<ColorInput
|
||||||
v-if="borderIsAllowed || paddingIsAllowed"
|
|
||||||
class="row margin-bottom-2"
|
|
||||||
style="--gap: 6px"
|
|
||||||
>
|
|
||||||
<div v-if="borderIsAllowed" class="col col-4">
|
|
||||||
<div class="margin-bottom-1">
|
|
||||||
{{ $t('styleBoxForm.borderLabel') }}
|
|
||||||
</div>
|
|
||||||
<FormInput
|
|
||||||
v-model="values.border_size"
|
|
||||||
size="large"
|
|
||||||
type="number"
|
|
||||||
:min="0"
|
|
||||||
:max="200"
|
|
||||||
:error="error"
|
|
||||||
@blur="$v.values.border_size.$touch()"
|
|
||||||
></FormInput>
|
|
||||||
</div>
|
|
||||||
<div v-if="paddingIsAllowed" class="col col-4">
|
|
||||||
<div class="margin-bottom-1">
|
|
||||||
{{ $t('styleBoxForm.paddingLabel') }}
|
|
||||||
</div>
|
|
||||||
<FormInput
|
|
||||||
v-model="values.padding"
|
|
||||||
size="large"
|
|
||||||
type="number"
|
|
||||||
:error="error"
|
|
||||||
@blur="$v.values.padding.$touch()"
|
|
||||||
></FormInput>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ColorInputGroup
|
|
||||||
v-if="borderIsAllowed"
|
|
||||||
v-model="values.border_color"
|
v-model="values.border_color"
|
||||||
label-after
|
small
|
||||||
class="margin-top-2"
|
|
||||||
:label="$t('styleBoxForm.borderLabel')"
|
|
||||||
:color-variables="colorVariables"
|
:color-variables="colorVariables"
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
v-if="borderIsAllowed"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
:label="$t('styleBoxForm.borderLabel')"
|
||||||
|
horizontal
|
||||||
|
:error-message="sizeError"
|
||||||
|
>
|
||||||
|
<PixelValueSelector v-model="values.border_size" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
v-if="paddingIsAllowed"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
:label="$t('styleBoxForm.paddingLabel')"
|
||||||
|
horizontal
|
||||||
|
:error-message="paddingError"
|
||||||
|
>
|
||||||
|
<PixelValueSelector v-model="values.padding" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
v-if="marginIsAllowed"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
:label="$t('styleBoxForm.marginLabel')"
|
||||||
|
horizontal
|
||||||
|
:error-message="marginError"
|
||||||
|
>
|
||||||
|
<PixelValueSelector v-model="actualMargin" />
|
||||||
|
</FormGroup>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -56,15 +55,14 @@ import { required, integer, between } from 'vuelidate/lib/validators'
|
||||||
import form from '@baserow/modules/core/mixins/form'
|
import form from '@baserow/modules/core/mixins/form'
|
||||||
import { themeToColorVariables } from '@baserow/modules/builder/utils/theme'
|
import { themeToColorVariables } from '@baserow/modules/builder/utils/theme'
|
||||||
|
|
||||||
|
import PixelValueSelector from '@baserow/modules/builder/components/PixelValueSelector'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'StyleBoxForm',
|
name: 'StyleBoxForm',
|
||||||
|
components: { PixelValueSelector },
|
||||||
mixins: [form],
|
mixins: [form],
|
||||||
inject: ['builder'],
|
inject: ['builder'],
|
||||||
props: {
|
props: {
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
value: {
|
value: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -74,6 +72,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: () => false,
|
default: () => false,
|
||||||
},
|
},
|
||||||
|
marginIsAllowed: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: () => false,
|
||||||
|
},
|
||||||
borderIsAllowed: {
|
borderIsAllowed: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -83,6 +86,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
values: {
|
values: {
|
||||||
|
margin: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
border_color: 'border',
|
border_color: 'border',
|
||||||
border_size: 0,
|
border_size: 0,
|
||||||
|
@ -90,15 +94,38 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// TODO zdm can be removed when we remove the null value from backend field
|
||||||
|
actualMargin: {
|
||||||
|
get() {
|
||||||
|
return this.values.margin || 0
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
this.values.margin = newValue
|
||||||
|
},
|
||||||
|
},
|
||||||
colorVariables() {
|
colorVariables() {
|
||||||
return themeToColorVariables(this.builder.theme)
|
return themeToColorVariables(this.builder.theme)
|
||||||
},
|
},
|
||||||
/**
|
marginError() {
|
||||||
* Returns only one error because we don't have the space to write one error per
|
if (this.$v.actualMargin.$invalid) {
|
||||||
* field as the style fields are on the same line.
|
return this.$t('error.minMaxValueField', { min: 0, max: 200 })
|
||||||
*/
|
} else {
|
||||||
error() {
|
return ''
|
||||||
return this.$v.values.padding.$error || this.$v.values.border_size.$error
|
}
|
||||||
|
},
|
||||||
|
paddingError() {
|
||||||
|
if (this.$v.values.padding.$invalid) {
|
||||||
|
return this.$t('error.minMaxValueField', { min: 0, max: 200 })
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sizeError() {
|
||||||
|
if (this.$v.values.border_size.$invalid) {
|
||||||
|
return this.$t('error.minMaxValueField', { min: 0, max: 200 })
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -123,6 +150,11 @@ export default {
|
||||||
between: between(0, 200),
|
between: between(0, 200),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
actualMargin: {
|
||||||
|
required,
|
||||||
|
integer,
|
||||||
|
between: between(0, 200),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<ThemeProvider>
|
<ThemeProvider class="page">
|
||||||
<PageElement
|
<PageElement
|
||||||
v-for="element in elements"
|
v-for="element in elements"
|
||||||
:key="element.id"
|
:key="element.id"
|
||||||
|
|
|
@ -3,16 +3,18 @@
|
||||||
v-if="elementMode === 'editing' || isVisible"
|
v-if="elementMode === 'editing' || isVisible"
|
||||||
class="element__wrapper"
|
class="element__wrapper"
|
||||||
:class="{
|
:class="{
|
||||||
'element__wrapper--full-width':
|
'element__wrapper--full-bleed':
|
||||||
element.style_width === WIDTH_TYPES.FULL.value,
|
element.style_width === WIDTH_TYPES.FULL.value,
|
||||||
|
'element__wrapper--full-width':
|
||||||
|
element.style_width === WIDTH_TYPES.FULL_WIDTH.value,
|
||||||
'element__wrapper--medium-width':
|
'element__wrapper--medium-width':
|
||||||
element.style_width === WIDTH_TYPES.MEDIUM.value,
|
element.style_width === WIDTH_TYPES.MEDIUM.value,
|
||||||
'element__wrapper--small-width':
|
'element__wrapper--small-width':
|
||||||
element.style_width === WIDTH_TYPES.SMALL.value,
|
element.style_width === WIDTH_TYPES.SMALL.value,
|
||||||
}"
|
}"
|
||||||
:style="wrapperStyles"
|
:style="elementStyles"
|
||||||
>
|
>
|
||||||
<div class="element__inner-wrapper" :style="innerWrapperStyles">
|
<div class="element__inner-wrapper">
|
||||||
<component
|
<component
|
||||||
:is="component"
|
:is="component"
|
||||||
:element="element"
|
:element="element"
|
||||||
|
@ -27,11 +29,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
|
||||||
import { resolveColor } from '@baserow/modules/core/utils/colors'
|
import { resolveColor } from '@baserow/modules/core/utils/colors'
|
||||||
import { themeToColorVariables } from '@baserow/modules/builder/utils/theme'
|
import { themeToColorVariables } from '@baserow/modules/builder/utils/theme'
|
||||||
|
|
||||||
import { BACKGROUND_TYPES, WIDTH_TYPES } from '@baserow/modules/builder/enums'
|
import {
|
||||||
|
BACKGROUND_TYPES,
|
||||||
|
WIDTH_TYPES,
|
||||||
|
BACKGROUND_MODES,
|
||||||
|
} from '@baserow/modules/builder/enums'
|
||||||
import applicationContextMixin from '@baserow/modules/builder/mixins/applicationContext'
|
import applicationContextMixin from '@baserow/modules/builder/mixins/applicationContext'
|
||||||
import {
|
import {
|
||||||
VISIBILITY_NOT_LOGGED,
|
VISIBILITY_NOT_LOGGED,
|
||||||
|
@ -107,109 +112,75 @@ export default {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
allowedStyles() {
|
elementStyles() {
|
||||||
const parentElement = this.$store.getters['element/getElementById'](
|
|
||||||
this.page,
|
|
||||||
this.element.parent_element_id
|
|
||||||
)
|
|
||||||
|
|
||||||
const elementType = this.$registry.get('element', this.element.type)
|
|
||||||
const parentElementType = this.parentElement
|
|
||||||
? this.$registry.get('element', parentElement?.type)
|
|
||||||
: null
|
|
||||||
|
|
||||||
return !parentElementType
|
|
||||||
? elementType.styles
|
|
||||||
: _.difference(
|
|
||||||
elementType.styles,
|
|
||||||
parentElementType.childStylesForbidden
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes an object containing all the style properties that must be set on
|
|
||||||
* the element wrapper.
|
|
||||||
*/
|
|
||||||
wrapperStyles() {
|
|
||||||
const styles = {
|
const styles = {
|
||||||
style_background_color: {
|
'--element-background-color':
|
||||||
'--background-color':
|
this.element.style_background === BACKGROUND_TYPES.COLOR
|
||||||
this.element.style_background === BACKGROUND_TYPES.COLOR.value
|
? this.resolveColor(
|
||||||
? this.resolveColor(
|
this.element.style_background_color,
|
||||||
this.element.style_background_color,
|
this.colorVariables
|
||||||
this.colorVariables
|
)
|
||||||
)
|
: 'none',
|
||||||
: 'transparent',
|
|
||||||
},
|
'--element-background-image':
|
||||||
style_border_top: {
|
this.element.style_background_file !== null
|
||||||
'--border-top': this.border(
|
? `url(${this.element.style_background_file.url})`
|
||||||
this.element.style_border_top_size,
|
: 'none',
|
||||||
this.element.style_border_top_color
|
|
||||||
),
|
'--element-border-top': this.border(
|
||||||
},
|
this.element.style_border_top_size,
|
||||||
style_border_bottom: {
|
this.element.style_border_top_color
|
||||||
'--border-bottom': this.border(
|
),
|
||||||
this.element.style_border_bottom_size,
|
'--element-margin-top': `${this.element.style_margin_top || 0}px`,
|
||||||
this.element.style_border_bottom_color
|
'--element-padding-top': `${this.element.style_padding_top || 0}px`,
|
||||||
),
|
'--element-border-bottom': this.border(
|
||||||
},
|
this.element.style_border_bottom_size,
|
||||||
style_border_left: {
|
this.element.style_border_bottom_color
|
||||||
'--border-left': this.border(
|
),
|
||||||
this.element.style_border_left_size,
|
'--element-margin-bottom': `${this.element.style_margin_bottom || 0}px`,
|
||||||
this.element.style_border_left_color
|
'--element-padding-bottom': `${
|
||||||
),
|
this.element.style_padding_bottom || 0
|
||||||
},
|
}px`,
|
||||||
style_border_right: {
|
'--element-border-left': this.border(
|
||||||
'--border-right': this.border(
|
this.element.style_border_left_size,
|
||||||
this.element.style_border_right_size,
|
this.element.style_border_left_color
|
||||||
this.element.style_border_right_color
|
),
|
||||||
),
|
'--element-margin-left': `${this.element.style_margin_left || 0}px`,
|
||||||
},
|
'--element-padding-left': `${this.element.style_padding_left || 0}px`,
|
||||||
|
'--element-border-right': this.border(
|
||||||
|
this.element.style_border_right_size,
|
||||||
|
this.element.style_border_right_color
|
||||||
|
),
|
||||||
|
'--element-margin-right': `${this.element.style_margin_right || 0}px`,
|
||||||
|
|
||||||
|
'--element-padding-right': `${this.element.style_padding_right || 0}px`,
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(styles).reduce((acc, key) => {
|
if (this.element.style_background_file !== null) {
|
||||||
if (this.allowedStyles.includes(key)) {
|
if (this.element.style_background_mode === BACKGROUND_MODES.FILL) {
|
||||||
acc = { ...acc, ...styles[key] }
|
styles['--element-background-size'] = 'cover'
|
||||||
|
styles['--element-background-repeat'] = 'no-repeat'
|
||||||
|
}
|
||||||
|
if (this.element.style_background_mode === BACKGROUND_MODES.TILE) {
|
||||||
|
styles['--element-background-size'] = 'auto'
|
||||||
|
styles['--element-background-repeat'] = 'repeat'
|
||||||
|
}
|
||||||
|
if (this.element.style_background_mode === BACKGROUND_MODES.FIT) {
|
||||||
|
styles['--element-background-size'] = 'contain'
|
||||||
|
styles['--element-background-repeat'] = 'no-repeat'
|
||||||
}
|
}
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes an object containing all the style properties that must be set on
|
|
||||||
* the element inner wrapper.
|
|
||||||
*/
|
|
||||||
innerWrapperStyles() {
|
|
||||||
const styles = {
|
|
||||||
style_padding_top: {
|
|
||||||
'--padding-top': `${this.element.style_padding_top || 0}px`,
|
|
||||||
},
|
|
||||||
style_padding_bottom: {
|
|
||||||
'--padding-bottom': `${this.element.style_padding_bottom || 0}px`,
|
|
||||||
},
|
|
||||||
style_padding_left: {
|
|
||||||
'--padding-left': `${this.element.style_padding_left || 0}px`,
|
|
||||||
},
|
|
||||||
style_padding_right: {
|
|
||||||
'--padding-right': `${this.element.style_padding_right || 0}px`,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(styles).reduce((acc, key) => {
|
return styles
|
||||||
if (this.allowedStyles.includes(key)) {
|
|
||||||
acc = { ...acc, ...styles[key] }
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resolveColor,
|
resolveColor,
|
||||||
border(size, color) {
|
border(size, color) {
|
||||||
return `solid ${size || 0}px ${this.resolveColor(
|
if (!size) {
|
||||||
color,
|
return 'none'
|
||||||
this.colorVariables
|
}
|
||||||
)}`
|
return `solid ${size}px ${this.resolveColor(color, this.colorVariables)}`
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,21 +23,23 @@
|
||||||
>
|
>
|
||||||
{{ $t('pagePreview.emptyMessage') }}
|
{{ $t('pagePreview.emptyMessage') }}
|
||||||
</CallToAction>
|
</CallToAction>
|
||||||
<AddElementModal ref="addElementModal" :page="page" />
|
<div class="page">
|
||||||
<ElementPreview
|
<ElementPreview
|
||||||
v-for="(element, index) in elements"
|
v-for="(element, index) in elements"
|
||||||
:key="element.id"
|
:key="element.id"
|
||||||
is-root-element
|
is-root-element
|
||||||
:element="element"
|
:element="element"
|
||||||
:is-first-element="index === 0"
|
:is-first-element="index === 0"
|
||||||
:is-last-element="index === elements.length - 1"
|
:is-last-element="index === elements.length - 1"
|
||||||
:is-copying="copyingElementIndex === index"
|
:is-copying="copyingElementIndex === index"
|
||||||
:application-context-additions="{
|
:application-context-additions="{
|
||||||
recordIndexPath: [],
|
recordIndexPath: [],
|
||||||
}"
|
}"
|
||||||
@move="moveElement($event)"
|
@move="moveElement($event)"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AddElementModal ref="addElementModal" :page="page" />
|
||||||
</div>
|
</div>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -12,9 +12,8 @@
|
||||||
<WidthSelector v-model="buttonWidth" />
|
<WidthSelector v-model="buttonWidth" />
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.button_width"
|
||||||
:theme="theme"
|
:default-value="theme?.button_width"
|
||||||
property="button_width"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -29,9 +28,8 @@
|
||||||
<HorizontalAlignmentsSelector v-model="values.button_alignment" />
|
<HorizontalAlignmentsSelector v-model="values.button_alignment" />
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.button_alignment"
|
||||||
:theme="theme"
|
:default-value="theme?.button_alignment"
|
||||||
property="button_alignment"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -48,12 +46,95 @@
|
||||||
/>
|
/>
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.button_text_alignment"
|
||||||
:theme="theme"
|
:default-value="theme?.button_text_alignment"
|
||||||
property="button_text_alignment"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
:label="$t('buttonThemeConfigBlock.fontFamily')"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
>
|
||||||
|
<FontFamilySelector v-model="values.button_font_family" />
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_font_family"
|
||||||
|
:default-value="theme?.button_font_family"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
:label="$t('buttonThemeConfigBlock.size')"
|
||||||
|
:error-message="getError('button_font_size')"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
>
|
||||||
|
<PixelValueSelector v-model="values.button_font_size" />
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_font_size"
|
||||||
|
:default-value="theme?.button_font_size"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
:label="$t('buttonThemeConfigBlock.borderSize')"
|
||||||
|
:error-message="getError('button_border_size')"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
>
|
||||||
|
<PixelValueSelector v-model="values.button_border_size" />
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_border_size"
|
||||||
|
:default-value="theme?.button_border_size"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
:label="$t('buttonThemeConfigBlock.borderRadius')"
|
||||||
|
:error-message="getError('button_border_radius')"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
>
|
||||||
|
<PixelValueSelector v-model="values.button_border_radius" />
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_border_radius"
|
||||||
|
:default-value="theme?.button_border_radius"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
:label="$t('buttonThemeConfigBlock.padding')"
|
||||||
|
:error-message="getPaddingError()"
|
||||||
|
class="margin-bottom-1"
|
||||||
|
>
|
||||||
|
<PaddingSelector v-model="padding" />
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="padding"
|
||||||
|
:default-value="
|
||||||
|
theme
|
||||||
|
? {
|
||||||
|
vertical: theme['button_vertical_padding'],
|
||||||
|
horizontal: theme['button_horizontal_padding'],
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
</template>
|
||||||
|
<template #preview>
|
||||||
|
<ABButton>{{ $t('buttonThemeConfigBlock.button') }}</ABButton>
|
||||||
</template>
|
</template>
|
||||||
</ThemeConfigBlockSection>
|
</ThemeConfigBlockSection>
|
||||||
<ThemeConfigBlockSection :title="$t('buttonThemeConfigBlock.defaultState')">
|
<ThemeConfigBlockSection :title="$t('buttonThemeConfigBlock.defaultState')">
|
||||||
|
@ -73,9 +154,48 @@
|
||||||
/>
|
/>
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.button_background_color"
|
||||||
:theme="theme"
|
:default-value="theme?.button_background_color"
|
||||||
property="button_background_color"
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('buttonThemeConfigBlock.textColor')"
|
||||||
|
>
|
||||||
|
<ColorInput
|
||||||
|
v-model="values.button_text_color"
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
:default-value="theme?.button_text_color"
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_text_color"
|
||||||
|
:default-value="theme?.button_text_color"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('buttonThemeConfigBlock.borderColor')"
|
||||||
|
>
|
||||||
|
<ColorInput
|
||||||
|
v-model="values.button_border_color"
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
:default-value="theme?.button_border_color"
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_border_color"
|
||||||
|
:default-value="theme?.button_border_color"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -101,9 +221,46 @@
|
||||||
/>
|
/>
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.button_hover_background_color"
|
||||||
:theme="theme"
|
:default-value="theme?.button_hover_background_color"
|
||||||
property="button_hover_background_color"
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('buttonThemeConfigBlock.textColor')"
|
||||||
|
>
|
||||||
|
<ColorInput
|
||||||
|
v-model="values.button_hover_text_color"
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
:default-value="theme?.button_hover_text_color"
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_hover_text_color"
|
||||||
|
:default-value="theme?.button_hover_text_color"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('buttonThemeConfigBlock.borderColor')"
|
||||||
|
>
|
||||||
|
<ColorInput
|
||||||
|
v-model="values.button_hover_border_color"
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
:default-value="theme?.button_hover_border_color"
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.button_hover_border_color"
|
||||||
|
:default-value="theme?.button_hover_border_color"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -123,6 +280,36 @@ import ThemeConfigBlockSection from '@baserow/modules/builder/components/theme/T
|
||||||
import ResetButton from '@baserow/modules/builder/components/theme/ResetButton'
|
import ResetButton from '@baserow/modules/builder/components/theme/ResetButton'
|
||||||
import HorizontalAlignmentsSelector from '@baserow/modules/builder/components/HorizontalAlignmentsSelector'
|
import HorizontalAlignmentsSelector from '@baserow/modules/builder/components/HorizontalAlignmentsSelector'
|
||||||
import WidthSelector from '@baserow/modules/builder/components/WidthSelector'
|
import WidthSelector from '@baserow/modules/builder/components/WidthSelector'
|
||||||
|
import FontFamilySelector from '@baserow/modules/builder/components/FontFamilySelector'
|
||||||
|
import PixelValueSelector from '@baserow/modules/builder/components/PixelValueSelector'
|
||||||
|
import PaddingSelector from '@baserow/modules/builder/components/PaddingSelector'
|
||||||
|
import { required, integer, minValue, maxValue } from 'vuelidate/lib/validators'
|
||||||
|
|
||||||
|
const pixelSizeMin = 1
|
||||||
|
const pixelSizeMax = 100
|
||||||
|
|
||||||
|
const minMax = {
|
||||||
|
button_font_size: {
|
||||||
|
min: 1,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
button_border_size: {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
button_border_radius: {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
button_horizontal_padding: {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
button_vertical_padding: {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ButtonThemeConfigBlock',
|
name: 'ButtonThemeConfigBlock',
|
||||||
|
@ -131,18 +318,14 @@ export default {
|
||||||
ResetButton,
|
ResetButton,
|
||||||
WidthSelector,
|
WidthSelector,
|
||||||
HorizontalAlignmentsSelector,
|
HorizontalAlignmentsSelector,
|
||||||
|
FontFamilySelector,
|
||||||
|
PixelValueSelector,
|
||||||
|
PaddingSelector,
|
||||||
},
|
},
|
||||||
mixins: [themeConfigBlock],
|
mixins: [themeConfigBlock],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
values: {},
|
values: {},
|
||||||
allowedValues: [
|
|
||||||
'button_background_color',
|
|
||||||
'button_hover_background_color',
|
|
||||||
'button_text_alignment',
|
|
||||||
'button_alignment',
|
|
||||||
'button_width',
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -161,6 +344,73 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
padding: {
|
||||||
|
get() {
|
||||||
|
return {
|
||||||
|
vertical: this.values.button_vertical_padding,
|
||||||
|
horizontal: this.values.button_horizontal_padding,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
this.values.button_vertical_padding = newValue.vertical
|
||||||
|
this.values.button_horizontal_padding = newValue.horizontal
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pixedSizeMin() {
|
||||||
|
return pixelSizeMin
|
||||||
|
},
|
||||||
|
pixedSizeMax() {
|
||||||
|
return pixelSizeMax
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isAllowedKey(key) {
|
||||||
|
return key.startsWith('button_')
|
||||||
|
},
|
||||||
|
getError(property) {
|
||||||
|
if (this.$v.values[property].$invalid) {
|
||||||
|
return this.$t('error.minMaxValueField', minMax[property])
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
getPaddingError() {
|
||||||
|
return (
|
||||||
|
this.getError('button_vertical_padding') ||
|
||||||
|
this.getError('button_horizontal_padding')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validations: {
|
||||||
|
values: {
|
||||||
|
button_font_size: {
|
||||||
|
required,
|
||||||
|
integer,
|
||||||
|
minValue: minValue(minMax.button_font_size.min),
|
||||||
|
maxValue: maxValue(minMax.button_font_size.max),
|
||||||
|
},
|
||||||
|
button_border_size: {
|
||||||
|
integer,
|
||||||
|
minValue: minValue(minMax.button_border_size.min),
|
||||||
|
maxValue: maxValue(minMax.button_border_size.max),
|
||||||
|
},
|
||||||
|
button_border_radius: {
|
||||||
|
integer,
|
||||||
|
minValue: minValue(minMax.button_border_radius.min),
|
||||||
|
maxValue: maxValue(minMax.button_border_radius.max),
|
||||||
|
},
|
||||||
|
button_horizontal_padding: {
|
||||||
|
required,
|
||||||
|
integer,
|
||||||
|
minValue: minValue(minMax.button_horizontal_padding.min),
|
||||||
|
maxValue: maxValue(minMax.button_horizontal_padding.max),
|
||||||
|
},
|
||||||
|
button_vertical_padding: {
|
||||||
|
required,
|
||||||
|
integer,
|
||||||
|
minValue: minValue(minMax.button_vertical_padding.min),
|
||||||
|
maxValue: maxValue(minMax.button_vertical_padding.max),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,15 +1,54 @@
|
||||||
<template>
|
<template>
|
||||||
<ThemeConfigBlockSection>
|
<ThemeConfigBlockSection>
|
||||||
<template #default>
|
<template #default>
|
||||||
<FormGroup horizontal :label="$t('colorThemeConfigBlock.primaryColor')">
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('colorThemeConfigBlock.primaryColor')"
|
||||||
|
>
|
||||||
<ColorInput v-model="values.primary_color" small />
|
<ColorInput v-model="values.primary_color" small />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup horizontal :label="$t('colorThemeConfigBlock.secondaryColor')">
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('colorThemeConfigBlock.secondaryColor')"
|
||||||
|
>
|
||||||
<ColorInput v-model="values.secondary_color" small />
|
<ColorInput v-model="values.secondary_color" small />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup horizontal :label="$t('colorThemeConfigBlock.borderColor')">
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('colorThemeConfigBlock.borderColor')"
|
||||||
|
>
|
||||||
<ColorInput v-model="values.border_color" small />
|
<ColorInput v-model="values.border_color" small />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('colorThemeConfigBlock.successColor')"
|
||||||
|
>
|
||||||
|
<ColorInput v-model="values.main_success_color" small />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('colorThemeConfigBlock.warningColor')"
|
||||||
|
>
|
||||||
|
<ColorInput v-model="values.main_warning_color" small />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('colorThemeConfigBlock.errorColor')"
|
||||||
|
>
|
||||||
|
<ColorInput v-model="values.main_error_color" small />
|
||||||
|
</FormGroup>
|
||||||
</template>
|
</template>
|
||||||
</ThemeConfigBlockSection>
|
</ThemeConfigBlockSection>
|
||||||
</template>
|
</template>
|
||||||
|
@ -26,8 +65,15 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
values: {},
|
values: {},
|
||||||
allowedValues: ['primary_color', 'secondary_color', 'border_color'],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
isAllowedKey(key) {
|
||||||
|
return (
|
||||||
|
key.startsWith('main_') ||
|
||||||
|
['primary_color', 'secondary_color', 'border_color'].includes(key)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -13,9 +13,8 @@
|
||||||
|
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.image_alignment"
|
||||||
:theme="theme"
|
:default-value="theme?.image_alignment"
|
||||||
property="image_alignment"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -30,7 +29,8 @@
|
||||||
v-model="values.image_max_width"
|
v-model="values.image_max_width"
|
||||||
small
|
small
|
||||||
type="number"
|
type="number"
|
||||||
:error="
|
remove-number-input-controls
|
||||||
|
:error-message="
|
||||||
$v.values.image_max_width.$dirty &&
|
$v.values.image_max_width.$dirty &&
|
||||||
!$v.values.image_max_width.integer
|
!$v.values.image_max_width.integer
|
||||||
? $t('error.integerField')
|
? $t('error.integerField')
|
||||||
|
@ -50,9 +50,8 @@
|
||||||
|
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.image_max_width"
|
||||||
:theme="theme"
|
:default-value="theme?.image_max_width"
|
||||||
property="image_max_width"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -67,7 +66,8 @@
|
||||||
v-model="imageMaxHeight"
|
v-model="imageMaxHeight"
|
||||||
small
|
small
|
||||||
type="number"
|
type="number"
|
||||||
:error="
|
remove-number-input-controls
|
||||||
|
:error-message="
|
||||||
$v.values.image_max_height.$dirty &&
|
$v.values.image_max_height.$dirty &&
|
||||||
!$v.values.image_max_height.integer
|
!$v.values.image_max_height.integer
|
||||||
? $t('error.integerField')
|
? $t('error.integerField')
|
||||||
|
@ -85,9 +85,8 @@
|
||||||
|
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="imageMaxHeight"
|
||||||
:theme="theme"
|
:default-value="theme?.image_max_height"
|
||||||
property="image_max_height"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -122,8 +121,7 @@
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="imageConstraintForReset"
|
v-model="imageConstraintForReset"
|
||||||
:theme="theme"
|
:default-value="theme?.image_constraint"
|
||||||
property="image_constraint"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -191,18 +189,18 @@ export default {
|
||||||
},
|
},
|
||||||
imageConstraintForReset: {
|
imageConstraintForReset: {
|
||||||
get() {
|
get() {
|
||||||
return { image_constraint: this.values.image_constraint }
|
return this.values.image_constraint
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value.image_constraint === 'contain') {
|
if (value === 'contain') {
|
||||||
// Reset the height as we can't have a max height with contain
|
// Reset the height as we can't have a max height with contain
|
||||||
this.values.image_max_height = null
|
this.values.image_max_height = null
|
||||||
}
|
}
|
||||||
if (value.image_constraint === 'cover') {
|
if (value === 'cover') {
|
||||||
// Set the height to what is defined in theme
|
// Set the height to what is defined in theme
|
||||||
this.values.image_max_height = this.theme.image_max_height
|
this.values.image_max_height = this.theme.image_max_height
|
||||||
}
|
}
|
||||||
this.values.image_constraint = value.image_constraint
|
this.values.image_constraint = value
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
IMAGE_SOURCE_TYPES() {
|
IMAGE_SOURCE_TYPES() {
|
||||||
|
@ -235,6 +233,9 @@ export default {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isAllowedKey(key) {
|
||||||
|
return key.startsWith('image_')
|
||||||
|
},
|
||||||
},
|
},
|
||||||
validations: {
|
validations: {
|
||||||
values: {
|
values: {
|
||||||
|
|
|
@ -6,15 +6,29 @@
|
||||||
horizontal
|
horizontal
|
||||||
small-label
|
small-label
|
||||||
required
|
required
|
||||||
:label="$t('linkThemeConfigBlock.alignment')"
|
|
||||||
class="margin-bottom-1"
|
class="margin-bottom-1"
|
||||||
|
:label="$t('linkThemeConfigBlock.fontFamily')"
|
||||||
|
>
|
||||||
|
<FontFamilySelector v-model="values.link_font_family" />
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.link_font_family"
|
||||||
|
:default-value="theme?.link_font_family"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
required
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('linkThemeConfigBlock.alignment')"
|
||||||
>
|
>
|
||||||
<HorizontalAlignmentsSelector v-model="values.link_text_alignment" />
|
<HorizontalAlignmentsSelector v-model="values.link_text_alignment" />
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.link_text_alignment"
|
||||||
:theme="theme"
|
:default-value="theme?.link_text_alignment"
|
||||||
property="link_text_alignment"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -37,9 +51,8 @@
|
||||||
/>
|
/>
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.link_text_color"
|
||||||
:theme="theme"
|
:default-value="theme?.link_text_color"
|
||||||
property="link_text_color"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -65,9 +78,8 @@
|
||||||
/>
|
/>
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.link_hover_text_color"
|
||||||
:theme="theme"
|
:default-value="theme?.link_hover_text_color"
|
||||||
property="link_hover_text_color"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -86,6 +98,7 @@ import themeConfigBlock from '@baserow/modules/builder/mixins/themeConfigBlock'
|
||||||
import ThemeConfigBlockSection from '@baserow/modules/builder/components/theme/ThemeConfigBlockSection'
|
import ThemeConfigBlockSection from '@baserow/modules/builder/components/theme/ThemeConfigBlockSection'
|
||||||
import ResetButton from '@baserow/modules/builder/components/theme/ResetButton'
|
import ResetButton from '@baserow/modules/builder/components/theme/ResetButton'
|
||||||
import HorizontalAlignmentsSelector from '@baserow/modules/builder/components/HorizontalAlignmentsSelector'
|
import HorizontalAlignmentsSelector from '@baserow/modules/builder/components/HorizontalAlignmentsSelector'
|
||||||
|
import FontFamilySelector from '@baserow/modules/builder/components/FontFamilySelector'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LinkThemeConfigBlock',
|
name: 'LinkThemeConfigBlock',
|
||||||
|
@ -93,17 +106,18 @@ export default {
|
||||||
ThemeConfigBlockSection,
|
ThemeConfigBlockSection,
|
||||||
ResetButton,
|
ResetButton,
|
||||||
HorizontalAlignmentsSelector,
|
HorizontalAlignmentsSelector,
|
||||||
|
FontFamilySelector,
|
||||||
},
|
},
|
||||||
mixins: [themeConfigBlock],
|
mixins: [themeConfigBlock],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
values: {},
|
values: {},
|
||||||
allowedValues: [
|
|
||||||
'link_text_color',
|
|
||||||
'link_hover_text_color',
|
|
||||||
'link_text_alignment',
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
isAllowedKey(key) {
|
||||||
|
return key.startsWith('link_')
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,215 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ColorPickerContext
|
|
||||||
ref="colorPicker"
|
|
||||||
:value="builder.theme[colorPickerPropertyName] || '#000000ff'"
|
|
||||||
@input="colorPickerColorChanged"
|
|
||||||
></ColorPickerContext>
|
|
||||||
<div class="theme-settings__section margin-bottom-3">
|
|
||||||
<div class="theme-settings__section-properties">
|
|
||||||
<a
|
|
||||||
class="theme-settings__section-title"
|
|
||||||
@click="toggleClosed('colors')"
|
|
||||||
>
|
|
||||||
{{ $t('mainThemeConfigBlock.colorsLabel') }}
|
|
||||||
<i
|
|
||||||
class="iconoir-nav-arrow-down theme-settings__section-title-icon"
|
|
||||||
:class="{
|
|
||||||
'theme-settings__section-title-icon': true,
|
|
||||||
'iconoir-nav-arrow-down': !isClosed('colors'),
|
|
||||||
'iconoir-nav-arrow-right': isClosed('colors'),
|
|
||||||
}"
|
|
||||||
></i>
|
|
||||||
</a>
|
|
||||||
<div v-show="!isClosed('colors')">
|
|
||||||
<ColorInputGroup
|
|
||||||
:value="builder.theme.primary_color"
|
|
||||||
label-after
|
|
||||||
:label="$t('mainThemeConfigBlock.primaryColor')"
|
|
||||||
class="margin-bottom-1"
|
|
||||||
@input="setPropertyInStore('primary_color', $event)"
|
|
||||||
/>
|
|
||||||
<ColorInputGroup
|
|
||||||
:value="builder.theme.secondary_color"
|
|
||||||
label-after
|
|
||||||
:label="$t('mainThemeConfigBlock.secondaryColor')"
|
|
||||||
@input="setPropertyInStore('secondary_color', $event)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="theme-settings__section">
|
|
||||||
<div class="theme-settings__section-properties">
|
|
||||||
<a
|
|
||||||
class="theme-settings__section-title"
|
|
||||||
@click="toggleClosed('typography')"
|
|
||||||
>
|
|
||||||
{{ $t('mainThemeConfigBlock.typography') }}
|
|
||||||
<i
|
|
||||||
class="iconoir-nav-arrow-down theme-settings__section-title-icon"
|
|
||||||
:class="{
|
|
||||||
'theme-settings__section-title-icon': true,
|
|
||||||
'iconoir-nav-arrow-down': !isClosed('typography'),
|
|
||||||
'iconoir-nav-arrow-right': isClosed('typography'),
|
|
||||||
}"
|
|
||||||
></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-for="i in headings"
|
|
||||||
v-show="!isClosed('typography')"
|
|
||||||
:key="i"
|
|
||||||
class="theme-settings__section"
|
|
||||||
>
|
|
||||||
<div class="theme-settings__section-properties">
|
|
||||||
<FormGroup
|
|
||||||
:label="$t('mainThemeConfigBlock.headingLabel', { i })"
|
|
||||||
small-label
|
|
||||||
required
|
|
||||||
:error="$v.builder.theme[`heading_${i}_font_size`].$error"
|
|
||||||
class="margin-bottom-2"
|
|
||||||
>
|
|
||||||
<div class="theme-settings__inputs-wrapper">
|
|
||||||
<ColorInput
|
|
||||||
:value="builder.theme[`heading_${i}_color`]"
|
|
||||||
@input="setPropertyInStore(`heading_${i}_color`, $event)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormInput
|
|
||||||
class="theme-settings__input-font-size"
|
|
||||||
type="number"
|
|
||||||
size="large"
|
|
||||||
remove-number-input-controls
|
|
||||||
:min="fontSizeMin"
|
|
||||||
:max="fontSizeMax"
|
|
||||||
:value="builder.theme[`heading_${i}_font_size`]"
|
|
||||||
:error="$v.builder.theme[`heading_${i}_font_size`].$error"
|
|
||||||
@input="
|
|
||||||
;[
|
|
||||||
$v.builder.theme[`heading_${i}_font_size`].$touch(),
|
|
||||||
setPropertyInStore(
|
|
||||||
`heading_${i}_font_size`,
|
|
||||||
$event,
|
|
||||||
!$v.builder.theme[`heading_${i}_font_size`].$error
|
|
||||||
),
|
|
||||||
]
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<template #suffix>px</template></FormInput
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template #error>
|
|
||||||
{{ $t('error.minMaxLength', { min: 1, max: 100 }) }}
|
|
||||||
</template>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
|
||||||
<div class="theme-settings__section-preview">
|
|
||||||
<component
|
|
||||||
:is="`h${i}`"
|
|
||||||
class="margin-bottom-2 theme-settings__section-ellipsis"
|
|
||||||
:class="`ab-heading--h${i}`"
|
|
||||||
:style="{
|
|
||||||
[`--heading-h${i}-color`]: builder.theme[`heading_${i}_color`],
|
|
||||||
[`--heading-h${i}-font-size`]:
|
|
||||||
builder.theme[`heading_${i}_font_size`] + 'px',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ $t('mainThemeConfigBlock.headingValue', { i }) }}
|
|
||||||
</component>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions } from 'vuex'
|
|
||||||
import { required, integer, minValue, maxValue } from 'vuelidate/lib/validators'
|
|
||||||
import ColorPickerContext from '@baserow/modules/core/components/ColorPickerContext'
|
|
||||||
import { notifyIf } from '@baserow/modules/core/utils/error'
|
|
||||||
|
|
||||||
const fontSizeMin = 1
|
|
||||||
const fontSizeMax = 100
|
|
||||||
const headings = [1, 2, 3]
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'MainThemeConfigBlock',
|
|
||||||
components: { ColorPickerContext },
|
|
||||||
props: {
|
|
||||||
builder: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
closed: [],
|
|
||||||
colorPickerPropertyName: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
headings() {
|
|
||||||
return headings
|
|
||||||
},
|
|
||||||
fontSizeMin() {
|
|
||||||
return fontSizeMin
|
|
||||||
},
|
|
||||||
fontSizeMax() {
|
|
||||||
return fontSizeMax
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
setThemeProperty: 'theme/setProperty',
|
|
||||||
forceSetThemeProperty: 'theme/forceSetProperty',
|
|
||||||
}),
|
|
||||||
toggleClosed(value) {
|
|
||||||
const index = this.closed.indexOf(value)
|
|
||||||
if (index < 0) {
|
|
||||||
this.closed.push(value)
|
|
||||||
} else {
|
|
||||||
this.closed.splice(index, 1)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isClosed(value) {
|
|
||||||
return this.closed.includes(value)
|
|
||||||
},
|
|
||||||
openColorPicker(opener, propertyName) {
|
|
||||||
this.colorPickerPropertyName = propertyName
|
|
||||||
this.$refs.colorPicker.toggle(opener)
|
|
||||||
},
|
|
||||||
colorPickerColorChanged(value) {
|
|
||||||
this.setPropertyInStore(this.colorPickerPropertyName, value)
|
|
||||||
},
|
|
||||||
async setPropertyInStore(key, value, makeRequest = true) {
|
|
||||||
const action = makeRequest ? 'setThemeProperty' : 'forceSetThemeProperty'
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this[action]({
|
|
||||||
builder: this.builder,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
notifyIf(error, 'row')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
validations: {
|
|
||||||
builder: {
|
|
||||||
theme: headings.reduce((o, i) => {
|
|
||||||
o[`heading_${i}_font_size`] = {
|
|
||||||
required,
|
|
||||||
integer,
|
|
||||||
minValue: minValue(fontSizeMin),
|
|
||||||
maxValue: maxValue(fontSizeMax),
|
|
||||||
}
|
|
||||||
return o
|
|
||||||
}, {}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<ThemeConfigBlockSection>
|
||||||
|
<template #default>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('pageThemeConfigBlock.backgroundColor')"
|
||||||
|
>
|
||||||
|
<ColorInput
|
||||||
|
v-model="values.page_background_color"
|
||||||
|
small
|
||||||
|
:allow-opacity="false"
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('pageThemeConfigBlock.backgroundImage')"
|
||||||
|
>
|
||||||
|
<ImageInput v-model="values.page_background_file" />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
v-if="values.page_background_file"
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('pageThemeConfigBlock.backgroundMode')"
|
||||||
|
>
|
||||||
|
<RadioGroup
|
||||||
|
v-model="values.page_background_mode"
|
||||||
|
type="button"
|
||||||
|
:options="backgroundModes"
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</template>
|
||||||
|
</ThemeConfigBlockSection>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import themeConfigBlock from '@baserow/modules/builder/mixins/themeConfigBlock'
|
||||||
|
import ThemeConfigBlockSection from '@baserow/modules/builder/components/theme/ThemeConfigBlockSection'
|
||||||
|
import { BACKGROUND_MODES } from '@baserow/modules/builder/enums'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PageThemeConfigBlock',
|
||||||
|
components: { ThemeConfigBlockSection },
|
||||||
|
mixins: [themeConfigBlock],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
values: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
backgroundModes() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundModes.tile'),
|
||||||
|
value: BACKGROUND_MODES.TILE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundModes.fill'),
|
||||||
|
value: BACKGROUND_MODES.FILL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('backgroundModes.fit'),
|
||||||
|
value: BACKGROUND_MODES.FIT,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isAllowedKey(key) {
|
||||||
|
return key.startsWith('page_')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -13,25 +13,25 @@ import _ from 'lodash'
|
||||||
export default {
|
export default {
|
||||||
inject: ['builder'],
|
inject: ['builder'],
|
||||||
props: {
|
props: {
|
||||||
theme: { type: Object, required: false, default: null },
|
value: { required: true, validator: (v) => true },
|
||||||
property: { type: String, required: true },
|
defaultValue: {
|
||||||
value: { type: Object, required: true },
|
required: false,
|
||||||
|
validator: (v) => true,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
propertyModified() {
|
propertyModified() {
|
||||||
if (!this.theme) {
|
return (
|
||||||
return false
|
this.defaultValue !== undefined &&
|
||||||
}
|
!_.isEqual(this.value, this.defaultValue)
|
||||||
return !_.isEqual(this.value[this.property], this.theme[this.property])
|
)
|
||||||
},
|
},
|
||||||
resetProperty() {
|
resetProperty() {
|
||||||
this.$emit('input', {
|
this.$emit('input', this.defaultValue)
|
||||||
...this.value,
|
|
||||||
[this.property]: this.theme[this.property],
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,54 +3,66 @@
|
||||||
<ThemeConfigBlockSection
|
<ThemeConfigBlockSection
|
||||||
v-if="showBody"
|
v-if="showBody"
|
||||||
:title="$t('typographyThemeConfigBlock.bodyLabel')"
|
:title="$t('typographyThemeConfigBlock.bodyLabel')"
|
||||||
class="margin-bottom-2"
|
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
horizontal
|
horizontal
|
||||||
small-label
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('typographyThemeConfigBlock.fontFamily')"
|
||||||
|
>
|
||||||
|
<FontFamilySelector v-model="values.body_font_family" />
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.body_font_family"
|
||||||
|
:default-value="theme?.body_font_family"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
:label="$t('typographyThemeConfigBlock.textAlignment')"
|
:label="$t('typographyThemeConfigBlock.textAlignment')"
|
||||||
>
|
>
|
||||||
<HorizontalAlignmentsSelector
|
<HorizontalAlignmentsSelector v-model="values.body_text_alignment" />
|
||||||
v-model="values[`body_text_alignment`]"
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values.body_text_alignment"
|
||||||
|
:default-value="theme?.body_text_alignment"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('typographyThemeConfigBlock.size')"
|
||||||
|
:error-message="
|
||||||
|
$v.values[`body_font_size`].$invalid
|
||||||
|
? $t('error.minMaxValueField', {
|
||||||
|
min: fontSizeMin,
|
||||||
|
max: bodyFontSizeMax,
|
||||||
|
})
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<PixelValueSelector
|
||||||
|
v-model="values.body_font_size"
|
||||||
|
class="typography-theme-config-block__input-number"
|
||||||
|
@blur="$v.values[`body_font_size`].$touch()"
|
||||||
/>
|
/>
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.body_font_size"
|
||||||
:theme="theme"
|
:default-value="theme?.body_font_size"
|
||||||
:property="`body_text_alignment`"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
horizontal
|
|
||||||
small-label
|
|
||||||
:label="$t('typographyThemeConfigBlock.size')"
|
|
||||||
:error="$v.values[`body_font_size`].$invalid"
|
|
||||||
>
|
|
||||||
<FormInput
|
|
||||||
v-model="values[`body_font_size`]"
|
|
||||||
type="number"
|
|
||||||
remove-number-input-controls
|
|
||||||
:min="fontSizeMin"
|
|
||||||
:max="fontSizeMax"
|
|
||||||
:error="$v.values[`body_font_size`].$invalid"
|
|
||||||
@blur="$v.values[`body_font_size`].$touch()"
|
|
||||||
>
|
|
||||||
<template #suffix>px</template>
|
|
||||||
</FormInput>
|
|
||||||
|
|
||||||
<template #after-input>
|
|
||||||
<ResetButton
|
|
||||||
v-model="values"
|
|
||||||
:theme="theme"
|
|
||||||
:property="`body_font_size`"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
horizontal
|
horizontal
|
||||||
small-label
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
:label="$t('typographyThemeConfigBlock.color')"
|
:label="$t('typographyThemeConfigBlock.color')"
|
||||||
>
|
>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
|
@ -61,9 +73,8 @@
|
||||||
/>
|
/>
|
||||||
<template #after-input>
|
<template #after-input>
|
||||||
<ResetButton
|
<ResetButton
|
||||||
v-model="values"
|
v-model="values.body_text_color"
|
||||||
:theme="theme"
|
:default-value="theme?.body_text_color"
|
||||||
:property="'body_text_color'"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -75,87 +86,103 @@
|
||||||
</ABParagraph>
|
</ABParagraph>
|
||||||
</template>
|
</template>
|
||||||
</ThemeConfigBlockSection>
|
</ThemeConfigBlockSection>
|
||||||
<ThemeConfigBlockSection
|
<template v-if="showHeadings">
|
||||||
v-for="level in headings"
|
<ThemeConfigBlockSection
|
||||||
:key="level"
|
v-for="level in headings"
|
||||||
:title="$t('typographyThemeConfigBlock.headingLabel', { i: level })"
|
:key="level"
|
||||||
class="margin-bottom-2"
|
:title="$t('typographyThemeConfigBlock.headingLabel', { i: level })"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
horizontal
|
horizontal
|
||||||
small-label
|
small-label
|
||||||
:label="$t('typographyThemeConfigBlock.textAlignment')"
|
class="margin-bottom-1"
|
||||||
>
|
:label="$t('typographyThemeConfigBlock.fontFamily')"
|
||||||
<HorizontalAlignmentsSelector
|
|
||||||
v-model="values[`heading_${level}_text_alignment`]"
|
|
||||||
/>
|
|
||||||
<template #after-input>
|
|
||||||
<ResetButton
|
|
||||||
v-model="values"
|
|
||||||
:theme="theme"
|
|
||||||
:property="`heading_${level}_text_alignment`"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup
|
|
||||||
horizontal
|
|
||||||
small-label
|
|
||||||
:label="$t('typographyThemeConfigBlock.size')"
|
|
||||||
:error="$v.values[`heading_${level}_font_size`].$invalid"
|
|
||||||
>
|
|
||||||
<FormInput
|
|
||||||
v-model="values[`heading_${level}_font_size`]"
|
|
||||||
type="number"
|
|
||||||
remove-number-input-controls
|
|
||||||
:min="fontSizeMin"
|
|
||||||
:max="fontSizeMax"
|
|
||||||
:error="$v.values[`heading_${level}_font_size`].$invalid"
|
|
||||||
@blur="$v.values[`heading_${level}_font_size`].$touch()"
|
|
||||||
>
|
>
|
||||||
<template #suffix>px</template>
|
<FontFamilySelector
|
||||||
</FormInput>
|
v-model="values[`heading_${level}_font_family`]"
|
||||||
|
|
||||||
<template #after-input>
|
|
||||||
<ResetButton
|
|
||||||
v-model="values"
|
|
||||||
:theme="theme"
|
|
||||||
:property="`body_font_size`"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
<template #after-input>
|
||||||
</FormGroup>
|
<ResetButton
|
||||||
|
v-model="values[`heading_${level}_font_family`]"
|
||||||
<FormGroup
|
:default-value="theme?.[`heading_${level}_font_family`]"
|
||||||
horizontal
|
/>
|
||||||
small-label
|
</template>
|
||||||
:label="$t('typographyThemeConfigBlock.color')"
|
</FormGroup>
|
||||||
>
|
<FormGroup
|
||||||
<ColorInput
|
horizontal
|
||||||
v-model="values[`heading_${level}_text_color`]"
|
small-label
|
||||||
:color-variables="colorVariables"
|
class="margin-bottom-1"
|
||||||
:default-value="theme ? theme[`heading_${level}_text_color`] : null"
|
:label="$t('typographyThemeConfigBlock.textAlignment')"
|
||||||
small
|
>
|
||||||
/>
|
<HorizontalAlignmentsSelector
|
||||||
<template #after-input>
|
v-model="values[`heading_${level}_text_alignment`]"
|
||||||
<ResetButton
|
|
||||||
v-model="values"
|
|
||||||
:theme="theme"
|
|
||||||
:property="`heading_${level}_text_color`"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
<template #after-input>
|
||||||
</FormGroup>
|
<ResetButton
|
||||||
</template>
|
v-model="values[`heading_${level}_text_alignment`]"
|
||||||
<template #preview>
|
:default-value="theme?.[`heading_${level}_text_alignment`]"
|
||||||
<component
|
/>
|
||||||
:is="`h${level}`"
|
</template>
|
||||||
class="margin-bottom-2 theme-settings__section-ellipsis"
|
</FormGroup>
|
||||||
:class="`ab-heading--h${level}`"
|
<FormGroup
|
||||||
>
|
horizontal
|
||||||
{{ $t('typographyThemeConfigBlock.headingValue', { i: level }) }}
|
small-label
|
||||||
</component>
|
class="margin-bottom-1"
|
||||||
</template>
|
:label="$t('typographyThemeConfigBlock.size')"
|
||||||
</ThemeConfigBlockSection>
|
:error-message="
|
||||||
|
$v.values[`heading_${level}_font_size`].$invalid
|
||||||
|
? $t('error.minMaxValueField', {
|
||||||
|
min: fontSizeMin,
|
||||||
|
max: fontSizeMax,
|
||||||
|
})
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<PixelValueSelector
|
||||||
|
v-model="values[`heading_${level}_font_size`]"
|
||||||
|
class="typography-theme-config-block__input-number"
|
||||||
|
@blur="$v.values[`heading_${level}_font_size`].$touch()"
|
||||||
|
/>
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values[`heading_${level}_font_size`]"
|
||||||
|
:default-value="theme?.[`heading_${level}_font_size`]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
horizontal
|
||||||
|
small-label
|
||||||
|
class="margin-bottom-1"
|
||||||
|
:label="$t('typographyThemeConfigBlock.color')"
|
||||||
|
>
|
||||||
|
<ColorInput
|
||||||
|
v-model="values[`heading_${level}_text_color`]"
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
:default-value="
|
||||||
|
theme ? theme[`heading_${level}_text_color`] : null
|
||||||
|
"
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
<template #after-input>
|
||||||
|
<ResetButton
|
||||||
|
v-model="values[`heading_${level}_text_color`]"
|
||||||
|
:default-value="theme?.[`heading_${level}_text_color`]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</FormGroup>
|
||||||
|
</template>
|
||||||
|
<template #preview>
|
||||||
|
<ABHeading
|
||||||
|
class="typography-theme-config-block__heading-preview"
|
||||||
|
:level="level"
|
||||||
|
>
|
||||||
|
{{ $t('typographyThemeConfigBlock.headingValue', { i: level }) }}
|
||||||
|
</ABHeading>
|
||||||
|
</template>
|
||||||
|
</ThemeConfigBlockSection>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -165,9 +192,12 @@ import themeConfigBlock from '@baserow/modules/builder/mixins/themeConfigBlock'
|
||||||
import ThemeConfigBlockSection from '@baserow/modules/builder/components/theme/ThemeConfigBlockSection'
|
import ThemeConfigBlockSection from '@baserow/modules/builder/components/theme/ThemeConfigBlockSection'
|
||||||
import ResetButton from '@baserow/modules/builder/components/theme/ResetButton'
|
import ResetButton from '@baserow/modules/builder/components/theme/ResetButton'
|
||||||
import HorizontalAlignmentsSelector from '@baserow/modules/builder/components/HorizontalAlignmentsSelector'
|
import HorizontalAlignmentsSelector from '@baserow/modules/builder/components/HorizontalAlignmentsSelector'
|
||||||
|
import FontFamilySelector from '@baserow/modules/builder/components/FontFamilySelector'
|
||||||
|
import PixelValueSelector from '@baserow/modules/builder/components/PixelValueSelector'
|
||||||
|
|
||||||
const fontSizeMin = 1
|
const fontSizeMin = 1
|
||||||
const fontSizeMax = 100
|
const fontSizeMax = 100
|
||||||
|
const bodyFontSizeMax = 30
|
||||||
const headings = [1, 2, 3, 4, 5, 6]
|
const headings = [1, 2, 3, 4, 5, 6]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -176,23 +206,13 @@ export default {
|
||||||
ThemeConfigBlockSection,
|
ThemeConfigBlockSection,
|
||||||
ResetButton,
|
ResetButton,
|
||||||
HorizontalAlignmentsSelector,
|
HorizontalAlignmentsSelector,
|
||||||
|
FontFamilySelector,
|
||||||
|
PixelValueSelector,
|
||||||
},
|
},
|
||||||
mixins: [themeConfigBlock],
|
mixins: [themeConfigBlock],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
values: {},
|
values: {},
|
||||||
allowedValues: [
|
|
||||||
...headings
|
|
||||||
.map((level) => [
|
|
||||||
`heading_${level}_text_color`,
|
|
||||||
`heading_${level}_font_size`,
|
|
||||||
`heading_${level}_text_alignment`,
|
|
||||||
])
|
|
||||||
.flat(),
|
|
||||||
'body_font_size',
|
|
||||||
'body_text_alignment',
|
|
||||||
'body_text_color',
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -206,12 +226,23 @@ export default {
|
||||||
showBody() {
|
showBody() {
|
||||||
return !this.extraArgs?.headingLevel
|
return !this.extraArgs?.headingLevel
|
||||||
},
|
},
|
||||||
|
showHeadings() {
|
||||||
|
return !this.extraArgs?.onlyBody
|
||||||
|
},
|
||||||
fontSizeMin() {
|
fontSizeMin() {
|
||||||
return fontSizeMin
|
return fontSizeMin
|
||||||
},
|
},
|
||||||
fontSizeMax() {
|
fontSizeMax() {
|
||||||
return fontSizeMax
|
return fontSizeMax
|
||||||
},
|
},
|
||||||
|
bodyFontSizeMax() {
|
||||||
|
return bodyFontSizeMax
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isAllowedKey(key) {
|
||||||
|
return key.startsWith('heading_') || key.startsWith('body_')
|
||||||
|
},
|
||||||
},
|
},
|
||||||
validations: {
|
validations: {
|
||||||
values: {
|
values: {
|
||||||
|
@ -228,7 +259,7 @@ export default {
|
||||||
required,
|
required,
|
||||||
integer,
|
integer,
|
||||||
minValue: minValue(fontSizeMin),
|
minValue: minValue(fontSizeMin),
|
||||||
maxValue: maxValue(fontSizeMax),
|
maxValue: maxValue(bodyFontSizeMax),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -73,12 +73,18 @@ export class ElementType extends Registerable {
|
||||||
'style_padding_bottom',
|
'style_padding_bottom',
|
||||||
'style_padding_left',
|
'style_padding_left',
|
||||||
'style_padding_right',
|
'style_padding_right',
|
||||||
|
'style_margin_top',
|
||||||
|
'style_margin_bottom',
|
||||||
|
'style_margin_left',
|
||||||
|
'style_margin_right',
|
||||||
'style_border_top',
|
'style_border_top',
|
||||||
'style_border_bottom',
|
'style_border_bottom',
|
||||||
'style_border_left',
|
'style_border_left',
|
||||||
'style_border_right',
|
'style_border_right',
|
||||||
'style_background',
|
'style_background',
|
||||||
'style_background_color',
|
'style_background_color',
|
||||||
|
'style_background_file',
|
||||||
|
'style_background_mode',
|
||||||
'style_width',
|
'style_width',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,15 +72,22 @@ export const WIDTHS_NEW = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BACKGROUND_TYPES = {
|
export const BACKGROUND_TYPES = {
|
||||||
NONE: { value: 'none', name: 'backgroundTypes.none' },
|
NONE: 'none',
|
||||||
COLOR: { value: 'color', name: 'backgroundTypes.color' },
|
COLOR: 'color',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BACKGROUND_MODES = {
|
||||||
|
FILL: 'fill',
|
||||||
|
TILE: 'tile',
|
||||||
|
FIT: 'fit',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WIDTH_TYPES = {
|
export const WIDTH_TYPES = {
|
||||||
FULL: { value: 'full', name: 'widthTypes.full' },
|
|
||||||
NORMAL: { value: 'normal', name: 'widthTypes.normal' },
|
|
||||||
MEDIUM: { value: 'medium', name: 'widthTypes.medium' },
|
|
||||||
SMALL: { value: 'small', name: 'widthTypes.small' },
|
SMALL: { value: 'small', name: 'widthTypes.small' },
|
||||||
|
MEDIUM: { value: 'medium', name: 'widthTypes.medium' },
|
||||||
|
NORMAL: { value: 'normal', name: 'widthTypes.normal' },
|
||||||
|
FULL: { value: 'full', name: 'widthTypes.fullBleed' },
|
||||||
|
FULL_WIDTH: { value: 'full-width', name: 'widthTypes.fullWidth' },
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
131
web-frontend/modules/builder/fontFamilyTypes.js
Normal file
131
web-frontend/modules/builder/fontFamilyTypes.js
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import { Registerable } from '@baserow/modules/core/registry'
|
||||||
|
|
||||||
|
export class FontFamilyType extends Registerable {
|
||||||
|
get name() {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
get safeFont() {
|
||||||
|
return 'sans-serif'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InterFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'inter'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Inter'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ArialFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'arial'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Arial'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VerdanaFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'verdana'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Verdana'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TahomaFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'tahoma'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Tahoma'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrebuchetMSFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'trebuchet_ms'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Trebuchet MS'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TimesNewRomanFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'times_new_roman'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Times new roman'
|
||||||
|
}
|
||||||
|
|
||||||
|
get safeFont() {
|
||||||
|
return 'serif'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GeorgiaFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'georgia'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Georgia'
|
||||||
|
}
|
||||||
|
|
||||||
|
get safeFont() {
|
||||||
|
return 'serif'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GaramondFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'garamond'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Garamond'
|
||||||
|
}
|
||||||
|
|
||||||
|
get safeFont() {
|
||||||
|
return 'serif'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CourierNewFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'courier_new'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Courier new'
|
||||||
|
}
|
||||||
|
|
||||||
|
get safeFont() {
|
||||||
|
return 'monospace'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BrushScriptMTFontFamilyType extends FontFamilyType {
|
||||||
|
static getType() {
|
||||||
|
return 'brush_script_mt'
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'Brush Script MT'
|
||||||
|
}
|
||||||
|
|
||||||
|
get safeFont() {
|
||||||
|
return 'cursive'
|
||||||
|
}
|
||||||
|
}
|
|
@ -159,10 +159,6 @@
|
||||||
"imageElement": {
|
"imageElement": {
|
||||||
"emptyState": "No alt text defined..."
|
"emptyState": "No alt text defined..."
|
||||||
},
|
},
|
||||||
"imageInput": {
|
|
||||||
"labelDescription": "Default description",
|
|
||||||
"labelButton": "Upload"
|
|
||||||
},
|
|
||||||
"generalForm": {
|
"generalForm": {
|
||||||
"labelTitle": "Label",
|
"labelTitle": "Label",
|
||||||
"labelPlaceholder": "Enter a label (optional)",
|
"labelPlaceholder": "Enter a label (optional)",
|
||||||
|
@ -300,7 +296,8 @@
|
||||||
"color": "Color"
|
"color": "Color"
|
||||||
},
|
},
|
||||||
"widthTypes": {
|
"widthTypes": {
|
||||||
"full": "Full width",
|
"fullBleed": "Full bleed",
|
||||||
|
"fullWidth": "Full width",
|
||||||
"normal": "Normal",
|
"normal": "Normal",
|
||||||
"medium": "Medium",
|
"medium": "Medium",
|
||||||
"small": "Small"
|
"small": "Small"
|
||||||
|
@ -364,14 +361,19 @@
|
||||||
"boxRight": "Right",
|
"boxRight": "Right",
|
||||||
"backgroundLabel": "Background",
|
"backgroundLabel": "Background",
|
||||||
"backgroundColor": "Background color",
|
"backgroundColor": "Background color",
|
||||||
"widthLabel": "Width"
|
"widthLabel": "Width",
|
||||||
|
"backgroundImage": "Image",
|
||||||
|
"backgroundImageMode": "Fill mode"
|
||||||
},
|
},
|
||||||
"styleBoxForm": {
|
"styleBoxForm": {
|
||||||
"borderLabel": "Border",
|
"borderColor": "Border color",
|
||||||
"paddingLabel": "Padding"
|
"borderLabel": "Size",
|
||||||
|
"paddingLabel": "Padding",
|
||||||
|
"marginLabel": "Margin"
|
||||||
},
|
},
|
||||||
"themeConfigBlockType": {
|
"themeConfigBlockType": {
|
||||||
"color": "Colors",
|
"color": "Colors",
|
||||||
|
"page": "Page",
|
||||||
"typography": "Typography",
|
"typography": "Typography",
|
||||||
"button": "Button",
|
"button": "Button",
|
||||||
"link": "Link",
|
"link": "Link",
|
||||||
|
@ -380,12 +382,23 @@
|
||||||
"colorThemeConfigBlock": {
|
"colorThemeConfigBlock": {
|
||||||
"primaryColor": "Primary",
|
"primaryColor": "Primary",
|
||||||
"secondaryColor": "Secondary",
|
"secondaryColor": "Secondary",
|
||||||
"borderColor": "Border"
|
"borderColor": "Border",
|
||||||
|
"successColor": "Success",
|
||||||
|
"warningColor": "Warning",
|
||||||
|
"errorColor": "Error"
|
||||||
|
},
|
||||||
|
"pageThemeConfigBlock": {
|
||||||
|
"backgroundColor": "Background color",
|
||||||
|
"backgroundImage": "Background image",
|
||||||
|
"backgroundMode": "Background mode"
|
||||||
},
|
},
|
||||||
"colorThemeConfigBlockType": {
|
"colorThemeConfigBlockType": {
|
||||||
"primary": "Primary",
|
"primary": "Primary",
|
||||||
"secondary": "Secondary",
|
"secondary": "Secondary",
|
||||||
"border": "Border"
|
"border": "Border",
|
||||||
|
"success": "Success",
|
||||||
|
"warning": "Warning",
|
||||||
|
"error": "Error"
|
||||||
},
|
},
|
||||||
"typographyThemeConfigBlock": {
|
"typographyThemeConfigBlock": {
|
||||||
"headingLabel": "Heading {i} (h{i})",
|
"headingLabel": "Heading {i} (h{i})",
|
||||||
|
@ -393,7 +406,8 @@
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"textAlignment": "Alignment",
|
"textAlignment": "Alignment",
|
||||||
"bodyLabel": "Body"
|
"bodyLabel": "Body",
|
||||||
|
"fontFamily": "Font"
|
||||||
},
|
},
|
||||||
"buttonThemeConfigBlock": {
|
"buttonThemeConfigBlock": {
|
||||||
"backgroundColor": "Background color",
|
"backgroundColor": "Background color",
|
||||||
|
@ -402,14 +416,22 @@
|
||||||
"hoverState": "Hover state",
|
"hoverState": "Hover state",
|
||||||
"textAlignment": "Text alignment",
|
"textAlignment": "Text alignment",
|
||||||
"alignment": "Alignment",
|
"alignment": "Alignment",
|
||||||
"width": "Width"
|
"width": "Width",
|
||||||
|
"textColor": "Text color",
|
||||||
|
"borderColor": "Border color",
|
||||||
|
"borderSize": "Border size",
|
||||||
|
"borderRadius": "Border radius",
|
||||||
|
"padding": "Padding",
|
||||||
|
"fontFamily": "Font",
|
||||||
|
"size": "Font size"
|
||||||
},
|
},
|
||||||
"linkThemeConfigBlock": {
|
"linkThemeConfigBlock": {
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"link": "Link",
|
"link": "Link",
|
||||||
"defaultState": "Default state",
|
"defaultState": "Default state",
|
||||||
"hoverState": "Hover state",
|
"hoverState": "Hover state",
|
||||||
"alignment": "Alignment"
|
"alignment": "Alignment",
|
||||||
|
"fontFamily": "Font"
|
||||||
},
|
},
|
||||||
"imageThemeConfigBlock": {
|
"imageThemeConfigBlock": {
|
||||||
"alignment": "Alignment",
|
"alignment": "Alignment",
|
||||||
|
@ -644,5 +666,13 @@
|
||||||
},
|
},
|
||||||
"resetButton": {
|
"resetButton": {
|
||||||
"reset": "Reset to default theme value"
|
"reset": "Reset to default theme value"
|
||||||
|
},
|
||||||
|
"backgroundModes": {
|
||||||
|
"fill": "Fill",
|
||||||
|
"tile": "Tile",
|
||||||
|
"fit": "Fit"
|
||||||
|
},
|
||||||
|
"customStyle": {
|
||||||
|
"themeOverrides": "Theme overrides"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,7 @@ export default {
|
||||||
getStyleOverride(key, colorVariables = null) {
|
getStyleOverride(key, colorVariables = null) {
|
||||||
return ThemeConfigBlockType.getAllStyles(
|
return ThemeConfigBlockType.getAllStyles(
|
||||||
this.themeConfigBlocks,
|
this.themeConfigBlocks,
|
||||||
this.element.styles[key] || {},
|
this.element.styles?.[key] || {},
|
||||||
colorVariables || this.colorVariables,
|
colorVariables || this.colorVariables,
|
||||||
this.builder.theme
|
this.builder.theme
|
||||||
)
|
)
|
||||||
|
|
|
@ -62,6 +62,7 @@ export default {
|
||||||
},
|
},
|
||||||
getBoxStyleValue(pos) {
|
getBoxStyleValue(pos) {
|
||||||
return {
|
return {
|
||||||
|
margin: this.defaultValues[`style_margin_${pos}`],
|
||||||
padding: this.defaultValues[`style_padding_${pos}`],
|
padding: this.defaultValues[`style_padding_${pos}`],
|
||||||
border_color: this.defaultValues[`style_border_${pos}_color`],
|
border_color: this.defaultValues[`style_border_${pos}_color`],
|
||||||
border_size: this.defaultValues[`style_border_${pos}_size`],
|
border_size: this.defaultValues[`style_border_${pos}_size`],
|
||||||
|
@ -69,6 +70,7 @@ export default {
|
||||||
},
|
},
|
||||||
setBoxStyleValue(pos, newValue) {
|
setBoxStyleValue(pos, newValue) {
|
||||||
if (newValue.padding !== undefined) {
|
if (newValue.padding !== undefined) {
|
||||||
|
this.values[`style_margin_${pos}`] = newValue.margin
|
||||||
this.values[`style_padding_${pos}`] = newValue.padding
|
this.values[`style_padding_${pos}`] = newValue.padding
|
||||||
this.values[`style_border_${pos}_color`] = newValue.border_color
|
this.values[`style_border_${pos}_color`] = newValue.border_color
|
||||||
this.values[`style_border_${pos}_size`] = newValue.border_size
|
this.values[`style_border_${pos}_size`] = newValue.border_size
|
||||||
|
@ -104,7 +106,8 @@ export default {
|
||||||
},
|
},
|
||||||
getValuesFromElement(allowedValues) {
|
getValuesFromElement(allowedValues) {
|
||||||
return allowedValues.reduce((obj, value) => {
|
return allowedValues.reduce((obj, value) => {
|
||||||
obj[value] = this.element[value] || null
|
obj[value] =
|
||||||
|
this.element[value] === undefined ? null : this.element[value]
|
||||||
return obj
|
return obj
|
||||||
}, {})
|
}, {})
|
||||||
},
|
},
|
||||||
|
|
|
@ -90,6 +90,7 @@ import {
|
||||||
ButtonThemeConfigBlockType,
|
ButtonThemeConfigBlockType,
|
||||||
LinkThemeConfigBlockType,
|
LinkThemeConfigBlockType,
|
||||||
ImageThemeConfigBlockType,
|
ImageThemeConfigBlockType,
|
||||||
|
PageThemeConfigBlockType,
|
||||||
} from '@baserow/modules/builder/themeConfigBlockTypes'
|
} from '@baserow/modules/builder/themeConfigBlockTypes'
|
||||||
import {
|
import {
|
||||||
CreateRowWorkflowActionType,
|
CreateRowWorkflowActionType,
|
||||||
|
@ -108,6 +109,19 @@ import {
|
||||||
TagsCollectionFieldType,
|
TagsCollectionFieldType,
|
||||||
} from '@baserow/modules/builder/collectionFieldTypes'
|
} from '@baserow/modules/builder/collectionFieldTypes'
|
||||||
|
|
||||||
|
import {
|
||||||
|
InterFontFamilyType,
|
||||||
|
ArialFontFamilyType,
|
||||||
|
VerdanaFontFamilyType,
|
||||||
|
TahomaFontFamilyType,
|
||||||
|
TrebuchetMSFontFamilyType,
|
||||||
|
TimesNewRomanFontFamilyType,
|
||||||
|
GeorgiaFontFamilyType,
|
||||||
|
GaramondFontFamilyType,
|
||||||
|
CourierNewFontFamilyType,
|
||||||
|
BrushScriptMTFontFamilyType,
|
||||||
|
} from '@baserow/modules/builder/fontFamilyTypes'
|
||||||
|
|
||||||
export default (context) => {
|
export default (context) => {
|
||||||
const { store, app, isDev } = context
|
const { store, app, isDev } = context
|
||||||
|
|
||||||
|
@ -146,6 +160,7 @@ export default (context) => {
|
||||||
app.$registry.registerNamespace('pathParamType')
|
app.$registry.registerNamespace('pathParamType')
|
||||||
app.$registry.registerNamespace('builderDataProvider')
|
app.$registry.registerNamespace('builderDataProvider')
|
||||||
app.$registry.registerNamespace('themeConfigBlock')
|
app.$registry.registerNamespace('themeConfigBlock')
|
||||||
|
app.$registry.registerNamespace('fontFamily')
|
||||||
|
|
||||||
app.$registry.register('application', new BuilderApplicationType(context))
|
app.$registry.register('application', new BuilderApplicationType(context))
|
||||||
app.$registry.register('job', new DuplicatePageJobType(context))
|
app.$registry.register('job', new DuplicatePageJobType(context))
|
||||||
|
@ -267,6 +282,10 @@ export default (context) => {
|
||||||
'themeConfigBlock',
|
'themeConfigBlock',
|
||||||
new ImageThemeConfigBlockType(context)
|
new ImageThemeConfigBlockType(context)
|
||||||
)
|
)
|
||||||
|
app.$registry.register(
|
||||||
|
'themeConfigBlock',
|
||||||
|
new PageThemeConfigBlockType(context)
|
||||||
|
)
|
||||||
|
|
||||||
app.$registry.register(
|
app.$registry.register(
|
||||||
'workflowAction',
|
'workflowAction',
|
||||||
|
@ -313,4 +332,15 @@ export default (context) => {
|
||||||
'collectionField',
|
'collectionField',
|
||||||
new ButtonCollectionFieldType(context)
|
new ButtonCollectionFieldType(context)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app.$registry.register('fontFamily', new InterFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new ArialFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new VerdanaFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new TahomaFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new TrebuchetMSFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new TimesNewRomanFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new GeorgiaFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new GaramondFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new CourierNewFontFamilyType(context))
|
||||||
|
app.$registry.register('fontFamily', new BrushScriptMTFontFamilyType(context))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@ import TypographyThemeConfigBlock from '@baserow/modules/builder/components/them
|
||||||
import ButtonThemeConfigBlock from '@baserow/modules/builder/components/theme/ButtonThemeConfigBlock'
|
import ButtonThemeConfigBlock from '@baserow/modules/builder/components/theme/ButtonThemeConfigBlock'
|
||||||
import LinkThemeConfigBlock from '@baserow/modules/builder/components/theme/LinkThemeConfigBlock'
|
import LinkThemeConfigBlock from '@baserow/modules/builder/components/theme/LinkThemeConfigBlock'
|
||||||
import ImageThemeConfigBlock from '@baserow/modules/builder/components/theme/ImageThemeConfigBlock'
|
import ImageThemeConfigBlock from '@baserow/modules/builder/components/theme/ImageThemeConfigBlock'
|
||||||
|
import PageThemeConfigBlock from '@baserow/modules/builder/components/theme/PageThemeConfigBlock'
|
||||||
import { resolveColor } from '@baserow/modules/core/utils/colors'
|
import { resolveColor } from '@baserow/modules/core/utils/colors'
|
||||||
import {
|
import {
|
||||||
WIDTHS_NEW,
|
WIDTHS_NEW,
|
||||||
HORIZONTAL_ALIGNMENTS,
|
HORIZONTAL_ALIGNMENTS,
|
||||||
|
BACKGROUND_MODES,
|
||||||
} from '@baserow/modules/builder/enums'
|
} from '@baserow/modules/builder/enums'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
|
|
||||||
|
@ -118,11 +120,7 @@ export class ColorThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
}
|
}
|
||||||
|
|
||||||
getCSS(theme, colorVariables, baseTheme = null) {
|
getCSS(theme, colorVariables, baseTheme = null) {
|
||||||
const style = new ThemeStyle()
|
return {}
|
||||||
style.addIfExists(theme, 'primary_color', '--primary-color')
|
|
||||||
style.addIfExists(theme, 'secondary_color', '--secondary-color')
|
|
||||||
style.addIfExists(theme, 'border_color', '--border-color')
|
|
||||||
return style.toObject()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getColorVariables(theme) {
|
getColorVariables(theme) {
|
||||||
|
@ -143,6 +141,21 @@ export class ColorThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
value: 'border',
|
value: 'border',
|
||||||
color: theme.border_color,
|
color: theme.border_color,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t('colorThemeConfigBlockType.success'),
|
||||||
|
value: 'success',
|
||||||
|
color: theme.main_success_color,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t('colorThemeConfigBlockType.warning'),
|
||||||
|
value: 'warning',
|
||||||
|
color: theme.main_warning_color,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t('colorThemeConfigBlockType.error'),
|
||||||
|
value: 'error',
|
||||||
|
color: theme.main_error_color,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +198,15 @@ export class TypographyThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
`--heading-h${level}-text-alignment`,
|
`--heading-h${level}-text-alignment`,
|
||||||
(v) => v
|
(v) => v
|
||||||
)
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
`heading_${level}_font_family`,
|
||||||
|
`--heading-h${level}-font-family`,
|
||||||
|
(v) => {
|
||||||
|
const fontFamilyType = this.app.$registry.get('fontFamily', v)
|
||||||
|
return `"${fontFamilyType.name}","${fontFamilyType.safeFont}"`
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
style.addIfExists(
|
style.addIfExists(
|
||||||
theme,
|
theme,
|
||||||
|
@ -201,6 +223,10 @@ export class TypographyThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
`--body-text-alignment`,
|
`--body-text-alignment`,
|
||||||
(v) => v
|
(v) => v
|
||||||
)
|
)
|
||||||
|
style.addIfExists(theme, `body_font_family`, `--body-font-family`, (v) => {
|
||||||
|
const fontFamilyType = this.app.$registry.get('fontFamily', v)
|
||||||
|
return `"${fontFamilyType.name}","${fontFamilyType.safeFont}"`
|
||||||
|
})
|
||||||
return style.toObject()
|
return style.toObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +259,28 @@ export class ButtonThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
style.addIfExists(
|
style.addIfExists(
|
||||||
theme,
|
theme,
|
||||||
'button_hover_background_color',
|
'button_hover_background_color',
|
||||||
'--hover-button-background-color',
|
'--button-hover-background-color',
|
||||||
|
(v) => resolveColor(v, colorVariables)
|
||||||
|
)
|
||||||
|
style.addIfExists(theme, 'button_text_color', '--button-text-color', (v) =>
|
||||||
|
resolveColor(v, colorVariables)
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
'button_hover_text_color',
|
||||||
|
'--button-hover-text-color',
|
||||||
|
(v) => resolveColor(v, colorVariables)
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
'button_border_color',
|
||||||
|
'--button-border-color',
|
||||||
|
(v) => resolveColor(v, colorVariables)
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
'button_hover_border_color',
|
||||||
|
'--button-hover-border-color',
|
||||||
(v) => resolveColor(v, colorVariables)
|
(v) => resolveColor(v, colorVariables)
|
||||||
)
|
)
|
||||||
style.addIfExists(theme, 'button_width', '--button-width', (v) =>
|
style.addIfExists(theme, 'button_width', '--button-width', (v) =>
|
||||||
|
@ -256,6 +303,51 @@ export class ButtonThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
[HORIZONTAL_ALIGNMENTS.RIGHT]: 'flex-end',
|
[HORIZONTAL_ALIGNMENTS.RIGHT]: 'flex-end',
|
||||||
}[v])
|
}[v])
|
||||||
)
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
'button_font_alignment',
|
||||||
|
'--button-text-alignment',
|
||||||
|
(v) => v
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
`button_font_family`,
|
||||||
|
`--button-font-family`,
|
||||||
|
(v) => {
|
||||||
|
const fontFamilyType = this.app.$registry.get('fontFamily', v)
|
||||||
|
return `"${fontFamilyType.name}","${fontFamilyType.safeFont}"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
`button_font_size`,
|
||||||
|
`--button-font-size`,
|
||||||
|
(v) => `${Math.min(100, v)}px`
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
`button_border_radius`,
|
||||||
|
`--button-border-radius`,
|
||||||
|
(v) => `${v}px`
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
`button_border_size`,
|
||||||
|
`--button-border-size`,
|
||||||
|
(v) => `${v}px`
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
`button_horizontal_padding`,
|
||||||
|
`--button-horizontal-padding`,
|
||||||
|
(v) => `${v}px`
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
`button_vertical_padding`,
|
||||||
|
`--button-vertical-padding`,
|
||||||
|
(v) => `${v}px`
|
||||||
|
)
|
||||||
return style.toObject()
|
return style.toObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,6 +391,10 @@ export class LinkThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
[HORIZONTAL_ALIGNMENTS.RIGHT]: 'flex-end',
|
[HORIZONTAL_ALIGNMENTS.RIGHT]: 'flex-end',
|
||||||
}[v])
|
}[v])
|
||||||
)
|
)
|
||||||
|
style.addIfExists(theme, `link_font_family`, `--link-font-family`, (v) => {
|
||||||
|
const fontFamilyType = this.app.$registry.get('fontFamily', v)
|
||||||
|
return `"${fontFamilyType.name}","${fontFamilyType.safeFont}"`
|
||||||
|
})
|
||||||
return style.toObject()
|
return style.toObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,31 +446,40 @@ export class ImageThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
baseTheme?.image_constraint
|
baseTheme?.image_constraint
|
||||||
)
|
)
|
||||||
|
|
||||||
style.style['--image-wrapper-width'] = `${imageMaxWidth}%`
|
if (Object.prototype.hasOwnProperty.call(theme, 'image_max_width')) {
|
||||||
style.style['--image-wrapper-max-width'] = `${imageMaxWidth}%`
|
style.style['--image-wrapper-width'] = `${imageMaxWidth}%`
|
||||||
|
style.style['--image-wrapper-max-width'] = `${imageMaxWidth}%`
|
||||||
if (imageMaxHeight) {
|
|
||||||
style.style['--image-max-width'] = 'auto'
|
|
||||||
style.style['--image-wrapper-max-height'] = `${imageMaxHeight}px`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (imageConstraint) {
|
if (Object.prototype.hasOwnProperty.call(theme, 'image_max_height')) {
|
||||||
case 'cover':
|
if (imageMaxHeight) {
|
||||||
style.style['--image-wrapper-width'] = '100%'
|
style.style['--image-max-width'] = 'auto'
|
||||||
style.style['--image-object-fit'] = 'cover'
|
style.style['--image-wrapper-max-height'] = `${imageMaxHeight}px`
|
||||||
style.style['--image-width'] = '100%'
|
} else {
|
||||||
style.style['--image-height'] = '100%'
|
style.style['--image-max-width'] = 'auto'
|
||||||
break
|
style.style['--image-wrapper-max-height'] = 'auto'
|
||||||
case 'contain':
|
}
|
||||||
style.style['--image-object-fit'] = 'contain'
|
}
|
||||||
style.style['--image-max-width'] = '100%'
|
|
||||||
break
|
if (Object.prototype.hasOwnProperty.call(theme, 'image_constraint')) {
|
||||||
case 'full-width':
|
switch (imageConstraint) {
|
||||||
style.style['--image-object-fit'] = 'fill'
|
case 'cover':
|
||||||
style.style['--image-width'] = '100%'
|
style.style['--image-wrapper-width'] = '100%'
|
||||||
style.style['--image-height'] = '100%'
|
style.style['--image-object-fit'] = 'cover'
|
||||||
style.style['--image-max-width'] = 'none'
|
style.style['--image-width'] = '100%'
|
||||||
break
|
style.style['--image-height'] = '100%'
|
||||||
|
break
|
||||||
|
case 'contain':
|
||||||
|
style.style['--image-object-fit'] = 'contain'
|
||||||
|
style.style['--image-max-width'] = '100%'
|
||||||
|
break
|
||||||
|
case 'full-width':
|
||||||
|
style.style['--image-object-fit'] = 'fill'
|
||||||
|
style.style['--image-width'] = '100%'
|
||||||
|
style.style['--image-height'] = '100%'
|
||||||
|
style.style['--image-max-width'] = 'none'
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return style.toObject()
|
return style.toObject()
|
||||||
|
@ -388,3 +493,50 @@ export class ImageThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
return 60
|
return 60
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PageThemeConfigBlockType extends ThemeConfigBlockType {
|
||||||
|
static getType() {
|
||||||
|
return 'page'
|
||||||
|
}
|
||||||
|
|
||||||
|
get label() {
|
||||||
|
return this.app.i18n.t('themeConfigBlockType.page')
|
||||||
|
}
|
||||||
|
|
||||||
|
getCSS(theme, colorVariables, baseTheme = null) {
|
||||||
|
const style = new ThemeStyle()
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
'page_background_color',
|
||||||
|
'--page-background-color',
|
||||||
|
(v) => resolveColor(v, colorVariables)
|
||||||
|
)
|
||||||
|
style.addIfExists(
|
||||||
|
theme,
|
||||||
|
'page_background_file',
|
||||||
|
'--page-background-image',
|
||||||
|
(v) => (v ? `url(${v.url})` : 'none')
|
||||||
|
)
|
||||||
|
if (theme.page_background_mode === BACKGROUND_MODES.FILL) {
|
||||||
|
style.style['--page-background-size'] = 'cover'
|
||||||
|
style.style['--page-background-repeat'] = 'no-repeat'
|
||||||
|
}
|
||||||
|
if (theme.page_background_mode === BACKGROUND_MODES.TILE) {
|
||||||
|
style.style['--page-background-size'] = 'auto'
|
||||||
|
style.style['--page-background-repeat'] = 'repeat'
|
||||||
|
}
|
||||||
|
if (theme.page_background_mode === BACKGROUND_MODES.FIT) {
|
||||||
|
style.style['--page-background-size'] = 'contain'
|
||||||
|
style.style['--page-background-repeat'] = 'no-repeat'
|
||||||
|
}
|
||||||
|
return style.toObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
get component() {
|
||||||
|
return PageThemeConfigBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrder() {
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,3 +31,5 @@
|
||||||
@import 'update_user_source_form';
|
@import 'update_user_source_form';
|
||||||
@import 'user_source_users_context';
|
@import 'user_source_users_context';
|
||||||
@import 'device_selector';
|
@import 'device_selector';
|
||||||
|
@import 'padding_selector';
|
||||||
|
@import 'page';
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
.element__wrapper {
|
.element__wrapper {
|
||||||
background-color: var(--background-color, $black);
|
background-color: var(--element-background-color, transparent);
|
||||||
border-top: var(--border-top, none);
|
background-image: var(--element-background-image, none);
|
||||||
border-bottom: var(--border-bottom, none);
|
background-size: var(--element-background-size, cover);
|
||||||
border-left: var(--border-left, none);
|
background-repeat: var(--element-background-repeat, no-repeat);
|
||||||
border-right: var(--border-right, none);
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: $builder-page-max-width;
|
max-width: $builder-page-max-width;
|
||||||
|
|
||||||
|
// We use padding here as margin to prevent margin collapsing
|
||||||
|
padding: var(--element-margin-top, 0) var(--element-margin-right, 0)
|
||||||
|
var(--element-margin-bottom, 0) var(--element-margin-left, 0);
|
||||||
|
|
||||||
|
&--full-bleed {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&--full-width {
|
&--full-width {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -21,22 +28,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.element__inner-wrapper {
|
.element__inner-wrapper {
|
||||||
padding: var(--padding-top, 0) var(--padding-right, 0)
|
border-top: var(--element-border-top, none);
|
||||||
var(--padding-bottom, 0) var(--padding-left, 0);
|
border-bottom: var(--element-border-bottom, none);
|
||||||
|
border-left: var(--element-border-left, none);
|
||||||
|
border-right: var(--element-border-right, none);
|
||||||
|
padding: var(--element-padding-top, 0) var(--element-padding-right, 0)
|
||||||
|
var(--element-padding-bottom, 0) var(--element-padding-left, 0);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: $builder-page-max-width;
|
max-width: $builder-page-max-width;
|
||||||
|
|
||||||
&--full-width {
|
.element__wrapper--full-width & {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--medium-width {
|
|
||||||
max-width: 960px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--small-width {
|
|
||||||
max-width: 680px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.element {
|
.element {
|
||||||
|
|
|
@ -1,52 +1,26 @@
|
||||||
.ab-button {
|
.ab-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-block;
|
|
||||||
color: $white;
|
|
||||||
background-color: var(--button-background-color, $black);
|
|
||||||
line-height: 28px;
|
|
||||||
border: none;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
line-height: 1em;
|
||||||
|
color: var(--button-text-color, $white);
|
||||||
|
background-color: var(--button-background-color, $black);
|
||||||
|
font-size: var(--button-font-size, 12px);
|
||||||
|
border: var(--button-border-size, 0) solid var(--button-border-color, black);
|
||||||
|
border-radius: var(--button-border-radius, 4px);
|
||||||
width: var(--button-width, auto);
|
width: var(--button-width, auto);
|
||||||
text-align: var(--button-text-alignment, center);
|
text-align: var(--button-text-alignment, center);
|
||||||
align-self: var(--button-alignment, flex-start);
|
align-self: var(--button-alignment, flex-start);
|
||||||
|
font-family: var(--button-font-family, Inter);
|
||||||
@include rounded($rounded);
|
padding: var(--button-vertical-padding, 4px)
|
||||||
|
var(--button-horizontal-padding, 12px);
|
||||||
&--full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--small {
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--medium {
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 0 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--large {
|
|
||||||
font-size: 15px;
|
|
||||||
padding: 5px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.loading-spinner):hover,
|
&:not(.loading-spinner):hover,
|
||||||
&:not(.loading-spinner).ab-button--force-hover {
|
&:not(.loading-spinner).ab-button--force-hover {
|
||||||
background-color: var(--hover-button-background-color, $black);
|
background-color: var(--button-hover-background-color, $black);
|
||||||
|
border-color: var(--button-hover-border-color, $white);
|
||||||
|
color: var(--button-hover-text-color, $white);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,35 +6,41 @@
|
||||||
color: var(--heading-h1-color, $black);
|
color: var(--heading-h1-color, $black);
|
||||||
font-size: var(--heading-h1-font-size, 30px);
|
font-size: var(--heading-h1-font-size, 30px);
|
||||||
text-align: var(--heading-h1-text-alignment, left);
|
text-align: var(--heading-h1-text-alignment, left);
|
||||||
|
font-family: var(--heading-h1-font-family, Inter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ab-heading--h2 {
|
.ab-heading--h2 {
|
||||||
color: var(--heading-h2-color, $black);
|
color: var(--heading-h2-color, $black);
|
||||||
font-size: var(--heading-h2-font-size, 26px);
|
font-size: var(--heading-h2-font-size, 26px);
|
||||||
text-align: var(--heading-h2-text-alignment, left);
|
text-align: var(--heading-h2-text-alignment, left);
|
||||||
|
font-family: var(--heading-h2-font-family, Inter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ab-heading--h3 {
|
.ab-heading--h3 {
|
||||||
color: var(--heading-h3-color, $black);
|
color: var(--heading-h3-color, $black);
|
||||||
font-size: var(--heading-h3-font-size, 22px);
|
font-size: var(--heading-h3-font-size, 22px);
|
||||||
text-align: var(--heading-h3-text-alignment, left);
|
text-align: var(--heading-h3-text-alignment, left);
|
||||||
|
font-family: var(--heading-h3-font-family, Inter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ab-heading--h4 {
|
.ab-heading--h4 {
|
||||||
color: var(--heading-h4-color, $black);
|
color: var(--heading-h4-color, $black);
|
||||||
font-size: var(--heading-h4-font-size, 18px);
|
font-size: var(--heading-h4-font-size, 18px);
|
||||||
text-align: var(--heading-h4-text-alignment, left);
|
text-align: var(--heading-h4-text-alignment, left);
|
||||||
|
font-family: var(--heading-h4-font-family, Inter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ab-heading--h5 {
|
.ab-heading--h5 {
|
||||||
color: var(--heading-h5-color, $black);
|
color: var(--heading-h5-color, $black);
|
||||||
font-size: var(--heading-h5-font-size, 14px);
|
font-size: var(--heading-h5-font-size, 14px);
|
||||||
text-align: var(--heading-h5-text-alignment, left);
|
text-align: var(--heading-h5-text-alignment, left);
|
||||||
|
font-family: var(--heading-h5-font-family, Inter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ab-heading--h6 {
|
.ab-heading--h6 {
|
||||||
color: var(--heading-h6-color, $black);
|
color: var(--heading-h6-color, $black);
|
||||||
font-size: var(--heading-h6-font-size, 14px);
|
font-size: var(--heading-h6-font-size, 14px);
|
||||||
text-align: var(--heading-h6-text-alignment, left);
|
text-align: var(--heading-h6-text-alignment, left);
|
||||||
|
font-family: var(--heading-h6-font-family, Inter);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
text-decoration: var(--link-text-decoration, underline);
|
text-decoration: var(--link-text-decoration, underline);
|
||||||
color: var(--link-text-color, $black);
|
color: var(--link-text-color, $black);
|
||||||
align-self: var(--link-text-alignment, flex-start);
|
align-self: var(--link-text-alignment, flex-start);
|
||||||
|
font-family: var(--link-font-family, Inter);
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&--force-hover {
|
&--force-hover {
|
||||||
|
|
|
@ -3,4 +3,10 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--body-text-color, $black);
|
color: var(--body-text-color, $black);
|
||||||
text-align: var(--body-text-alignment, left);
|
text-align: var(--body-text-alignment, left);
|
||||||
|
font-family: var(--body-font-family, Inter);
|
||||||
|
|
||||||
|
// for markdown text
|
||||||
|
strong {
|
||||||
|
color: var(--body-text-color, $black);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
.padding-selector {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-selector__input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
.page {
|
||||||
|
background-color: var(--page-background-color, #fff);
|
||||||
|
background-image: var(--page-background-image, none);
|
||||||
|
background-size: var(--page-background-size, cover);
|
||||||
|
background-repeat: var(--page-background-repeat, no-repeat);
|
||||||
|
|
||||||
|
.public-page & {
|
||||||
|
// We want to fill the screen when it's the published version
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,10 +35,19 @@
|
||||||
.page-preview__scaled {
|
.page-preview__scaled {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
background-color: $white;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
// These properties are duplicated from the page element because during the SSR
|
||||||
|
// the screen size is fixed to an arbitrary value and when returned to the browser
|
||||||
|
// there is a small gap at the bottom of the screen between the end of the `.page` and
|
||||||
|
// the end of this element. By duplicating them, we hide the difference before the
|
||||||
|
// front hydratation.
|
||||||
|
background-color: var(--page-background-color, #fff);
|
||||||
|
background-image: var(--page-background-image, none);
|
||||||
|
background-size: var(--page-background-size, cover);
|
||||||
|
background-repeat: var(--page-background-repeat, no-repeat);
|
||||||
|
|
||||||
// We need to do this because the border of the preview is round and we need to round
|
// We need to do this because the border of the preview is round and we need to round
|
||||||
// the border of the selection box so that border has to have the same shape.
|
// the border of the selection box so that border has to have the same shape.
|
||||||
> div:first-child {
|
> div:first-child {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
place-content: center space-between;
|
place-content: center space-between;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
width: 60%;
|
width: 60%;
|
||||||
|
padding: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -24,6 +25,25 @@
|
||||||
.theme-config-block {
|
.theme-config-block {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
& .control--horizontal {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
.control__label {
|
||||||
|
flex-basis: 40%;
|
||||||
|
color: $color-neutral-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control__elements {
|
||||||
|
flex-basis: 60%;
|
||||||
|
white-space: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control__wrapper {
|
||||||
|
flex-basis: 60%;
|
||||||
|
white-space: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:not(.theme-config-block--no-preview)::after {
|
&:not(.theme-config-block--no-preview)::after {
|
||||||
@include absolute(0, calc(40% - 14px), 0, auto);
|
@include absolute(0, calc(40% - 14px), 0, auto);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 28px;
|
gap: 28px;
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-config-block-section__properties {
|
.theme-config-block-section__properties {
|
||||||
|
@ -17,6 +18,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
max-width: calc(40% - 15px);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.theme-config-block--no-preview & {
|
.theme-config-block--no-preview & {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
.typography-theme-config-block__input-number {
|
.typography-theme-config-block__input-number {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.typography-theme-config-block__heading-preview {
|
||||||
|
@extend %ellipsis;
|
||||||
|
}
|
||||||
|
|
|
@ -3,9 +3,30 @@
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
position: relative;
|
||||||
border: 1px solid $palette-neutral-400;
|
border: 1px solid $palette-neutral-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-input__preview::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background-size: 14px 14px;
|
||||||
|
background-image: conic-gradient(
|
||||||
|
$white 90deg,
|
||||||
|
$color-neutral-400 90deg 180deg,
|
||||||
|
$white 180deg 270deg,
|
||||||
|
$color-neutral-400 270deg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-input__preview::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background-color: var(--selected-color, black);
|
||||||
|
}
|
||||||
|
|
||||||
.color-input__input {
|
.color-input__input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid $palette-neutral-400;
|
border: 1px solid $palette-neutral-400;
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
.color-picker {
|
.color-picker {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker__space {
|
.color-picker__space {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 230px 0 0;
|
|
||||||
height: 230px;
|
height: 230px;
|
||||||
|
width: 230px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid $color-neutral-200;
|
border: 1px solid $color-neutral-200;
|
||||||
background-image: linear-gradient(to top, #000, transparent),
|
background-image: linear-gradient(to top, #000, transparent),
|
||||||
|
@ -38,8 +39,7 @@
|
||||||
|
|
||||||
.color-picker__slider {
|
.color-picker__slider {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 18px 0 0;
|
width: 18px;
|
||||||
margin-left: 22px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid $color-neutral-200;
|
border: 1px solid $color-neutral-200;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
.color-picker-context {
|
.color-picker-context {
|
||||||
padding: 15px;
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker-context__color {
|
.color-picker-context__color {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
width: 310px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker-context__color-type {
|
.color-picker-context__color-type {
|
||||||
|
@ -13,25 +14,28 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker-context__color-hex {
|
.color-picker-context__color-hex {
|
||||||
flex-basis: 95px;
|
width: 95px;
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker-context__color-rgb {
|
.color-picker-context__color-rgb {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
margin: 0 5px;
|
width: 135px;
|
||||||
|
margin-left: 11px;
|
||||||
|
|
||||||
// input {
|
.form-input__input {
|
||||||
// width: 38px;
|
padding: 12px 5px;
|
||||||
// padding-left: 5px;
|
text-align: center;
|
||||||
// padding-right: 5px;
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker-context__color-opacity {
|
.color-picker-context__color-opacity {
|
||||||
flex: 0 0 72px;
|
width: 80px;
|
||||||
margin-left: auto;
|
|
||||||
|
.form-input__input {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-picker-context__variables {
|
.color-picker-context__variables {
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.control--error .form-input__input,
|
||||||
|
&.control--error .form-input__icon {
|
||||||
|
color: $palette-red-600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.control__elements--flex {
|
.control__elements--flex {
|
||||||
|
@ -180,3 +185,26 @@
|
||||||
|
|
||||||
@include flex-align-items(3px);
|
@include flex-align-items(3px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
border-bottom: 1px solid $color-neutral-200;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
// If we have an horizontal FormGroup
|
||||||
|
// these labels should be grayer for better effect.
|
||||||
|
& .control--horizontal .control__label {
|
||||||
|
color: $color-neutral-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section__title {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.form-input--error {
|
&.form-input--error {
|
||||||
border-color: $palette-red-600;
|
color: $palette-red-600;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.form-input--disabled {
|
&.form-input--disabled {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.image-input {
|
.image-input {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,11 +9,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-input__image-placeholder-img {
|
.image-input__image-placeholder-img {
|
||||||
|
object-fit: contain;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-input__image-upload {
|
.image-input__image-upload {
|
||||||
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
@ -21,30 +23,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-input__image-upload-description {
|
.image-input__image-upload-description {
|
||||||
font-weight: lighter;
|
|
||||||
color: $palette-neutral-900;
|
color: $palette-neutral-900;
|
||||||
}
|
margin-bottom: 0;
|
||||||
|
line-height: 1em;
|
||||||
.image-input__thumbnail-remove {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
display: none;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
|
||||||
color: $color-error-500;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
:hover > & {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
ref="colorPicker"
|
ref="colorPicker"
|
||||||
:value="value"
|
:value="value"
|
||||||
:variables="localColorVariables"
|
:variables="localColorVariables"
|
||||||
|
:allow-opacity="allowOpacity"
|
||||||
@input="$emit('input', $event)"
|
@input="$emit('input', $event)"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
@ -15,7 +16,7 @@
|
||||||
<span
|
<span
|
||||||
class="color-input__preview"
|
class="color-input__preview"
|
||||||
:style="{
|
:style="{
|
||||||
'background-color': actualValue,
|
'--selected-color': actualValue,
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<span>{{ displayValue }}</span>
|
<span>{{ displayValue }}</span>
|
||||||
|
@ -51,6 +52,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
allowOpacity: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
variablesMap() {
|
variablesMap() {
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
v-if="allowOpacity"
|
||||||
ref="alphaSpace"
|
ref="alphaSpace"
|
||||||
class="color-picker__slider color-picker__slider--alpha color-picker__thumb--negative-bottom-margin"
|
class="color-picker__slider color-picker__slider--alpha color-picker__thumb--negative-bottom-margin"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
|
@ -91,6 +92,11 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: '#ffffffff',
|
default: '#ffffffff',
|
||||||
},
|
},
|
||||||
|
allowOpacity: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<Context class="color-picker-context">
|
<Context class="color-picker-context">
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
:value="hexColorIncludingAlpha"
|
:value="hexColorIncludingAlpha"
|
||||||
|
:allow-opacity="allowOpacity"
|
||||||
@input="setColorFromPicker($event)"
|
@input="setColorFromPicker($event)"
|
||||||
></ColorPicker>
|
></ColorPicker>
|
||||||
<div class="color-picker-context__color">
|
<div class="color-picker-context__color">
|
||||||
|
@ -9,21 +10,18 @@
|
||||||
v-model="type"
|
v-model="type"
|
||||||
class="dropdown--floating color-picker-context__color-type"
|
class="dropdown--floating color-picker-context__color-type"
|
||||||
:show-search="false"
|
:show-search="false"
|
||||||
|
small
|
||||||
>
|
>
|
||||||
<DropdownItem name="Hex" :value="COLOR_NOTATIONS.HEX"></DropdownItem>
|
<DropdownItem name="Hex" :value="COLOR_NOTATIONS.HEX"></DropdownItem>
|
||||||
<DropdownItem name="RGB" :value="COLOR_NOTATIONS.RGB"></DropdownItem>
|
<DropdownItem name="RGB" :value="COLOR_NOTATIONS.RGB"></DropdownItem>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<div v-if="type === 'hex'" class="color-picker-context__color-hex">
|
<div v-if="type === 'hex'" class="color-picker-context__color-hex">
|
||||||
<FormInput
|
<FormInput small :value="hexColorExcludingAlpha" @input="hexChanged" />
|
||||||
size="large"
|
|
||||||
:value="hexColorExcludingAlpha"
|
|
||||||
@input="hexChanged"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="type === 'rgb'" class="color-picker-context__color-rgb">
|
<div v-if="type === 'rgb'" class="color-picker-context__color-rgb">
|
||||||
<FormInput
|
<FormInput
|
||||||
type="number"
|
type="number"
|
||||||
size="large"
|
small
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="255"
|
:max="255"
|
||||||
:value="r"
|
:value="r"
|
||||||
|
@ -32,7 +30,7 @@
|
||||||
/>
|
/>
|
||||||
<FormInput
|
<FormInput
|
||||||
type="number"
|
type="number"
|
||||||
size="large"
|
small
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="255"
|
:max="255"
|
||||||
:value="g"
|
:value="g"
|
||||||
|
@ -41,7 +39,7 @@
|
||||||
/>
|
/>
|
||||||
<FormInput
|
<FormInput
|
||||||
type="number"
|
type="number"
|
||||||
size="large"
|
small
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="255"
|
:max="255"
|
||||||
:value="b"
|
:value="b"
|
||||||
|
@ -49,17 +47,18 @@
|
||||||
@input="rgbaChanged($event, 'b')"
|
@input="rgbaChanged($event, 'b')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="color-picker-context__color-opacity">
|
<div class="flex-grow-1" />
|
||||||
|
<div v-if="allowOpacity" class="color-picker-context__color-opacity">
|
||||||
<FormInput
|
<FormInput
|
||||||
type="number"
|
type="number"
|
||||||
size="large"
|
small
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:value="a"
|
:value="a"
|
||||||
remove-number-input-controls
|
|
||||||
icon-right="iconoir-percentage"
|
icon-right="iconoir-percentage"
|
||||||
|
remove-number-input-controls
|
||||||
@input="rgbaChanged($event, 'a')"
|
@input="rgbaChanged($event, 'a')"
|
||||||
></FormInput>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -121,6 +120,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
allowOpacity: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -182,7 +186,12 @@ export default {
|
||||||
this.r = rgba.r * 255
|
this.r = rgba.r * 255
|
||||||
this.g = rgba.g * 255
|
this.g = rgba.g * 255
|
||||||
this.b = rgba.b * 255
|
this.b = rgba.b * 255
|
||||||
this.a = Math.round(rgba.a * 100)
|
|
||||||
|
if (this.allowOpacity) {
|
||||||
|
this.a = Math.round(rgba.a * 100)
|
||||||
|
} else {
|
||||||
|
this.a = 100
|
||||||
|
}
|
||||||
|
|
||||||
this.hexColorIncludingAlpha = convertRgbToHex(rgba)
|
this.hexColorIncludingAlpha = convertRgbToHex(rgba)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
'control--horizontal-variable': horizontalVariable,
|
'control--horizontal-variable': horizontalVariable,
|
||||||
'control--messages': hasMessages,
|
'control--messages': hasMessages,
|
||||||
'control--after-input': hasAfterInputSlot,
|
'control--after-input': hasAfterInputSlot,
|
||||||
|
'control--error': hasError,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
|
@ -42,7 +43,8 @@
|
||||||
<slot v-if="hasHelperSlot" name="helper" />
|
<slot v-if="hasHelperSlot" name="helper" />
|
||||||
</p>
|
</p>
|
||||||
<p v-if="hasError" class="control__messages--error">
|
<p v-if="hasError" class="control__messages--error">
|
||||||
<slot name="error" />
|
<slot v-if="hasErrorSlot" name="error" />
|
||||||
|
<template v-else-if="errorMessage">{{ errorMessage }}</template>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="hasWarningSlot" class="control__messages--warning">
|
<p v-if="hasWarningSlot" class="control__messages--warning">
|
||||||
<slot name="warning" />
|
<slot name="warning" />
|
||||||
|
@ -64,6 +66,14 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Shorthand when you don't need a specific error display.
|
||||||
|
*/
|
||||||
|
errorMessage: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* The id of the form group.
|
* The id of the form group.
|
||||||
*/
|
*/
|
||||||
|
@ -123,7 +133,10 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasError() {
|
hasError() {
|
||||||
return Boolean(this.error)
|
return Boolean(this.error) || Boolean(this.errorMessage)
|
||||||
|
},
|
||||||
|
hasErrorSlot() {
|
||||||
|
return !!this.$slots.error
|
||||||
},
|
},
|
||||||
hasLabelSlot() {
|
hasLabelSlot() {
|
||||||
return !!this.$slots.label
|
return !!this.$slots.label
|
||||||
|
|
19
web-frontend/modules/core/components/FormSection.vue
Normal file
19
web-frontend/modules/core/components/FormSection.vue
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<div class="form-section">
|
||||||
|
<h3 v-if="title" class="form-section__title">{{ title }}</h3>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FormSection',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,21 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="image-input">
|
<div class="image-input image-input--with-image">
|
||||||
<div class="image-input__image-placeholder">
|
<div v-if="imageUrl" class="image-input__image-placeholder">
|
||||||
<img class="image-input__image-placeholder-img" :src="imageUrl" />
|
<img class="image-input__image-placeholder-img" :src="imageUrl" />
|
||||||
<a
|
|
||||||
v-if="removable"
|
|
||||||
class="image-input__thumbnail-remove"
|
|
||||||
@click="$emit('input', null)"
|
|
||||||
>
|
|
||||||
<i class="iconoir-cancel"></i>
|
|
||||||
{{ $t('action.remove') }}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="image-input__image-upload">
|
||||||
<div class="image-input__image-upload">
|
<template v-if="!hasImage">
|
||||||
<span class="image-input__image-upload-description">
|
<p class="image-input__image-upload-description">
|
||||||
{{ labelDescription || $t('imageInput.labelDescription') }}
|
{{ labelDescription || $t('imageInput.labelDescription') }}
|
||||||
</span>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
icon="iconoir-upload-square"
|
icon="iconoir-upload-square"
|
||||||
type="upload"
|
type="upload"
|
||||||
|
@ -23,7 +15,14 @@
|
||||||
>
|
>
|
||||||
{{ labelButton || $t('imageInput.labelButton') }}
|
{{ labelButton || $t('imageInput.labelButton') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="image-input__image-delete">
|
||||||
|
<ButtonIcon
|
||||||
|
v-if="hasImage"
|
||||||
|
icon="iconoir-bin"
|
||||||
|
@click="$emit('input', null)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<UserFilesModal
|
<UserFilesModal
|
||||||
ref="userFilesModal"
|
ref="userFilesModal"
|
||||||
|
@ -75,7 +74,7 @@ export default {
|
||||||
defaultImage: {
|
defaultImage: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -83,14 +82,21 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
imageUrl() {
|
imageUrl() {
|
||||||
if (!this.value) {
|
if (this.value === null) {
|
||||||
return this.defaultImage
|
if (this.defaultImage) {
|
||||||
|
return this.defaultImage
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this.value.url
|
return this.value.url
|
||||||
},
|
},
|
||||||
removable() {
|
removable() {
|
||||||
return this.value !== null
|
return this.value !== null
|
||||||
},
|
},
|
||||||
|
hasImage() {
|
||||||
|
return this.value !== null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openFileUploadModal() {
|
openFileUploadModal() {
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
export const IMAGE_FILE_TYPES = ['image/jpeg', 'image/jpg', 'image/png']
|
export const IMAGE_FILE_TYPES = [
|
||||||
|
'image/jpeg',
|
||||||
|
'image/jpg',
|
||||||
|
'image/png',
|
||||||
|
'image/apng',
|
||||||
|
'image/gif',
|
||||||
|
'image/tiff',
|
||||||
|
'image/bmp',
|
||||||
|
'image/webp',
|
||||||
|
]
|
||||||
|
|
||||||
export const FAVICON_IMAGE_FILE_TYPES = [...IMAGE_FILE_TYPES, 'image/x-icon']
|
export const FAVICON_IMAGE_FILE_TYPES = [...IMAGE_FILE_TYPES, 'image/x-icon']
|
||||||
|
|
||||||
|
|
|
@ -711,5 +711,9 @@
|
||||||
},
|
},
|
||||||
"colorInput": {
|
"colorInput": {
|
||||||
"default": "Default"
|
"default": "Default"
|
||||||
|
},
|
||||||
|
"imageInput": {
|
||||||
|
"labelDescription": "Select an image to upload...",
|
||||||
|
"labelButton": "Upload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,17 +38,23 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
* Returns all the provided default values, but if the allowedValues are set
|
* Returns whether a key of the given defaultValue should be handled by this
|
||||||
* an object only containing those values is returned. This could be useful
|
* form component. This is useful when the defaultValues also contain other
|
||||||
* when the defaultValues also contain other values which must not be used
|
* values which must not be used when submitting. By default this implementation
|
||||||
* when submitting.
|
* is filtered by the list of `allowedValues`.
|
||||||
|
*/
|
||||||
|
isAllowedKey(key) {
|
||||||
|
if (this.allowedValues !== null) {
|
||||||
|
return this.allowedValues.includes(key)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Returns all the provided default values filtered by the `isAllowedKey` method.
|
||||||
*/
|
*/
|
||||||
getDefaultValues() {
|
getDefaultValues() {
|
||||||
if (this.allowedValues === null) {
|
|
||||||
return clone(this.defaultValues)
|
|
||||||
}
|
|
||||||
return Object.keys(this.defaultValues).reduce((result, key) => {
|
return Object.keys(this.defaultValues).reduce((result, key) => {
|
||||||
if (this.allowedValues.includes(key)) {
|
if (this.isAllowedKey(key)) {
|
||||||
let value = this.defaultValues[key]
|
let value = this.defaultValues[key]
|
||||||
|
|
||||||
// If the value is an array or object, it could be that it contains
|
// If the value is an array or object, it could be that it contains
|
||||||
|
|
|
@ -612,7 +612,7 @@
|
||||||
</template>
|
</template>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<Alert type="error" close-button>
|
<Alert type="danger" close-button>
|
||||||
<template #title>Alert title</template>
|
<template #title>Alert title</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<button
|
<button
|
||||||
|
@ -1375,7 +1375,7 @@
|
||||||
toggle error toast
|
toggle error toast
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="warning"
|
type="danger"
|
||||||
@click="
|
@click="
|
||||||
$store.dispatch('toast/warning', {
|
$store.dispatch('toast/warning', {
|
||||||
title: 'Custom warning toast',
|
title: 'Custom warning toast',
|
||||||
|
@ -1957,6 +1957,11 @@
|
||||||
{{ color }} - {{ resolveColor(color, colorVariables) }} <br /><br />
|
{{ color }} - {{ resolveColor(color, colorVariables) }} <br /><br />
|
||||||
<br />
|
<br />
|
||||||
<ColorInput v-model="color" :color-variables="colorVariables" />
|
<ColorInput v-model="color" :color-variables="colorVariables" />
|
||||||
|
<ColorInput
|
||||||
|
v-model="color"
|
||||||
|
:color-variables="colorVariables"
|
||||||
|
:allow-opacity="false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="margin-bottom-3">
|
<div class="margin-bottom-3">
|
||||||
|
|
|
@ -38,6 +38,7 @@ import FormGroup from '@baserow/modules/core/components/FormGroup'
|
||||||
import FormRow from '@baserow/modules/core/components/FormRow'
|
import FormRow from '@baserow/modules/core/components/FormRow'
|
||||||
import Logo from '@baserow/modules/core/components/Logo'
|
import Logo from '@baserow/modules/core/components/Logo'
|
||||||
import ReadOnlyForm from '@baserow/modules/core/components/ReadOnlyForm'
|
import ReadOnlyForm from '@baserow/modules/core/components/ReadOnlyForm'
|
||||||
|
import FormSection from '@baserow/modules/core/components/FormSection'
|
||||||
|
|
||||||
import lowercase from '@baserow/modules/core/filters/lowercase'
|
import lowercase from '@baserow/modules/core/filters/lowercase'
|
||||||
import uppercase from '@baserow/modules/core/filters/uppercase'
|
import uppercase from '@baserow/modules/core/filters/uppercase'
|
||||||
|
@ -105,6 +106,7 @@ function setupVue(Vue) {
|
||||||
Vue.component('SelectSearch', SelectSearch)
|
Vue.component('SelectSearch', SelectSearch)
|
||||||
Vue.component('Logo', Logo)
|
Vue.component('Logo', Logo)
|
||||||
Vue.component('ReadOnlyForm', ReadOnlyForm)
|
Vue.component('ReadOnlyForm', ReadOnlyForm)
|
||||||
|
Vue.component('FormSection', FormSection)
|
||||||
|
|
||||||
Vue.filter('lowercase', lowercase)
|
Vue.filter('lowercase', lowercase)
|
||||||
Vue.filter('uppercase', uppercase)
|
Vue.filter('uppercase', uppercase)
|
||||||
|
|
Loading…
Add table
Reference in a new issue