1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-06 22:08:52 +00:00

Merge branch '3360-allow-fully-round-image' into 'develop'

Image Element's border radius can use pixel or percentage

Closes 

See merge request 
This commit is contained in:
Tsering Paljor 2025-03-27 20:44:22 +04:00
commit d7541bb6cc
10 changed files with 312 additions and 31 deletions

View file

@ -42,3 +42,8 @@ class FontWeights(models.TextChoices):
HEAVY = "heavy" HEAVY = "heavy"
BLACK = "black" BLACK = "black"
EXTRA_BLACK = "extra-black" EXTRA_BLACK = "extra-black"
class BorderRadius(models.TextChoices):
PERCENT = "percent"
PIXEL = "pixel"

View file

@ -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,
),
),
]

View file

@ -5,6 +5,7 @@ from baserow.contrib.builder.constants import (
BACKGROUND_IMAGE_MODES, BACKGROUND_IMAGE_MODES,
COLOR_FIELD_MAX_LENGTH, COLOR_FIELD_MAX_LENGTH,
WIDTHS, WIDTHS,
BorderRadius,
FontWeights, FontWeights,
HorizontalAlignments, HorizontalAlignments,
) )
@ -411,8 +412,26 @@ class ImageThemeConfigBlock(ThemeConfigBlock):
], ],
) )
image_border_radius = models.SmallIntegerField( image_border_radius_type = models.CharField(
help_text="The border radius for this image element.", 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=[ validators=[
MinValueValidator(0, message="Value cannot be less than 0."), MinValueValidator(0, message="Value cannot be less than 0."),
MaxValueValidator(100, message="Value cannot be greater than 100."), MaxValueValidator(100, message="Value cannot be greater than 100."),

View file

@ -683,7 +683,9 @@ def test_builder_application_export(data_fixture):
"link_default_text_decoration": [True, False, False, False], "link_default_text_decoration": [True, False, False, False],
"link_hover_text_decoration": [True, False, False, False], "link_hover_text_decoration": [True, False, False, False],
"image_alignment": "left", "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_width": 100,
"image_max_height": None, "image_max_height": None,
"image_constraint": "contain", "image_constraint": "contain",

View file

@ -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"
}

View file

@ -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>

View file

@ -121,25 +121,84 @@
</FormGroup> </FormGroup>
<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 horizontal-narrow
small-label small-label
required required
class="margin-bottom-2" class="margin-bottom-2"
:label="$t('imageThemeConfigBlock.imageBorderRadiusLabel')" :label="$t('imageThemeConfigBlock.imageBorderRadiusLabel')"
:error="fieldHasErrors('image_border_radius')" :error="fieldHasErrors('image_border_radius_percent')"
> >
<FormInput <FormInput
v-model="values.image_border_radius" v-model="v$.values.image_border_radius_percent.$model"
:default-value-when-empty=" :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" type="number"
:min="0" :min="defaultValuesWhenEmpty.image_border_radius_percent.min"
:max="100" :max="defaultValuesWhenEmpty.image_border_radius_percent.max"
remove-number-input-controls remove-number-input-controls
:placeholder=" :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)" :to-value="(value) => (value ? parseInt(value) : null)"
> >
@ -148,13 +207,13 @@
<template #after-input> <template #after-input>
<ResetButton <ResetButton
v-model="values.image_border_radius" v-model="values.image_border_radius_pixel"
:default-value="theme?.image_border_radius" :default-value="theme?.image_border_radius_pixel"
/> />
</template> </template>
<template #error> <template #error>
{{ v$.values.image_border_radius.$errors[0].$message }} {{ v$.values.image_border_radius_pixel.$errors[0].$message }}
</template> </template>
</FormGroup> </FormGroup>
</template> </template>
@ -171,7 +230,11 @@ 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 { 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 { import {
integer, integer,
maxValue, maxValue,
@ -189,7 +252,11 @@ const minMax = {
min: 5, min: 5,
max: 3000, max: 3000,
}, },
image_border_radius: { image_border_radius_percent: {
min: 0,
max: 50,
},
image_border_radius_pixel: {
min: 0, min: 0,
max: 100, max: 100,
}, },
@ -198,6 +265,7 @@ const minMax = {
export default { export default {
name: 'ImageThemeConfigBlock', name: 'ImageThemeConfigBlock',
components: { components: {
BorderRadiusSelector,
ThemeConfigBlockSection, ThemeConfigBlockSection,
ResetButton, ResetButton,
HorizontalAlignmentsSelector, HorizontalAlignmentsSelector,
@ -213,23 +281,48 @@ export default {
image_max_width: this.theme?.image_max_width, image_max_width: this.theme?.image_max_width,
image_max_height: this.theme?.image_max_height, image_max_height: this.theme?.image_max_height,
image_constraint: this.theme?.image_constraint, 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: [ allowedValues: [
'image_alignment', 'image_alignment',
'image_max_width', 'image_max_width',
'image_max_height', 'image_max_height',
'image_constraint', 'image_constraint',
'image_border_radius', 'image_border_radius_pixel',
'image_border_radius_percent',
'image_border_radius_type',
], ],
defaultValuesWhenEmpty: { defaultValuesWhenEmpty: {
image_min_width: minMax.image_width.min, image_min_width: minMax.image_width.min,
image_min_height: minMax.image_height.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: { 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: { imageMaxHeight: {
get() { get() {
return this.values.image_max_height return this.values.image_max_height
@ -338,19 +431,34 @@ export default {
}, },
image_constraint: {}, image_constraint: {},
image_alignment: {}, image_alignment: {},
image_border_radius: { image_border_radius_percent: {
integer: helpers.withMessage(this.$t('error.integerField'), integer), integer: helpers.withMessage(this.$t('error.integerField'), integer),
minValue: helpers.withMessage( minValue: helpers.withMessage(
this.$t('error.minValueField', { 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( maxValue: helpers.withMessage(
this.$t('error.minValueField', { this.$t('error.maxValueField', {
min: minMax.image_border_radius.max, 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)
), ),
}, },
}, },

View file

@ -40,6 +40,11 @@ export const ALLOWED_LINK_PROTOCOLS = [
'tel:', 'tel:',
] ]
export const BORDER_RADIUS_TYPES = {
PIXEL: 'pixel',
PERCENT: 'percent',
}
export const TEXT_FORMAT_TYPES = { export const TEXT_FORMAT_TYPES = {
PLAIN: 'plain', PLAIN: 'plain',
MARKDOWN: 'markdown', MARKDOWN: 'markdown',

View file

@ -407,6 +407,10 @@
"alignmentCenter": "Center", "alignmentCenter": "Center",
"alignmentRight": "Right" "alignmentRight": "Right"
}, },
"borderRadiusSelector": {
"percent": "Percent",
"pixel": "Pixel"
},
"verticalAlignmentSelector": { "verticalAlignmentSelector": {
"alignmentTop": "Top", "alignmentTop": "Top",
"alignmentCenter": "Middle", "alignmentCenter": "Middle",
@ -644,7 +648,11 @@
"imageConstraintContain": "Contain", "imageConstraintContain": "Contain",
"imageConstraintContainDisabled": "Unavailable with a max height.", "imageConstraintContainDisabled": "Unavailable with a max height.",
"imageBorderRadiusLabel": "Border radius", "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": { "tableThemeConfigBlock": {
"borderColor": "Border color", "borderColor": "Border color",

View file

@ -14,6 +14,7 @@ import {
colorContrast, colorContrast,
} from '@baserow/modules/core/utils/colors' } from '@baserow/modules/core/utils/colors'
import { import {
BORDER_RADIUS_TYPES,
WIDTHS_NEW, WIDTHS_NEW,
HORIZONTAL_ALIGNMENTS, HORIZONTAL_ALIGNMENTS,
BACKGROUND_MODES, BACKGROUND_MODES,
@ -580,10 +581,20 @@ export class ImageThemeConfigBlockType extends ThemeConfigBlockType {
'image_constraint', 'image_constraint',
baseTheme?.image_constraint baseTheme?.image_constraint
) )
const imageBorderRadius = get( const imageBorderRadiusPixel = get(
theme, theme,
'image_border_radius', 'image_border_radius_pixel',
baseTheme?.image_border_radius 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')) { 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 (
if (imageBorderRadius) { Object.prototype.hasOwnProperty.call(
style.style['--image-border-radius'] = `${imageBorderRadius}px` 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`
} }
} }