From f5db58414a7d5ab7052c5fbd94a602fd870826b4 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Fri, 26 Feb 2021 18:49:50 +0100 Subject: [PATCH] fix: safeguards against invalid values --- apps/toolbox/src/box-shadow.ts | 6 +- apps/toolbox/src/box-shadow.xml | 63 +++++++++--- packages/core/ui/styling/background.ios.ts | 24 ++++- packages/core/ui/styling/css-shadow.ts | 2 +- packages/core/utils/native-helper.d.ts | 3 +- packages/core/utils/native-helper.ios.ts | 110 ++++++++++++++------- 6 files changed, 154 insertions(+), 54 deletions(-) diff --git a/apps/toolbox/src/box-shadow.ts b/apps/toolbox/src/box-shadow.ts index 379db6b91..9815bc6e2 100644 --- a/apps/toolbox/src/box-shadow.ts +++ b/apps/toolbox/src/box-shadow.ts @@ -11,13 +11,17 @@ export class BoxShadowModel extends Observable { private _selectedBackgroundType: string; private _selectedBorderType: string; private _selectedAnimation: string; - private _boxShadow: string; + private _boxShadow: string = '5 5 1 1 rgba(255, 0, 0, .9)'; background: string; borderWidth: number; borderRadius: number; appliedBoxShadow: string; + get boxShadow(): string { + return this._boxShadow; + } + constructor() { super(); } diff --git a/apps/toolbox/src/box-shadow.xml b/apps/toolbox/src/box-shadow.xml index 64fee8d49..d1d373a95 100644 --- a/apps/toolbox/src/box-shadow.xml +++ b/apps/toolbox/src/box-shadow.xml @@ -9,14 +9,14 @@ - @@ -29,7 +29,7 @@ boxShadow="{{ appliedBoxShadow }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" - backgroundColor="{{ background }}" + background="{{ background }}" tap="{{ toggleAnimation }}" > @@ -42,7 +42,7 @@ boxShadow="{{ appliedBoxShadow }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" - backgroundColor="{{ background }}" + background="{{ background }}" tap="{{ toggleAnimation }}" > @@ -55,7 +55,7 @@ boxShadow="{{ appliedBoxShadow }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" - backgroundColor="{{ background }}" + background="{{ background }}" tap="{{ toggleAnimation }}" > @@ -68,17 +68,33 @@ boxShadow="{{ appliedBoxShadow }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" - backgroundColor="{{ background }}" + background="{{ background }}" tap="{{ toggleAnimation }}" > + + + + + + - @@ -90,32 +106,53 @@ boxShadow="{{ appliedBoxShadow }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" - backgroundColor="{{ background }}" + background="{{ background }}" tap="{{ toggleAnimation }}" text="Label"> - - + + + + + + + + + + @@ -146,6 +184,7 @@ + @@ -172,7 +211,7 @@ - + diff --git a/packages/core/ui/styling/background.ios.ts b/packages/core/ui/styling/background.ios.ts index 82f22183d..4ab83ff6b 100644 --- a/packages/core/ui/styling/background.ios.ts +++ b/packages/core/ui/styling/background.ios.ts @@ -134,6 +134,7 @@ function unsubscribeFromScrollNotifications(view: View) { view.off('scroll', onScroll); } } + function subscribeForScrollNotifications(view: View) { if (view.nativeViewProtected instanceof UIScrollView) { view.on('scroll', onScroll); @@ -170,6 +171,7 @@ function clearNonUniformBorders(nativeView: NativeView): void { } const pattern = /url\(('|")(.*?)\1\)/; + function setUIColorFromImage(view: View, nativeView: UIView, callback: (uiColor: UIColor) => void, flip?: boolean): void { const frame = nativeView.frame; const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width; @@ -733,15 +735,24 @@ function drawBoxShadow(nativeView: NativeView, view: View, boxShadow: CSSShadow, } // shadow opacity is handled on the shadow's color instance layer.shadowOpacity = boxShadow.color?.a ? boxShadow.color?.a / 255 : 1; - layer.shadowRadius = Length.toDevicePixels(boxShadow.blurRadius); + layer.shadowRadius = Length.toDevicePixels(boxShadow.blurRadius, 0.0); layer.shadowColor = boxShadow.color.ios.CGColor; - layer.shadowOffset = CGSizeMake(Length.toDevicePixels(boxShadow.offsetX), Length.toDevicePixels(boxShadow.offsetY)); + + // prettier-ignore + layer.shadowOffset = CGSizeMake( + Length.toDevicePixels(boxShadow.offsetX, 0.0), + Length.toDevicePixels(boxShadow.offsetY, 0.0) + ); // this should match the view's border radius - const cornerRadius = Length.toDevicePixels(view.style.borderRadius); + const cornerRadius = Length.toDevicePixels(view.style.borderRadius, 0.0); // apply spreadRadius by expanding shadow layer bounds - const bounds = boxShadow.spreadRadius ? CGRectInset(nativeView.bounds, -Length.toDevicePixels(boxShadow.spreadRadius), -Length.toDevicePixels(boxShadow.spreadRadius)) : nativeView.bounds; + // prettier-ignore + const bounds = CGRectInset(nativeView.bounds, + -Length.toDevicePixels(boxShadow.spreadRadius, 0.0), + -Length.toDevicePixels(boxShadow.spreadRadius, 0.0) + ); // This has the nice glow with box shadow of 0,0 layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(bounds, cornerRadius).CGPath; @@ -749,7 +760,10 @@ function drawBoxShadow(nativeView: NativeView, view: View, boxShadow: CSSShadow, function clearBoxShadow(nativeView: NativeView) { nativeView.clipsToBounds = true; - const layer: CALayer = iOSNativeHelper.getShadowLayer(nativeView, 'ns-box-shadow'); + const layer: CALayer = iOSNativeHelper.getShadowLayer(nativeView, 'ns-box-shadow', false); + if (!layer) { + return; + } layer.masksToBounds = true; layer.shadowOffset = CGSizeMake(0, 0); layer.shadowColor = UIColor.clearColor.CGColor; diff --git a/packages/core/ui/styling/css-shadow.ts b/packages/core/ui/styling/css-shadow.ts index 645d87913..515b31a1e 100644 --- a/packages/core/ui/styling/css-shadow.ts +++ b/packages/core/ui/styling/css-shadow.ts @@ -34,7 +34,7 @@ const isLength = (v) => v === '0' || LENGTH_RE.test(v); * @param value */ export function parseCSSShadow(value: string): CSSShadow { - const parts = value.split(PARTS_RE); + const parts = value.trim().split(PARTS_RE); const inset = parts.includes('inset'); const first = parts[0]; const last = parts[parts.length - 1]; diff --git a/packages/core/utils/native-helper.d.ts b/packages/core/utils/native-helper.d.ts index ddf3e1e4f..1f9becfff 100644 --- a/packages/core/utils/native-helper.d.ts +++ b/packages/core/utils/native-helper.d.ts @@ -171,8 +171,9 @@ export namespace iOSNativeHelper { /** * @param nativeView UIView to find shadow layer with * @param name Name of the shadow layer if looking for specifically named layer + * @param create should we create a new layer if not found */ - export function getShadowLayer(nativeView: any /* UIView */, name?: string): any; /* CALayer */ + export function getShadowLayer(nativeView: any /* UIView */, name?: string, create?: boolean): any; /* CALayer */ /** * Create a UIDocumentInteractionControllerDelegate implementation for use with UIDocumentInteractionController diff --git a/packages/core/utils/native-helper.ios.ts b/packages/core/utils/native-helper.ios.ts index 2e47e33a2..b7d93f398 100644 --- a/packages/core/utils/native-helper.ios.ts +++ b/packages/core/utils/native-helper.ios.ts @@ -120,44 +120,86 @@ export namespace iOSNativeHelper { return transform; } - export function getShadowLayer(nativeView: UIView, name?: string): CALayer { - let layer: CALayer; - name = name || 'ns-shadow-layer'; - if (nativeView) { - if (nativeView.layer) { - if (nativeView.layer.name === name) { - console.log('- found shadow layer - reusing.'); - return nativeView.layer; - } else { - if (nativeView.layer.sublayers && nativeView.layer.sublayers.count) { - console.log('nativeView.layer.sublayers.count:', nativeView.layer.sublayers.count); - for (let i = 0; i < nativeView.layer.sublayers.count; i++) { - console.log(`layer ${i}:`, nativeView.layer.sublayers.objectAtIndex(i)); - console.log(`layer ${i} name:`, nativeView.layer.sublayers.objectAtIndex(i).name); - if (nativeView.layer.sublayers.objectAtIndex(i).name === name) { - return nativeView.layer.sublayers.objectAtIndex(i); - } - } - if (nativeView instanceof UITextView) { - layer = nativeView.layer.sublayers.objectAtIndex(1); - } else { - layer = nativeView.layer.sublayers.objectAtIndex(nativeView.layer.sublayers.count - 1); - } - } else { - layer = nativeView.layer; - } + export function getShadowLayer(nativeView: UIView, name: string = 'ns-shadow-layer', create: boolean = true): CALayer { + return nativeView.layer; + + console.log(`--- ${create ? 'CREATE' : 'READ'}`); + + /** + * UIView + * -> Shadow + * + * + * UIView + * -> UIView + * -> Shadow + */ + + if (!nativeView) { + return null; + } + + if (!nativeView.layer) { + // should never hit this? + console.log('- no layer! -'); + return null; + } + + // if the nativeView's layer is the shadow layer? + if (nativeView.layer.name === name) { + console.log('- found shadow layer - reusing.'); + return nativeView.layer; + } + + console.log('>> layer :', nativeView.layer); + if (nativeView.layer.sublayers?.count) { + const count = nativeView.layer.sublayers.count; + for (let i = 0; i < count; i++) { + const subLayer = nativeView.layer.sublayers.objectAtIndex(i); + + console.log(`>> subLayer ${i + 1}/${count} :`, subLayer); + console.log(`>> subLayer ${i + 1}/${count} name :`, subLayer.name); + + if (subLayer.name === name) { + console.log('- found shadow sublayer - reusing.'); + return subLayer; } - } else { - // could this occur? - console.log('no layer!'); } + // if (nativeView instanceof UITextView) { + // return nativeView.layer.sublayers.objectAtIndex(1); + // } else { + // return nativeView.layer.sublayers.objectAtIndex(nativeView.layer.sublayers.count - 1); + // } } - console.log('layer.name:', layer.name); - if (!layer.name) { - // only explicitly name if the developer had not named it themselves and/or some other integration - layer.name = name; + // else { + // layer = nativeView.layer; + // } + + // we're not interested in creating a new layer + if (!create) { + return null; } - return layer; + + console.log(`- adding a new layer for - ${name}`); + + const viewLayer = nativeView.layer; + const newLayer = CALayer.layer(); + + newLayer.name = name; + newLayer.zPosition = 0.0; + // nativeView.layer.insertSublayerBelow(newLayer, nativeView.layer) + // newLayer.insertSublayerAtIndex(nativeView.layer, 0) + // nativeView.layer.zPosition = 1.0; + // nativeView.layer.addSublayer(newLayer); + + // nativeView.layer = CALayer.layer() + + nativeView.layer.insertSublayerAtIndex(newLayer, 0); + // nativeView.layer.insertSublayerAtIndex(viewLayer, 1) + + // nativeView.layer.replaceSublayerWith(newLayer, nativeView.layer); + + return newLayer; } export function createUIDocumentInteractionControllerDelegate(): NSObject {