mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-03 04:35:31 +00:00
Merge branch '3360-allow-fully-round-image' into 'develop'
Image Element's border radius can use pixel or percentage Closes #3360 See merge request baserow/baserow!3073
This commit is contained in:
commit
d7541bb6cc
10 changed files with 312 additions and 31 deletions
backend
changelog/entries/unreleased/feature
web-frontend/modules/builder
|
@ -42,3 +42,8 @@ class FontWeights(models.TextChoices):
|
|||
HEAVY = "heavy"
|
||||
BLACK = "black"
|
||||
EXTRA_BLACK = "extra-black"
|
||||
|
||||
|
||||
class BorderRadius(models.TextChoices):
|
||||
PERCENT = "percent"
|
||||
PIXEL = "pixel"
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# Generated by Django 5.0.13 on 2025-03-26 08:12
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("builder", "0055_linkthemeconfigblock_link_active_text_decoration_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="imagethemeconfigblock",
|
||||
name="image_border_radius",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="imagethemeconfigblock",
|
||||
name="image_border_radius_percent",
|
||||
field=models.SmallIntegerField(
|
||||
db_default=0,
|
||||
default=0,
|
||||
help_text="The border radius percentage for this image element.",
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(
|
||||
0, message="Value cannot be less than 0."
|
||||
),
|
||||
django.core.validators.MaxValueValidator(
|
||||
50, message="Value cannot be greater than 50."
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="imagethemeconfigblock",
|
||||
name="image_border_radius_pixel",
|
||||
field=models.SmallIntegerField(
|
||||
db_default=0,
|
||||
default=0,
|
||||
help_text="The border radius pixels for this image element.",
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(
|
||||
0, message="Value cannot be less than 0."
|
||||
),
|
||||
django.core.validators.MaxValueValidator(
|
||||
100, message="Value cannot be greater than 100."
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="imagethemeconfigblock",
|
||||
name="image_border_radius_type",
|
||||
field=models.CharField(
|
||||
choices=[("percent", "Percent"), ("pixel", "Pixel")],
|
||||
db_default="pixel",
|
||||
default="pixel",
|
||||
help_text="The border radius type for this image element.",
|
||||
max_length=7,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -5,6 +5,7 @@ from baserow.contrib.builder.constants import (
|
|||
BACKGROUND_IMAGE_MODES,
|
||||
COLOR_FIELD_MAX_LENGTH,
|
||||
WIDTHS,
|
||||
BorderRadius,
|
||||
FontWeights,
|
||||
HorizontalAlignments,
|
||||
)
|
||||
|
@ -411,8 +412,26 @@ class ImageThemeConfigBlock(ThemeConfigBlock):
|
|||
],
|
||||
)
|
||||
|
||||
image_border_radius = models.SmallIntegerField(
|
||||
help_text="The border radius for this image element.",
|
||||
image_border_radius_type = models.CharField(
|
||||
help_text="The border radius type for this image element.",
|
||||
choices=BorderRadius.choices,
|
||||
max_length=7,
|
||||
default=BorderRadius.PIXEL,
|
||||
db_default=BorderRadius.PIXEL,
|
||||
)
|
||||
|
||||
image_border_radius_percent = models.SmallIntegerField(
|
||||
help_text="The border radius percentage for this image element.",
|
||||
validators=[
|
||||
MinValueValidator(0, message="Value cannot be less than 0."),
|
||||
MaxValueValidator(50, message="Value cannot be greater than 50."),
|
||||
],
|
||||
default=0,
|
||||
db_default=0,
|
||||
)
|
||||
|
||||
image_border_radius_pixel = models.SmallIntegerField(
|
||||
help_text="The border radius pixels for this image element.",
|
||||
validators=[
|
||||
MinValueValidator(0, message="Value cannot be less than 0."),
|
||||
MaxValueValidator(100, message="Value cannot be greater than 100."),
|
||||
|
|
|
@ -683,7 +683,9 @@ def test_builder_application_export(data_fixture):
|
|||
"link_default_text_decoration": [True, False, False, False],
|
||||
"link_hover_text_decoration": [True, False, False, False],
|
||||
"image_alignment": "left",
|
||||
"image_border_radius": 0,
|
||||
"image_border_radius_percent": 0,
|
||||
"image_border_radius_pixel": 0,
|
||||
"image_border_radius_type": "pixel",
|
||||
"image_max_width": 100,
|
||||
"image_max_height": None,
|
||||
"image_constraint": "contain",
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"message": "Add ability to set Image element's border radius using pixels or percentage.",
|
||||
"domain": "builder",
|
||||
"issue_number": 3360,
|
||||
"bullet_points": [],
|
||||
"created_at": "2025-03-26"
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<RadioGroup
|
||||
:model-value="value"
|
||||
:options="options"
|
||||
type="button"
|
||||
@input="$emit('input', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BORDER_RADIUS_TYPES } from '@baserow/modules/builder/enums'
|
||||
|
||||
export default {
|
||||
name: 'BorderRadiusSelector',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
options() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('borderRadiusSelector.pixel'),
|
||||
value: BORDER_RADIUS_TYPES.PIXEL,
|
||||
},
|
||||
{
|
||||
label: this.$t('borderRadiusSelector.percent'),
|
||||
value: BORDER_RADIUS_TYPES.PERCENT,
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -121,25 +121,84 @@
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
horizontal-narrow
|
||||
small-label
|
||||
required
|
||||
:label="$t('imageThemeConfigBlock.imageBorderRadiusType')"
|
||||
class="margin-bottom-2"
|
||||
>
|
||||
<BorderRadiusSelector v-model="values.image_border_radius_type" />
|
||||
|
||||
<template #after-input>
|
||||
<ResetButton
|
||||
v-model="values.image_border_radius_type"
|
||||
:default-value="theme?.image_border_radius_type"
|
||||
/>
|
||||
</template>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
v-if="showBorderRadiusPercent"
|
||||
horizontal-narrow
|
||||
small-label
|
||||
required
|
||||
class="margin-bottom-2"
|
||||
:label="$t('imageThemeConfigBlock.imageBorderRadiusLabel')"
|
||||
:error="fieldHasErrors('image_border_radius')"
|
||||
:error="fieldHasErrors('image_border_radius_percent')"
|
||||
>
|
||||
<FormInput
|
||||
v-model="values.image_border_radius"
|
||||
v-model="v$.values.image_border_radius_percent.$model"
|
||||
:default-value-when-empty="
|
||||
defaultValuesWhenEmpty[`image_border_radius`]
|
||||
defaultValuesWhenEmpty[`image_border_radius_percent`]
|
||||
"
|
||||
:error="fieldHasErrors('image_border_radius')"
|
||||
:error="fieldHasErrors('image_border_radius_percent')"
|
||||
type="number"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:min="defaultValuesWhenEmpty.image_border_radius_percent.min"
|
||||
:max="defaultValuesWhenEmpty.image_border_radius_percent.max"
|
||||
remove-number-input-controls
|
||||
:placeholder="
|
||||
$t('imageThemeConfigBlock.imageBorderRadiusPlaceholder')
|
||||
$t('imageThemeConfigBlock.imageBorderRadiusPercentPlaceholder')
|
||||
"
|
||||
:to-value="(value) => (value ? parseInt(value) : null)"
|
||||
>
|
||||
<template #suffix>
|
||||
<i class="iconoir-percentage"></i>
|
||||
</template>
|
||||
</FormInput>
|
||||
|
||||
<template #after-input>
|
||||
<ResetButton
|
||||
v-model="values.image_border_radius_percent"
|
||||
:default-value="theme?.image_border_radius_percent"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
{{ v$.values.image_border_radius_percent.$errors[0].$message }}
|
||||
</template>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
v-if="showBorderRadiusPixel"
|
||||
horizontal-narrow
|
||||
small-label
|
||||
required
|
||||
class="margin-bottom-2"
|
||||
:label="$t('imageThemeConfigBlock.imageBorderRadiusLabel')"
|
||||
:error="fieldHasErrors('image_border_radius_pixel')"
|
||||
>
|
||||
<FormInput
|
||||
v-model="v$.values.image_border_radius_pixel.$model"
|
||||
:default-value-when-empty="
|
||||
defaultValuesWhenEmpty[`image_border_radius_pixel`]
|
||||
"
|
||||
:error="fieldHasErrors('image_border_radius_pixel')"
|
||||
type="number"
|
||||
:min="defaultValuesWhenEmpty.image_border_radius_pixel.min"
|
||||
:max="defaultValuesWhenEmpty.image_border_radius_pixel.max"
|
||||
remove-number-input-controls
|
||||
:placeholder="
|
||||
$t('imageThemeConfigBlock.imageBorderRadiusPixelPlaceholder')
|
||||
"
|
||||
:to-value="(value) => (value ? parseInt(value) : null)"
|
||||
>
|
||||
|
@ -148,13 +207,13 @@
|
|||
|
||||
<template #after-input>
|
||||
<ResetButton
|
||||
v-model="values.image_border_radius"
|
||||
:default-value="theme?.image_border_radius"
|
||||
v-model="values.image_border_radius_pixel"
|
||||
:default-value="theme?.image_border_radius_pixel"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
{{ v$.values.image_border_radius.$errors[0].$message }}
|
||||
{{ v$.values.image_border_radius_pixel.$errors[0].$message }}
|
||||
</template>
|
||||
</FormGroup>
|
||||
</template>
|
||||
|
@ -171,7 +230,11 @@ import themeConfigBlock from '@baserow/modules/builder/mixins/themeConfigBlock'
|
|||
import ThemeConfigBlockSection from '@baserow/modules/builder/components/theme/ThemeConfigBlockSection'
|
||||
import ResetButton from '@baserow/modules/builder/components/theme/ResetButton'
|
||||
import HorizontalAlignmentsSelector from '@baserow/modules/builder/components/HorizontalAlignmentsSelector'
|
||||
import { IMAGE_SOURCE_TYPES } from '@baserow/modules/builder/enums'
|
||||
import BorderRadiusSelector from '@baserow/modules/builder/components/BorderRadiusSelector.vue'
|
||||
import {
|
||||
IMAGE_SOURCE_TYPES,
|
||||
BORDER_RADIUS_TYPES,
|
||||
} from '@baserow/modules/builder/enums'
|
||||
import {
|
||||
integer,
|
||||
maxValue,
|
||||
|
@ -189,7 +252,11 @@ const minMax = {
|
|||
min: 5,
|
||||
max: 3000,
|
||||
},
|
||||
image_border_radius: {
|
||||
image_border_radius_percent: {
|
||||
min: 0,
|
||||
max: 50,
|
||||
},
|
||||
image_border_radius_pixel: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
},
|
||||
|
@ -198,6 +265,7 @@ const minMax = {
|
|||
export default {
|
||||
name: 'ImageThemeConfigBlock',
|
||||
components: {
|
||||
BorderRadiusSelector,
|
||||
ThemeConfigBlockSection,
|
||||
ResetButton,
|
||||
HorizontalAlignmentsSelector,
|
||||
|
@ -213,23 +281,48 @@ export default {
|
|||
image_max_width: this.theme?.image_max_width,
|
||||
image_max_height: this.theme?.image_max_height,
|
||||
image_constraint: this.theme?.image_constraint,
|
||||
image_border_radius: this.theme?.image_border_radius,
|
||||
image_border_radius_type: this.theme?.image_border_radius_type,
|
||||
image_border_radius_pixel: this.theme?.image_border_radius_pixel,
|
||||
image_border_radius_percent: this.theme?.image_border_radius_percent,
|
||||
},
|
||||
allowedValues: [
|
||||
'image_alignment',
|
||||
'image_max_width',
|
||||
'image_max_height',
|
||||
'image_constraint',
|
||||
'image_border_radius',
|
||||
'image_border_radius_pixel',
|
||||
'image_border_radius_percent',
|
||||
'image_border_radius_type',
|
||||
],
|
||||
defaultValuesWhenEmpty: {
|
||||
image_min_width: minMax.image_width.min,
|
||||
image_min_height: minMax.image_height.min,
|
||||
image_border_radius: minMax.image_border_radius.min,
|
||||
image_border_radius_pixel: minMax.image_border_radius_pixel.min,
|
||||
image_border_radius_percent: minMax.image_border_radius_percent.min,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
borderRadiusTypes() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('imageThemeConfigBlock.imageBorderRadiusTypePixel'),
|
||||
value: BORDER_RADIUS_TYPES.PIXEL,
|
||||
},
|
||||
{
|
||||
label: this.$t('imageThemeConfigBlock.imageBorderRadiusTypePercent'),
|
||||
value: BORDER_RADIUS_TYPES.PERCENT,
|
||||
},
|
||||
]
|
||||
},
|
||||
showBorderRadiusPixel() {
|
||||
return this.values.image_border_radius_type === BORDER_RADIUS_TYPES.PIXEL
|
||||
},
|
||||
showBorderRadiusPercent() {
|
||||
return (
|
||||
this.values.image_border_radius_type === BORDER_RADIUS_TYPES.PERCENT
|
||||
)
|
||||
},
|
||||
imageMaxHeight: {
|
||||
get() {
|
||||
return this.values.image_max_height
|
||||
|
@ -338,19 +431,34 @@ export default {
|
|||
},
|
||||
image_constraint: {},
|
||||
image_alignment: {},
|
||||
image_border_radius: {
|
||||
image_border_radius_percent: {
|
||||
integer: helpers.withMessage(this.$t('error.integerField'), integer),
|
||||
minValue: helpers.withMessage(
|
||||
this.$t('error.minValueField', {
|
||||
min: minMax.image_border_radius.min,
|
||||
min: minMax.image_border_radius_percent.min,
|
||||
}),
|
||||
minValue(minMax.image_border_radius.min)
|
||||
minValue(minMax.image_border_radius_percent.min)
|
||||
),
|
||||
maxValue: helpers.withMessage(
|
||||
this.$t('error.minValueField', {
|
||||
min: minMax.image_border_radius.max,
|
||||
this.$t('error.maxValueField', {
|
||||
max: minMax.image_border_radius_percent.max,
|
||||
}),
|
||||
minValue(minMax.image_border_radius.min)
|
||||
maxValue(minMax.image_border_radius_percent.max)
|
||||
),
|
||||
},
|
||||
image_border_radius_pixel: {
|
||||
integer: helpers.withMessage(this.$t('error.integerField'), integer),
|
||||
minValue: helpers.withMessage(
|
||||
this.$t('error.minValueField', {
|
||||
min: minMax.image_border_radius_pixel.min,
|
||||
}),
|
||||
minValue(minMax.image_border_radius_pixel.min)
|
||||
),
|
||||
maxValue: helpers.withMessage(
|
||||
this.$t('error.maxValueField', {
|
||||
max: minMax.image_border_radius_pixel.max,
|
||||
}),
|
||||
maxValue(minMax.image_border_radius_pixel.max)
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -40,6 +40,11 @@ export const ALLOWED_LINK_PROTOCOLS = [
|
|||
'tel:',
|
||||
]
|
||||
|
||||
export const BORDER_RADIUS_TYPES = {
|
||||
PIXEL: 'pixel',
|
||||
PERCENT: 'percent',
|
||||
}
|
||||
|
||||
export const TEXT_FORMAT_TYPES = {
|
||||
PLAIN: 'plain',
|
||||
MARKDOWN: 'markdown',
|
||||
|
|
|
@ -407,6 +407,10 @@
|
|||
"alignmentCenter": "Center",
|
||||
"alignmentRight": "Right"
|
||||
},
|
||||
"borderRadiusSelector": {
|
||||
"percent": "Percent",
|
||||
"pixel": "Pixel"
|
||||
},
|
||||
"verticalAlignmentSelector": {
|
||||
"alignmentTop": "Top",
|
||||
"alignmentCenter": "Middle",
|
||||
|
@ -644,7 +648,11 @@
|
|||
"imageConstraintContain": "Contain",
|
||||
"imageConstraintContainDisabled": "Unavailable with a max height.",
|
||||
"imageBorderRadiusLabel": "Border radius",
|
||||
"imageBorderRadiusPlaceholder": "Enter the image border radius."
|
||||
"imageBorderRadiusPercentPlaceholder": "Enter the image border radius percentage.",
|
||||
"imageBorderRadiusPixelPlaceholder": "Enter the image border radius pixels.",
|
||||
"imageBorderRadiusType": "Border radius type",
|
||||
"imageBorderRadiusTypePercent": "Pixel",
|
||||
"imageBorderRadiusTypePixel": "Percent"
|
||||
},
|
||||
"tableThemeConfigBlock": {
|
||||
"borderColor": "Border color",
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
colorContrast,
|
||||
} from '@baserow/modules/core/utils/colors'
|
||||
import {
|
||||
BORDER_RADIUS_TYPES,
|
||||
WIDTHS_NEW,
|
||||
HORIZONTAL_ALIGNMENTS,
|
||||
BACKGROUND_MODES,
|
||||
|
@ -580,10 +581,20 @@ export class ImageThemeConfigBlockType extends ThemeConfigBlockType {
|
|||
'image_constraint',
|
||||
baseTheme?.image_constraint
|
||||
)
|
||||
const imageBorderRadius = get(
|
||||
const imageBorderRadiusPixel = get(
|
||||
theme,
|
||||
'image_border_radius',
|
||||
baseTheme?.image_border_radius
|
||||
'image_border_radius_pixel',
|
||||
baseTheme?.image_border_radius_pixel
|
||||
)
|
||||
const imageBorderRadiusPercent = get(
|
||||
theme,
|
||||
'image_border_radius_percent',
|
||||
baseTheme?.image_border_radius_percent
|
||||
)
|
||||
const imageBorderRadiusType = get(
|
||||
theme,
|
||||
'image_border_radius_type',
|
||||
baseTheme?.image_border_radius_type
|
||||
)
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(theme, 'image_max_width')) {
|
||||
|
@ -621,9 +632,25 @@ export class ImageThemeConfigBlockType extends ThemeConfigBlockType {
|
|||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(theme, 'image_border_radius')) {
|
||||
if (imageBorderRadius) {
|
||||
style.style['--image-border-radius'] = `${imageBorderRadius}px`
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
theme,
|
||||
'image_border_radius_percent'
|
||||
) &&
|
||||
imageBorderRadiusType === BORDER_RADIUS_TYPES.PERCENT
|
||||
) {
|
||||
if (imageBorderRadiusPercent) {
|
||||
style.style['--image-border-radius'] = `${imageBorderRadiusPercent}%`
|
||||
}
|
||||
} else if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
theme,
|
||||
'image_border_radius_pixel'
|
||||
) &&
|
||||
imageBorderRadiusType === BORDER_RADIUS_TYPES.PIXEL
|
||||
) {
|
||||
if (imageBorderRadiusPixel) {
|
||||
style.style['--image-border-radius'] = `${imageBorderRadiusPixel}px`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue