Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/core/ui/core/view-base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,8 @@ export abstract class ViewBase extends Observable {
* as well as adding/rmoving the view to/from the visual tree.
*/
public _suspendNativeUpdatesCount: number;
/** Set by views that apply their own padding, borders and backgrounds; core then skips the box state it derives for them. */
public _appliesOwnBoxStyle?: boolean;
public _isStyleScopeHost: boolean;
public _automaticallyAdjustsScrollViewInsets: boolean;

Expand Down
43 changes: 43 additions & 0 deletions packages/core/ui/styling/own-box-style.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, it, expect } from 'vitest';

import { View } from '../core/view';
import { Color } from '../../color';

function styledView(ownsBoxStyle: boolean) {
const view = new View();
view._appliesOwnBoxStyle = ownsBoxStyle;
const background = view.style.backgroundInternal;
view.style.paddingTop = 10;
view.style.borderTopWidth = 2;
view.style.borderTopColor = new Color('red');
view.style.borderTopLeftRadius = 4;
view.style.backgroundColor = new Color('blue');
view.style.boxShadow = '0 2px 4px black' as never;
view.style.clipPath = 'circle(50%)';

return { view, background };
}

describe('views that apply their own box style', () => {
it('keep the style values but skip the derived padding, border and background state', () => {
const { view, background } = styledView(true);

expect(view.style.paddingTop).toBe(10);
expect(view.style.borderTopWidth).toBe(2);
expect(view.style.backgroundColor.hex).toBe('#0000FF');
expect(view.effectivePaddingTop).toBe(0);
expect(view.effectiveBorderTopWidth).toBe(0);
expect(view.style.backgroundInternal).toBe(background);
});

it('leaves core views deriving them as before', () => {
const { view, background } = styledView(false);

expect(view.effectivePaddingTop).toBeGreaterThan(0);
expect(view.effectiveBorderTopWidth).toBeGreaterThan(0);
expect(view.style.backgroundInternal).not.toBe(background);
expect(view.style.backgroundInternal.color.hex).toBe('#0000FF');
expect(view.style.backgroundInternal.hasBorderRadius()).toBe(true);
expect(view.style.backgroundInternal.hasBoxShadows()).toBe(true);
});
});
77 changes: 77 additions & 0 deletions packages/core/ui/styling/style-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ function convertToTransform(value: string): [CssAnimationProperty<any, any>, any
];
}

/** True for a view that applies its own padding, borders and backgrounds, so core skips the state it derives from them. */
function skipsDerivedBoxStyle(style: Style): boolean {
return style.viewRef?.get()?._appliesOwnBoxStyle === true;
}

