mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
chore: box shadow updates (#9220)
This commit is contained in:
committed by
Nathan Walker
parent
d2f50e50bb
commit
19ce9fc2bf
@@ -105,11 +105,6 @@ export class BoxShadowModel extends Observable {
|
||||
}
|
||||
this.appliedBoxShadow = this._boxShadow;
|
||||
this.notifyPropertyChange('appliedBoxShadow', this.appliedBoxShadow);
|
||||
|
||||
// TODO: this is a workaround to apply shadow immediately,
|
||||
// since the box-shadow logic is currently inside background.ts
|
||||
this.notifyPropertyChange('background', '');
|
||||
this.notifyPropertyChange('background', this.background);
|
||||
}
|
||||
|
||||
textChange(args): void {
|
||||
|
||||
145
packages/core/__tests__/ui/styling/css-shadow.ts
Normal file
145
packages/core/__tests__/ui/styling/css-shadow.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
// This has been disabled/unfinished becase with the current testing setup
|
||||
// the imports don't work. We need to fully switch to jest & set up ts support
|
||||
// todo: fix tests.
|
||||
|
||||
// import { parseCSSShadow } from "@nativescript/core/ui/styling/css-shadow";
|
||||
// import { zeroLength } from "@nativescript/core";
|
||||
|
||||
// describe('ui', () => {
|
||||
// describe('styling', () => {
|
||||
// describe('css-shadow', () => {
|
||||
//
|
||||
// it("empty", () => {
|
||||
// const shadow = parseCSSShadow("")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(zeroLength)
|
||||
// expect(shadow.offsetY).toBe(zeroLength)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('black')
|
||||
// });
|
||||
//
|
||||
// it("1px 1px 2px black", () => {
|
||||
// const shadow = parseCSSShadow("1px 1px 2px black")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(1)
|
||||
// expect(shadow.offsetY).toBe(1)
|
||||
// expect(shadow.blurRadius).toBe(2)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('black')
|
||||
// });
|
||||
//
|
||||
// it("#fc0 1px 0 10px", () => {
|
||||
// const shadow = parseCSSShadow("#fc0 1px 0 10px")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(1)
|
||||
// expect(shadow.offsetY).toBe(zeroLength)
|
||||
// expect(shadow.blurRadius).toBe(10)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('#fc0')
|
||||
// });
|
||||
//
|
||||
// it("5px 5px #558abb", () => {
|
||||
// const shadow = parseCSSShadow("5px 5px #558abb")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(5)
|
||||
// expect(shadow.offsetY).toBe(5)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('#558abb')
|
||||
// });
|
||||
//
|
||||
// it("white 2px 5px", () => {
|
||||
// const shadow = parseCSSShadow("white 2px 5px")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(2)
|
||||
// expect(shadow.offsetY).toBe(5)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('white')
|
||||
// });
|
||||
//
|
||||
// it("5px 10px", () => {
|
||||
// const shadow = parseCSSShadow("5px 10px")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(5)
|
||||
// expect(shadow.offsetY).toBe(10)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('black')
|
||||
// });
|
||||
//
|
||||
// // box
|
||||
// it("60px -16px teal", () => {
|
||||
// const shadow = parseCSSShadow("60px -16px teal")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(60)
|
||||
// expect(shadow.offsetY).toBe(-16)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('teal')
|
||||
// });
|
||||
//
|
||||
// it("10px 5px 5px black", () => {
|
||||
// const shadow = parseCSSShadow("10px 5px 5px black")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(10)
|
||||
// expect(shadow.offsetY).toBe(5)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('black')
|
||||
// });
|
||||
//
|
||||
// it("2px 2px 2px 1px rgba(0, 0, 0, 0.2)", () => {
|
||||
// const shadow = parseCSSShadow("2px 2px 2px 1px rgba(0, 0, 0, 0.2)")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(2)
|
||||
// expect(shadow.offsetY).toBe(2)
|
||||
// expect(shadow.blurRadius).toBe(2)
|
||||
// expect(shadow.spreadRadius).toBe(1)
|
||||
// expect(shadow.color).toBe('rgba(0, 0, 0, 0.2)')
|
||||
// });
|
||||
//
|
||||
// it("inset 5em 1em gold", () => {
|
||||
// const shadow = parseCSSShadow("inset 5em 1em gold")
|
||||
// expect(shadow.inset).toBe(true)
|
||||
// expect(shadow.offsetX).toBe(5)
|
||||
// expect(shadow.offsetY).toBe(1)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('gold')
|
||||
// });
|
||||
//
|
||||
// it("5 10", () => {
|
||||
// const shadow = parseCSSShadow("5 10")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(5)
|
||||
// expect(shadow.offsetY).toBe(10)
|
||||
// expect(shadow.blurRadius).toBe(zeroLength)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('black')
|
||||
// });
|
||||
//
|
||||
// it("2 2 2 #333", () => {
|
||||
// const shadow = parseCSSShadow("2 2 2 #333")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(2)
|
||||
// expect(shadow.offsetY).toBe(2)
|
||||
// expect(shadow.blurRadius).toBe(2)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('#333')
|
||||
// });
|
||||
//
|
||||
// it("-1 -1 1 #333", () => {
|
||||
// const shadow = parseCSSShadow("-1 -1 1 #333")
|
||||
// expect(shadow.inset).toBe(false)
|
||||
// expect(shadow.offsetX).toBe(-1)
|
||||
// expect(shadow.offsetY).toBe(-1)
|
||||
// expect(shadow.blurRadius).toBe(1)
|
||||
// expect(shadow.spreadRadius).toBe(zeroLength)
|
||||
// expect(shadow.color).toBe('#333')
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
//
|
||||
@@ -20,6 +20,7 @@ class AndroidSharedA11YObservable extends SharedA11YObservable {
|
||||
[accessibilityStateEnabledPropName]: boolean;
|
||||
[touchExplorationStateEnabledPropName]: boolean;
|
||||
|
||||
// @ts-ignore todo: fix
|
||||
get accessibilityServiceEnabled(): boolean {
|
||||
return !!this[accessibilityStateEnabledPropName] && !!this[touchExplorationStateEnabledPropName];
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BackgroundRepeat } from '../styling/style-properties';
|
||||
import { LinearGradient } from './linear-gradient';
|
||||
// Types.
|
||||
import { Color } from '../../color';
|
||||
import { CSSShadow } from './css-shadow';
|
||||
|
||||
export class Background implements BackgroundDefinition {
|
||||
public static default = new Background();
|
||||
@@ -26,6 +27,7 @@ export class Background implements BackgroundDefinition {
|
||||
public borderBottomLeftRadius = 0;
|
||||
public borderBottomRightRadius = 0;
|
||||
public clipPath: string;
|
||||
public boxShadow: CSSShadow;
|
||||
|
||||
private clone(): Background {
|
||||
const clone = new Background();
|
||||
@@ -48,6 +50,7 @@ export class Background implements BackgroundDefinition {
|
||||
clone.borderBottomRightRadius = this.borderBottomRightRadius;
|
||||
clone.borderBottomLeftRadius = this.borderBottomLeftRadius;
|
||||
clone.clipPath = this.clipPath;
|
||||
clone.boxShadow = this.boxShadow;
|
||||
|
||||
return clone;
|
||||
}
|
||||
@@ -178,6 +181,13 @@ export class Background implements BackgroundDefinition {
|
||||
return clone;
|
||||
}
|
||||
|
||||
public withBoxShadow(value: CSSShadow): Background {
|
||||
const clone = this.clone();
|
||||
clone.boxShadow = value;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return !this.color && !this.image && !this.hasBorderWidth() && !this.hasBorderRadius() && !this.clipPath;
|
||||
}
|
||||
@@ -274,6 +284,14 @@ export class Background implements BackgroundDefinition {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public hasBoxShadow(): boolean {
|
||||
return !!this.boxShadow;
|
||||
}
|
||||
|
||||
public getBoxShadow(): CSSShadow {
|
||||
return this.boxShadow;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `isEmpty: ${this.isEmpty()}; color: ${this.color}; image: ${this.image}; repeat: ${this.repeat}; position: ${this.position}; size: ${this.size}; borderTopColor: ${this.borderTopColor}; borderRightColor: ${this.borderRightColor}; borderBottomColor: ${this.borderBottomColor}; borderLeftColor: ${this.borderLeftColor}; borderTopWidth: ${this.borderTopWidth}; borderRightWidth: ${this.borderRightWidth}; borderBottomWidth: ${this.borderBottomWidth}; borderLeftWidth: ${this.borderLeftWidth}; borderTopLeftRadius: ${this.borderTopLeftRadius}; borderTopRightRadius: ${
|
||||
this.borderTopRightRadius
|
||||
|
||||
@@ -6,9 +6,9 @@ import { parse } from '../../css-value';
|
||||
import { path, knownFolders } from '../../file-system';
|
||||
import * as application from '../../application';
|
||||
import { profile } from '../../profiling';
|
||||
import { BoxShadow } from './box-shadow';
|
||||
import { Color } from '../../color';
|
||||
import { Screen } from '../../platform';
|
||||
import { CSSShadow } from './css-shadow';
|
||||
export * from './background-common';
|
||||
|
||||
interface AndroidView {
|
||||
@@ -93,9 +93,10 @@ export namespace ad {
|
||||
nativeView.setBackground(defaultDrawable);
|
||||
}
|
||||
|
||||
const boxShadow = view.style.boxShadow;
|
||||
if (boxShadow) {
|
||||
drawBoxShadow(nativeView, view, boxShadow);
|
||||
if (background.hasBoxShadow()) {
|
||||
drawBoxShadow(nativeView, view, background.getBoxShadow());
|
||||
} else {
|
||||
clearBoxShadow(nativeView);
|
||||
}
|
||||
|
||||
// TODO: Can we move BorderWidths as separate native setter?
|
||||
@@ -226,7 +227,7 @@ function createNativeCSSValueArray(css: string): androidNative.Array<org.natives
|
||||
return nativeArray;
|
||||
}
|
||||
|
||||
function drawBoxShadow(nativeView: android.view.View, view: View, boxShadow: BoxShadow) {
|
||||
function drawBoxShadow(nativeView: android.view.View, view: View, boxShadow: CSSShadow) {
|
||||
const color = boxShadow.color;
|
||||
const shadowOpacity = color.a;
|
||||
const shadowColor = new Color(shadowOpacity, color.r, color.g, color.b);
|
||||
@@ -243,6 +244,10 @@ function drawBoxShadow(nativeView: android.view.View, view: View, boxShadow: Box
|
||||
org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config));
|
||||
}
|
||||
|
||||
function clearBoxShadow(nativeView: android.view.View) {
|
||||
// org.nativescript.widgets.Utils.clearBoxShadow(nativeView);
|
||||
}
|
||||
|
||||
export enum CacheMode {
|
||||
none,
|
||||
memory,
|
||||
|
||||
3
packages/core/ui/styling/background.d.ts
vendored
3
packages/core/ui/styling/background.d.ts
vendored
@@ -50,6 +50,7 @@ export declare class Background {
|
||||
public withBorderBottomRightRadius(value: number): Background;
|
||||
public withBorderBottomLeftRadius(value: number): Background;
|
||||
public withClipPath(value: string): Background;
|
||||
public withBoxShadow(value: BoxShadow): Background;
|
||||
|
||||
public isEmpty(): boolean;
|
||||
|
||||
@@ -65,6 +66,8 @@ export declare class Background {
|
||||
public getUniformBorderColor(): Color;
|
||||
public getUniformBorderWidth(): number;
|
||||
public getUniformBorderRadius(): number;
|
||||
public hasBoxShadow(): boolean;
|
||||
public getBoxShadow(): BoxShadow;
|
||||
}
|
||||
|
||||
export namespace ios {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isDataURI, isFileOrResourcePath, layout } from '../../utils';
|
||||
import { ImageSource } from '../../image-source';
|
||||
import { CSSValue, parse as cssParse } from '../../css-value';
|
||||
import { CSSShadow } from './css-shadow';
|
||||
import { Length } from './style-properties';
|
||||
|
||||
export * from './background-common';
|
||||
|
||||
@@ -88,12 +89,11 @@ export namespace ios {
|
||||
setUIColorFromImage(view, nativeView, callback, flip);
|
||||
}
|
||||
|
||||
const boxShadow = view.style.boxShadow;
|
||||
if (boxShadow) {
|
||||
// this is required (if not, shadow will get cutoff at parent's dimensions)
|
||||
// nativeView.clipsToBounds doesn't work
|
||||
view.setProperty('clipToBounds', false);
|
||||
drawBoxShadow(nativeView, view, boxShadow, background);
|
||||
if (background.hasBoxShadow()) {
|
||||
drawBoxShadow(nativeView, view, background.getBoxShadow(), background);
|
||||
} else {
|
||||
view.setProperty('clipToBounds', true);
|
||||
clearBoxShadow(nativeView);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -718,23 +718,28 @@ function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: Backg
|
||||
|
||||
// TODO: use sublayer if its applied to a layout
|
||||
function drawBoxShadow(nativeView: NativeView, view: View, boxShadow: CSSShadow, background: BackgroundDefinition, useSubLayer: boolean = false) {
|
||||
// TODO: fine named 'shadow-layer' first and otherwise need to search through sublayers (need logic used in text-shadow for layer - see getShadowLayer)
|
||||
const layer: CALayer = nativeView.layer;
|
||||
|
||||
layer.masksToBounds = false;
|
||||
nativeView.clipsToBounds = false;
|
||||
|
||||
// this is required (if not, shadow will get cutoff at parent's dimensions)
|
||||
// nativeView.clipsToBounds doesn't work
|
||||
view.setProperty('clipToBounds', false);
|
||||
|
||||
if (!background.color?.a) {
|
||||
// add white background if view has a transparent background
|
||||
layer.backgroundColor = UIColor.whiteColor.CGColor;
|
||||
}
|
||||
// shadow opacity is handled on the shadow's color instance
|
||||
layer.shadowOpacity = 1;
|
||||
layer.shadowRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
|
||||
layer.shadowOpacity = background.color?.a ? background.color?.a / 255 : 1;
|
||||
layer.shadowRadius = Length.toDevicePixels(boxShadow.spreadRadius);
|
||||
layer.shadowColor = boxShadow.color.ios.CGColor;
|
||||
|
||||
const adjustedShadowOffset = {
|
||||
x: layout.toDeviceIndependentPixels(boxShadow.offsetX),
|
||||
y: layout.toDeviceIndependentPixels(boxShadow.offsetY),
|
||||
x: Length.toDevicePixels(boxShadow.offsetX),
|
||||
y: Length.toDevicePixels(boxShadow.offsetY),
|
||||
};
|
||||
layer.shadowOffset = CGSizeMake(adjustedShadowOffset.x, adjustedShadowOffset.y);
|
||||
|
||||
@@ -745,6 +750,17 @@ function drawBoxShadow(nativeView: NativeView, view: View, boxShadow: CSSShadow,
|
||||
layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(nativeView.bounds, cornerRadius).CGPath;
|
||||
}
|
||||
|
||||
function clearBoxShadow(nativeView: NativeView) {
|
||||
nativeView.clipsToBounds = true;
|
||||
const layer: CALayer = nativeView.layer;
|
||||
layer.masksToBounds = true;
|
||||
layer.shadowOffset = CGSizeMake(0, 0);
|
||||
layer.shadowColor = UIColor.clearColor.CGColor;
|
||||
layer.cornerRadius = 0.0;
|
||||
layer.shadowRadius = 0.0;
|
||||
layer.shadowOpacity = 0.0;
|
||||
}
|
||||
|
||||
function drawGradient(nativeView: NativeView, gradient: LinearGradient) {
|
||||
const gradientLayer = CAGradientLayer.layer();
|
||||
gradientLayer.frame = nativeView.bounds;
|
||||
|
||||
@@ -1280,7 +1280,8 @@ const boxShadowProperty = new CssProperty<Style, CSSShadow>({
|
||||
name: 'boxShadow',
|
||||
cssName: 'box-shadow',
|
||||
valueChanged: (target, oldValue, newValue) => {
|
||||
target.boxShadow = newValue;
|
||||
const background = target.backgroundInternal.withBoxShadow(newValue);
|
||||
target.backgroundInternal = background;
|
||||
},
|
||||
valueConverter: (value) => {
|
||||
return parseCSSShadow(value);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Types
|
||||
import { TextDecoration, TextAlignment, TextTransform, TextShadow, WhiteSpace, getClosestPropertyValue } from './text-base-common';
|
||||
import { TextDecoration, TextAlignment, TextTransform, WhiteSpace, getClosestPropertyValue } from './text-base-common';
|
||||
import { CSSShadow } from '../styling/css-shadow';
|
||||
|
||||
// Requires
|
||||
import { Font } from '../styling/font';
|
||||
@@ -387,7 +388,7 @@ export class TextBase extends TextBaseCommon {
|
||||
};
|
||||
}
|
||||
|
||||
[textShadowProperty.setNative](value: TextShadow) {
|
||||
[textShadowProperty.setNative](value: CSSShadow) {
|
||||
this.nativeViewProtected.setShadowLayer(Length.toDevicePixels(value.blurRadius, 0), Length.toDevicePixels(value.offsetX, 0), Length.toDevicePixels(value.offsetY, 0), value.color.android);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user