export const minWidthProperty = new CssProperty<Style, CoreTypes.LengthType>({
name: 'minWidth',
cssName: 'min-width',
Expand Down Expand Up @@ -437,6 +442,9 @@ export const paddingLeftProperty = new CssProperty<Style, CoreTypes.LengthType>(
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const view = target.viewRef.get();
if (view) {
view.effectivePaddingLeft = paddingLeftProperty.isSet(target) ? Length.toDevicePixels(newValue, 0) : null;
Expand All @@ -456,6 +464,9 @@ export const paddingRightProperty = new CssProperty<Style, CoreTypes.LengthType>
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const view = target.viewRef.get();
if (view) {
view.effectivePaddingRight = paddingRightProperty.isSet(target) ? Length.toDevicePixels(newValue, 0) : null;
Expand All @@ -475,6 +486,9 @@ export const paddingTopProperty = new CssProperty<Style, CoreTypes.LengthType>({
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const view = target.viewRef.get();
if (view) {
view.effectivePaddingTop = paddingTopProperty.isSet(target) ? Length.toDevicePixels(newValue, 0) : null;
Expand All @@ -494,6 +508,9 @@ export const paddingBottomProperty = new CssProperty<Style, CoreTypes.LengthType
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const view = target.viewRef.get();
if (view) {
view.effectivePaddingBottom = paddingBottomProperty.isSet(target) ? Length.toDevicePixels(newValue, 0) : null;
Expand Down Expand Up @@ -691,6 +708,9 @@ export const backgroundImageProperty = new CssProperty<Style, string | LinearGra
name: 'backgroundImage',
cssName: 'background-image',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withImage(newValue);
},
equalityComparer: (value1, value2) => {
Expand All @@ -717,6 +737,9 @@ export const backgroundColorProperty = new CssAnimationProperty<Style, Color>({
name: 'backgroundColor',
cssName: 'background-color',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withColor(newValue);
},
equalityComparer: Color.equals,
Expand All @@ -729,6 +752,9 @@ export const backgroundRepeatProperty = new CssProperty<Style, CoreTypes.Backgro
cssName: 'background-repeat',
valueConverter: CoreTypes.BackgroundRepeat.parse,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withRepeat(newValue);
},
});
Expand All @@ -738,6 +764,9 @@ export const backgroundSizeProperty = new CssProperty<Style, string>({
name: 'backgroundSize',
cssName: 'background-size',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withSize(newValue);
},
});
Expand All @@ -747,6 +776,9 @@ export const backgroundPositionProperty = new CssProperty<Style, string>({
name: 'backgroundPosition',
cssName: 'background-position',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withPosition(newValue);
},
});
Expand Down Expand Up @@ -789,6 +821,9 @@ export const borderTopColorProperty = new CssProperty<Style, Color>({
name: 'borderTopColor',
cssName: 'border-top-color',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withBorderTopColor(newValue);
},
equalityComparer: Color.equals,
Expand All @@ -800,6 +835,9 @@ export const borderRightColorProperty = new CssProperty<Style, Color>({
name: 'borderRightColor',
cssName: 'border-right-color',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withBorderRightColor(newValue);
},
equalityComparer: Color.equals,
Expand All @@ -811,6 +849,9 @@ export const borderBottomColorProperty = new CssProperty<Style, Color>({
name: 'borderBottomColor',
cssName: 'border-bottom-color',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withBorderBottomColor(newValue);
},
equalityComparer: Color.equals,
Expand All @@ -822,6 +863,9 @@ export const borderLeftColorProperty = new CssProperty<Style, Color>({
name: 'borderLeftColor',
cssName: 'border-left-color',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withBorderLeftColor(newValue);
},
equalityComparer: Color.equals,
Expand Down Expand Up @@ -869,6 +913,9 @@ export const borderTopWidthProperty = new CssProperty<Style, CoreTypes.LengthTyp
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-top-width should be Non-Negative Finite number. Value: ${value}`);
Expand All @@ -893,6 +940,9 @@ export const borderRightWidthProperty = new CssProperty<Style, CoreTypes.LengthT
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-right-width should be Non-Negative Finite number. Value: ${value}`);
Expand All @@ -917,6 +967,9 @@ export const borderBottomWidthProperty = new CssProperty<Style, CoreTypes.Length
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-bottom-width should be Non-Negative Finite number. Value: ${value}`);
Expand All @@ -941,6 +994,9 @@ export const borderLeftWidthProperty = new CssProperty<Style, CoreTypes.LengthTy
affectsLayout: __APPLE__,
equalityComparer: Length.equals,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-left-width should be Non-Negative Finite number. Value: ${value}`);
Expand Down Expand Up @@ -997,6 +1053,9 @@ export const borderTopLeftRadiusProperty = new CssProperty<Style, CoreTypes.Leng
defaultValue: 0,
affectsLayout: __APPLE__,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-top-left-radius should be Non-Negative Finite number. Value: ${value}`);
Expand All @@ -1014,6 +1073,9 @@ export const borderTopRightRadiusProperty = new CssProperty<Style, CoreTypes.Len
defaultValue: 0,
affectsLayout: __APPLE__,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-top-right-radius should be Non-Negative Finite number. Value: ${value}`);
Expand All @@ -1031,6 +1093,9 @@ export const borderBottomRightRadiusProperty = new CssProperty<Style, CoreTypes.
defaultValue: 0,
affectsLayout: __APPLE__,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-bottom-right-radius should be Non-Negative Finite number. Value: ${value}`);
Expand All @@ -1048,6 +1113,9 @@ export const borderBottomLeftRadiusProperty = new CssProperty<Style, CoreTypes.L
defaultValue: 0,
affectsLayout: __APPLE__,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
const value = Length.toDevicePixels(newValue, 0);
if (!isNonNegativeFiniteNumber(value)) {
throw new Error(`border-bottom-left-radius should be Non-Negative Finite number. Value: ${value}`);
Expand All @@ -1065,6 +1133,9 @@ export const cornerShapeProperty = new CssProperty<Style, CoreTypes.CornerShapeT
defaultValue: CoreTypes.CornerShape.round,
valueConverter: CoreTypes.CornerShape.parse,
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withCornerShape(newValue);
},
});
Expand All @@ -1074,6 +1145,9 @@ const boxShadowProperty = new CssProperty<Style, ShadowCSSValues[]>({
name: 'boxShadow',
cssName: 'box-shadow',
valueChanged: (target, _oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withBoxShadows(
newValue?.length
? newValue.map((v) => {
Expand Down Expand Up @@ -1110,6 +1184,9 @@ export const clipPathProperty = new CssProperty<Style, string | ClipPathFunction
name: 'clipPath',
cssName: 'clip-path',
valueChanged: (target, oldValue, newValue) => {
if (skipsDerivedBoxStyle(target)) {
return;
}
target.backgroundInternal = target.backgroundInternal.withClipPath(newValue);
},
equalityComparer: (value1, value2) => {
Expand Down
Loading