feat(core): box shadow demo (#9182)

This commit is contained in:
William Tjondrosuharto
2021-01-30 07:28:07 +07:00
committed by Nathan Walker
parent b745e0c5d4
commit ad92ba567f
9 changed files with 620 additions and 281 deletions

View File

@ -5,17 +5,79 @@
The following CSS rule changes the font size of all UI The following CSS rule changes the font size of all UI
components that have the btn class name. components that have the btn class name.
*/ */
.btn { .btn-view-demo {
font-size: 18; font-size: 18;
/* box-shadow: -5 -5 10 10 navy; */ background-color: #65ADF1;
box-shadow: -5 -5 rgba(0,0,0,0.5); border-radius: 5;
background-color: #add8e6; font-size: 17;
color: navy; padding: 15;
/* TODO: adding border radius breaks shadow */ font-weight: bold;
/* border-radius: 10; */
} }
.bold{ .bold{
font-weight: bold;
}
.controls Label {
font-size: 20;
font-weight: bold;
color: #333;
margin-top: 10;
}
.controls Button {
padding: 10 15;
margin: 5;
font-size: 17;
font-weight: bold;
color: #65ADF1;
border-radius: 5;
border-width: 1;
border-color: #65ADF1;
}
.box-shadow-demo .demo-component {
font-size: 20;
font-weight: bold;
color: #555;
border-color: #555;
margin: 10;
padding: 20 25;
text-transform: uppercase;
}
.box-shadow-demo .box-shadow-prop-controls {
padding: 10;
color: #333;
font-size: 17;
}
.box-shadow-demo .box-shadow-prop-controls TextField{
margin-left: 10;
padding: 5;
border-bottom-width: 1;
border-color: #65ADF1;
}
.box-shadow-demo .controls .description {
font-size: 15;
font-weight: normal;
color: #333;
margin-bottom: 10;
}
.box-shadow-demo .controls Button[selectedAttr=true] {
background-color: #65ADF1;
color: #fff;
}
.box-shadow-prop-controls .btn-apply {
background-color: #65ADF1;
color: #fff;
padding: 10 15;
border-radius: 4;
margin-left: 10;
font-size: 17;
font-weight: bold; font-weight: bold;
} }

View File

@ -0,0 +1,177 @@
import { Observable } from '@nativescript/core';
import { EventData, Page } from '@nativescript/core';
export function navigatingTo(args: EventData) {
const page = <Page>args.object;
page.bindingContext = new BoxShadowModel();
}
export class BoxShadowModel extends Observable {
private _selectedComponentType: string;
private _selectedBackgroundType: string;
private _selectedBorderType: string;
private _selectedAnimation: string;
private _boxShadow: string;
background: string;
borderWidth: number;
borderRadius: number;
appliedBoxShadow: string;
constructor() {
super();
}
get selectedComponentType(): string {
return this._selectedComponentType;
}
set selectedComponentType(value: string) {
if (this._selectedComponentType !== value) {
this._selectedComponentType = value;
this.notifyPropertyChange('selectedComponentType', value);
}
}
get selectedBackgroundType(): string {
return this._selectedBackgroundType;
}
set selectedBackgroundType(value: string) {
if (this._selectedBackgroundType !== value) {
this._selectedBackgroundType = value;
this.notifyPropertyChange('selectedBackgroundType', value);
switch (value) {
case 'solid':
this.background = '#65ADF1';
break;
case 'gradient':
this.background = 'linear-gradient(to top, #65ADF1, white)';
break;
case 'transparent':
this.background = 'transparent';
break;
default:
break;
}
this.notifyPropertyChange('background', this.background);
}
}
get selectedBorderType(): string {
return this._selectedBorderType;
}
set selectedBorderType(value: string) {
this._selectedBorderType = value;
this.notifyPropertyChange('selectedBorderType', value);
switch (value) {
case 'solid':
this.borderWidth = this.borderWidth ? 0 : 2;
break;
case 'rounded':
this.borderRadius = this.borderRadius ? 0 : 10;
break;
case 'none':
this.borderRadius = 0;
this.borderWidth = 0;
break;
default:
break;
}
this.notifyPropertyChange('borderRadius', this.borderRadius);
this.notifyPropertyChange('borderWidth', this.borderWidth);
}
selectComponentType(args): void {
this.selectedComponentType = args.object.componentType;
}
selectBackgroundType(args): void {
this.selectedBackgroundType = args.object.backgroundType;
}
selectBorderType(args): void {
this.selectedBorderType = args.object.borderType;
}
selectAnimationType(args): void {
this._selectedAnimation = args.object.animationType;
}
applyBoxShadow(): void {
if (!this._boxShadow) {
this._boxShadow = '';
}
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 {
this._boxShadow = args.object.text;
}
toggleAnimation(args) {
const view = args.object;
const animationDuration = 500;
if (this._selectedAnimation === 'width') {
const originalWidth = args.object.getActualSize().width;
view
.animate({
width: originalWidth / 2,
duration: animationDuration,
})
.then(() =>
view.animate({
width: originalWidth,
duration: animationDuration,
})
)
.catch((err) => {
console.error('animation error', err);
});
} else if (this._selectedAnimation === 'height') {
const originalHeight = args.object.getActualSize().height;
view
.animate({
height: originalHeight / 2,
duration: animationDuration,
})
.then(() =>
view.animate({
height: originalHeight,
duration: animationDuration,
})
)
.catch((err) => {
console.error('animation error', err);
});
} else {
view
.animate({
opacity: this._selectedAnimation === 'opacity' ? 0 : 1,
scale: this._selectedAnimation === 'scale' ? { x: 0.5, y: 0.6 } : { x: 1, y: 1 },
rotate: this._selectedAnimation === 'rotate' ? 180 : 0,
translate: this._selectedAnimation === 'translate' ? { x: 100, y: 100 } : { x: 0, y: 0 },
duration: 500,
})
.then(() =>
view.animate({
opacity: 1,
scale: { x: 1, y: 1 },
rotate: 0,
translate: { x: 0, y: 0 },
duration: 500,
})
)
.catch((err) => {
console.error('animation error', err);
});
}
}
}

View File

@ -0,0 +1,179 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Dev Toolbox" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout rows="*, auto, *" class="box-shadow-demo">
<StackLayout backgroundColor="#ededed" row="0" padding="20" id="boxShadowDemo">
<!-- layouts -->
<ScrollView height="100%" visibility="{{ selectedComponentType === 'layouts' ? 'visible' : 'collapsed' }}">
<StackLayout>
<StackLayout
width="300"
height="100"
class="demo-component"
boxShadow="{{ appliedBoxShadow }}"
borderWidth="{{ borderWidth }}"
borderRadius="{{ borderRadius }}"
background="{{ background }}"
tap="{{ toggleAnimation }}"
>
<Label text="StackLayout"></Label>
</StackLayout>
<GridLayout
width="300"
height="100"
class="demo-component"
boxShadow="{{ appliedBoxShadow }}"
borderWidth="{{ borderWidth }}"
borderRadius="{{ borderRadius }}"
background="{{ background }}"
tap="{{ toggleAnimation }}"
>
<Label text="GridLayout"></Label>
</GridLayout>
<AbsoluteLayout
width="300"
height="100"
class="demo-component"
boxShadow="{{ appliedBoxShadow }}"
borderWidth="{{ borderWidth }}"
borderRadius="{{ borderRadius }}"
background="{{ background }}"
tap="{{ toggleAnimation }}"
>
<Label text="AbsoluteLayout"></Label>
</AbsoluteLayout>
<DockLayout
width="300"
height="100"
class="demo-component"
boxShadow="{{ appliedBoxShadow }}"
borderWidth="{{ borderWidth }}"
borderRadius="{{ borderRadius }}"
background="{{ background }}"
tap="{{ toggleAnimation }}"
>
<Label text="DockLayout"></Label>
</DockLayout>
<FlexboxLayout
width="300"
height="100"
class="demo-component"
boxShadow="{{ appliedBoxShadow }}"
borderWidth="{{ borderWidth }}"
borderRadius="{{ borderRadius }}"
background="{{ background }}"
tap="{{ toggleAnimation }}"
>
<Label text="FlexboxLayout"></Label>
</FlexboxLayout>
</StackLayout>
</ScrollView>
<!-- labels -->
<GridLayout
rows="*"
height="100%"
visibility="{{ selectedComponentType === 'labels' ? 'visible' : 'collapsed' }}">
<Label
horizontalAlignment="center"
verticalAlignment="center"
class="demo-component"
boxShadow="{{ appliedBoxShadow }}"
borderWidth="{{ borderWidth }}"
borderRadius="{{ borderRadius }}"
background="{{ background }}"
tap="{{ toggleAnimation }}"
text="Label"></Label>
</GridLayout>
<!-- buttons -->
<GridLayout
rows="*"
height="100%"
visibility="{{ selectedComponentType === 'buttons' ? 'visible' : 'collapsed' }}">
<Button
horizontalAlignment="center"
verticalAlignment="center"
class="demo-component"
boxShadow="{{ appliedBoxShadow }}"
borderWidth="{{ borderWidth }}"
borderRadius="{{ borderRadius }}"
background="{{ background }}"
tap="{{ toggleAnimation }}"
text="button"
></Button>
</GridLayout>
</StackLayout>
<GridLayout
row="1"
rows="auto"
columns="auto, *, auto"
class="box-shadow-prop-controls">
<Label
col="0"
verticalAlignment="center"
text="box-shadow:"></Label>
<TextField
col="1"
placeholder="box-shadow"
textChange="{{ textChange }}"
>
</TextField>
<Button
col="2"
text="APPLY"
class="btn-apply"
tap="{{ applyBoxShadow }}"></Button>
</GridLayout>
<ScrollView row="2">
<StackLayout padding="10" class="controls">
<Label text="Components"></Label>
<FlexboxLayout flexDirection="row" flexWrap="wrap">
<Button text="Layouts" componentType="layouts" tap="{{ selectComponentType }}"></Button>
<Button text="Labels" componentType="labels" selectedAttr="{{ selectedComponentType }}" tap="{{ selectComponentType }}"></Button>
<Button text="Buttons" componentType="buttons" selectedAttr="{{ selectedComponentType == 'buttons' }}" tap="{{ selectComponentType }}"></Button>
</FlexboxLayout>
<Label text="Background"></Label>
<FlexboxLayout flexDirection="row" flexWrap="wrap">
<Button text="Solid" backgroundType="solid" tap="{{ selectBackgroundType }}"></Button>
<Button text="Transparent" backgroundType="transparent" tap="{{ selectBackgroundType }}"></Button>
<Button text="Gradient" backgroundType="gradient" tap="{{ selectBackgroundType }}"></Button>
</FlexboxLayout>
<Label text="Borders"></Label>
<FlexboxLayout flexDirection="row" flexWrap="wrap">
<Button text="Solid" borderType="solid" tap="{{ selectBorderType }}"></Button>
<Button text="Rounded" borderType="rounded" tap="{{ selectBorderType }}"></Button>
<Button text="None" borderType="none" tap="{{ selectBorderType }}"></Button>
</FlexboxLayout>
<Label text="Animations"></Label>
<Label text="Tap on the component to start and stop animation" class="description"></Label>
<FlexboxLayout flexDirection="row" flexWrap="wrap">
<Button text="Width" animationType="width" tap="{{ selectAnimationType }}"></Button>
<Button text="Height" animationType="height" tap="{{ selectAnimationType }}"></Button>
<Button text="Opacity" animationType="opacity" tap="{{ selectAnimationType }}"></Button>
<Button text="Translate" animationType="translate" tap="{{ selectAnimationType }}"></Button>
<Button text="Scale" animationType="scale" tap="{{ selectAnimationType }}"></Button>
<Button text="Rotate" animationType="rotate" tap="{{ selectAnimationType }}"></Button>
</FlexboxLayout>
</StackLayout>
</ScrollView>
</GridLayout>
</Page>

View File

@ -1,86 +1,15 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page"> <Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar> <Page.actionBar>
<ActionBar title="Dev Toolbox" icon="" class="action-bar"> <ActionBar title="Dev Toolbox" icon="" class="action-bar">
</ActionBar> </ActionBar>
</Page.actionBar> </Page.actionBar>
<StackLayout class="p-20">
<GridLayout rows="2*, *"> <ScrollView class="h-full">
<!-- background color transparent here to hide children overflow --> <StackLayout>
<AbsoluteLayout row="0" backgroundColor="transparent"> <Button text="list-page" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<!-- Root layout demo --> <Button text="box-shadow" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<RootLayout height="100%" width="100%"> <Button text="root-layout" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
<GridLayout height="100%" backgroundColor="#232652">
<Label verticalAlignment="center" textAlignment="center" fontWeight="bold" color="#fff" text="ROOT LAYOUT CONTENT"></Label>
</GridLayout>
</RootLayout>
</AbsoluteLayout>
<!-- Root layout controls -->
<StackLayout row="1">
<ScrollView height="100%">
<StackLayout class="p-15">
<Label color="#b4b6b9" fontSize="25" fontWeight="bold" text="ORANGE"></Label>
<FlexboxLayout flexDirection="row" justifyContent="space-between">
<Button flexGrow="1" text="open" tap="{{ open }}" popupIndex="0" class="btn btn-primary btn-active"/>
<Button flexGrow="1" text="front" tap="{{ bringToFront }}" popupIndex="0" class="btn btn-primary btn-active"/>
<Button flexGrow="1" text="close" tap="{{ close }}" popupIndex="0" class="btn btn-primary btn-active"/>
</FlexboxLayout>
<Label color="#b4b6b9" fontSize="25" fontWeight="bold" text="NAVY"></Label>
<FlexboxLayout flexDirection="row">
<Button flexGrow="1" text="open" tap="{{ open }}" popupIndex="1" class="btn btn-primary btn-active"/>
<Button flexGrow="1" text="front" tap="{{ bringToFront }}" popupIndex="1" class="btn btn-primary btn-active"/>
<Button flexGrow="1" text="close" tap="{{ close }}" popupIndex="1" class="btn btn-primary btn-active"/>
</FlexboxLayout>
<Label color="#b4b6b9" fontSize="25" fontWeight="bold" text="GRAY"></Label>
<FlexboxLayout flexDirection="row">
<Button flexGrow="1" text="open" tap="{{ open }}" popupIndex="2" class="btn btn-primary btn-active"/>
<Button flexGrow="1" text="front" tap="{{ bringToFront }}" popupIndex="2" class="btn btn-primary btn-active"/>
<Button flexGrow="1" text="close" tap="{{ close }}" popupIndex="2" class="btn btn-primary btn-active"/>
</FlexboxLayout>
<!-- <Button text="Button with html boxShadow" tap="{{ onTap }}" height="50" class="btn btn-primary btn-active" boxShadow="5 5 10 navy"/>
<Button text="Button with css boxShadow (rgba)" tap="{{ onTap }}" height="50" marginTop="50" class="btn btn-primary btn-active"/>
<Button text="Button with boxShadow 3 props" tap="{{ onTap }}" height="50" marginTop="50" boxShadow="5 5 navy" class="btn btn-primary btn-active"/>
<Button text="Button with boxShadow 4 props" tap="{{ onTap }}" height="50" marginTop="50" boxShadow="5 5 10 navy" class="btn btn-primary btn-active"/>
<Button text="Button with boxShadow 5 props" tap="{{ onTap }}" height="50" marginTop="50" boxShadow="5 5 10 10 navy" class="btn btn-primary btn-active"/>
<StackLayout boxShadow="10 10 rgba(0,0,0,1)" marginTop="50">
<Button text="Button with css boxShadow" tap="{{ onTap }}" height="50" borderRadius="10"/>
</StackLayout> -->
<!-- TODO: if backgroundColor is not set, it won't call background.ios.ts hence not applying the boxShadow -->
<!-- <StackLayout boxShadow="5 5 10 10 red" height="100" backgroundColor="transparent" padding="10" margin="20">
<Label text="StackLayout with transparent background"></Label>
</StackLayout> -->
<GridLayout boxShadow="10 -10 10 10 rgba(0,0,0,0.5)" height="100" backgroundColor="lightblue" padding="10" margin="20" tap="{{ toggleAnimation }}">
<Label text="GridLayout"></Label>
</GridLayout>
<StackLayout boxShadow="5 10 10 20 #000" height="100" backgroundColor="lightblue" padding="10" margin="20" tap="{{ toggleAnimation }}">
<Label text="StackLayout"></Label>
</StackLayout>
<AbsoluteLayout boxShadow="5 15 10 20 green" height="100" backgroundColor="lightblue" padding="10" margin="20" tap="{{ toggleAnimation }}">
<Label text="AbsoluteLayout"></Label>
</AbsoluteLayout>
<!-- note: the 3rd number in box shadow is currently being ignored, only the 1st, 2nd, and 4th, and color are being used-->
<FlexboxLayout boxShadow="0 0 10 25 red" height="100" backgroundColor="lightblue" padding="10" margin="20" tap="{{ toggleAnimation }}">
<Label text="FlexboxLayout"></Label>
</FlexboxLayout>
<FlexboxLayout boxShadow="15 10 10 20 #000" height="100" backgroundColor="transparent" padding="10" margin="20" tap="{{ toggleAnimation }}">
<Label text="FlexboxLayout (transparent background)"></Label>
</FlexboxLayout>
<Button marginTop="30" boxShadow="0 0 10 8 #000" backgroundColor="transparent" text="button" padding="20"></Button>
</StackLayout>
</ScrollView>
</StackLayout> </StackLayout>
</GridLayout> </ScrollView>
</StackLayout>
</Page> </Page>

View File

@ -1,171 +1,9 @@
import { Observable, Frame, View, StackLayout, getRootLayout, EventData, RootLayout, RootLayoutOptions } from '@nativescript/core'; import { Observable, Frame } from '@nativescript/core';
import { AnimationCurve } from '@nativescript/core/ui/enums';
export class HelloWorldModel extends Observable { export class HelloWorldModel extends Observable {
private _counter: number; viewDemo(args) {
private _message: string;
constructor() {
super();
// Initialize default values.
this._counter = 42;
this.updateMessage();
}
get message(): string {
return this._message;
}
set message(value: string) {
if (this._message !== value) {
this._message = value;
this.notifyPropertyChange('message', value);
}
}
toggleAnimation(args) {
const layout = args.object as StackLayout;
if (!layout.className) {
layout.className = 'sample-animation';
} else {
layout.className = undefined;
}
}
onTap() {
this._counter--;
this.updateMessage();
}
popupViews: { view: View; options: RootLayoutOptions; extra?: any }[] = [
{
view: this.getPopup('#EA5936', 110, -30),
options: {
shadeCover: {
color: '#FFF',
opacity: 0.7,
tapToClose: true,
},
animation: {
enterFrom: {
opacity: 0,
translateY: 500,
duration: 500,
},
exitTo: {
opacity: 0,
duration: 300,
},
},
},
extra: {
customExitAnimation: {
opacity: 0,
translate: { x: 0, y: -500 },
},
},
},
{
view: this.getPopup('#232652', 110, 0),
options: {
shadeCover: {
color: 'pink',
opacity: 0.7,
tapToClose: false,
animation: {
exitTo: {
scaleX: 0,
},
},
},
},
},
{
view: this.getPopup('#E1E4E8', 110, 30),
options: {
shadeCover: {
color: '#ffffdd',
opacity: 0.5,
tapToClose: true,
ignoreShadeRestore: true,
animation: {
enterFrom: {
translateX: -1000,
duration: 500,
},
exitTo: {
rotate: -180,
duration: 500,
},
},
},
animation: {
enterFrom: {
rotate: 180,
duration: 300,
},
exitTo: {
rotate: 180,
opacity: 0,
duration: 300,
curve: AnimationCurve.spring,
},
},
},
},
];
open(args: EventData): void {
getRootLayout()
.open(this.popupViews[(<any>args.object).popupIndex].view, this.popupViews[(<any>args.object).popupIndex].options)
.then(() => console.log('opened'))
.catch((ex) => console.error(ex));
}
bringToFront(args: EventData): void {
getRootLayout()
.bringToFront(this.popupViews[(<any>args.object).popupIndex].view, true)
.then(() => console.log('brought to front'))
.catch((ex) => console.error(ex));
}
close(args: EventData): void {
if (this.popupViews[(<any>args.object).popupIndex]?.extra?.customExitAnimation) {
getRootLayout()
.close(this.popupViews[(<any>args.object).popupIndex].view, this.popupViews[(<any>args.object).popupIndex].extra.customExitAnimation)
.then(() => console.log('closed with custom exit animation'))
.catch((ex) => console.error(ex));
} else {
getRootLayout()
.close(this.popupViews[(<any>args.object).popupIndex].view)
.then(() => console.log('closed'))
.catch((ex) => console.error(ex));
}
}
getPopup(color: string, size: number, offset: number): View {
const layout = new StackLayout();
layout.height = size;
layout.width = size;
layout.marginTop = offset;
layout.marginLeft = offset;
layout.backgroundColor = color;
layout.borderRadius = 10;
return layout;
}
viewList() {
Frame.topmost().navigate({ Frame.topmost().navigate({
moduleName: 'list-page', moduleName: `${args.object.text}`,
}); });
} }
private updateMessage() {
if (this._counter <= 0) {
this.message = 'Hoorraaay! You unlocked the NativeScript clicker achievement!';
} else {
this.message = `${this._counter} taps left`;
}
}
} }

View File

@ -0,0 +1,122 @@
import { EventData, Page, Observable, RootLayoutOptions, getRootLayout, StackLayout, View } from '@nativescript/core';
import { AnimationCurve } from '@nativescript/core/ui/enums';
export function navigatingTo(args: EventData) {
const page = <Page>args.object;
page.bindingContext = new BoxShadowModel();
}
export class BoxShadowModel extends Observable {
popupViews: { view: View; options: RootLayoutOptions; extra?: any }[] = [
{
view: this.getPopup('#EA5936', 110, -30),
options: {
shadeCover: {
color: '#FFF',
opacity: 0.7,
tapToClose: true,
},
animation: {
enterFrom: {
opacity: 0,
translateY: 500,
duration: 500,
},
exitTo: {
opacity: 0,
duration: 300,
},
},
},
extra: {
customExitAnimation: {
opacity: 0,
translate: { x: 0, y: -500 },
},
},
},
{
view: this.getPopup('#232652', 110, 0),
options: {
shadeCover: {
color: 'pink',
opacity: 0.7,
tapToClose: false,
animation: {
exitTo: {
scaleX: 0,
},
},
},
},
},
{
view: this.getPopup('#E1E4E8', 110, 30),
options: {
shadeCover: {
color: '#ffffdd',
opacity: 0.5,
tapToClose: true,
ignoreShadeRestore: true,
animation: {
enterFrom: {
translateX: -1000,
duration: 500,
},
exitTo: {
rotate: -180,
duration: 500,
},
},
},
animation: {
enterFrom: {
rotate: 180,
duration: 300,
},
exitTo: {
rotate: 180,
opacity: 0,
duration: 300,
curve: AnimationCurve.spring,
},
},
},
},
];
open(args: EventData): void {
getRootLayout()
.open(this.popupViews[(<any>args.object).popupIndex].view, this.popupViews[(<any>args.object).popupIndex].options)
.catch((ex) => console.error(ex));
}
bringToFront(args: EventData): void {
getRootLayout()
.bringToFront(this.popupViews[(<any>args.object).popupIndex].view, true)
.catch((ex) => console.error(ex));
}
close(args: EventData): void {
if (this.popupViews[(<any>args.object).popupIndex]?.extra?.customExitAnimation) {
getRootLayout()
.close(this.popupViews[(<any>args.object).popupIndex].view, this.popupViews[(<any>args.object).popupIndex].extra.customExitAnimation)
.catch((ex) => console.error(ex));
} else {
getRootLayout()
.close(this.popupViews[(<any>args.object).popupIndex].view)
.catch((ex) => console.error(ex));
}
}
getPopup(color: string, size: number, offset: number): View {
const layout = new StackLayout();
layout.height = size;
layout.width = size;
layout.marginTop = offset;
layout.marginLeft = offset;
layout.backgroundColor = color;
layout.borderRadius = 10;
return layout;
}
}

View File

@ -0,0 +1,47 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Dev Toolbox" icon="" class="action-bar">
</ActionBar>
</Page.actionBar>
<GridLayout rows="2*, *">
<!-- background color transparent here to hide children overflow -->
<AbsoluteLayout row="0" backgroundColor="transparent">
<!-- Root layout demo -->
<RootLayout height="100%" width="100%">
<GridLayout height="100%" backgroundColor="#ededed">
<Label verticalAlignment="center" textAlignment="center" fontWeight="bold" color="#333" text="ROOT LAYOUT CONTENT"></Label>
</GridLayout>
</RootLayout>
</AbsoluteLayout>
<!-- Root layout controls -->
<StackLayout row="1">
<ScrollView height="100%">
<StackLayout padding="20" class="controls">
<Label text="ORANGE"></Label>
<FlexboxLayout flexDirection="row" justifyContent="space-between">
<Button flexGrow="1" text="open" tap="{{ open }}" popupIndex="0"/>
<Button flexGrow="1" text="front" tap="{{ bringToFront }}" popupIndex="0"/>
<Button flexGrow="1" text="close" tap="{{ close }}" popupIndex="0"/>
</FlexboxLayout>
<Label text="NAVY"></Label>
<FlexboxLayout flexDirection="row" justifyContent="space-between">
<Button flexGrow="1" text="open" tap="{{ open }}" popupIndex="1"/>
<Button flexGrow="1" text="front" tap="{{ bringToFront }}" popupIndex="1"/>
<Button flexGrow="1" text="close" tap="{{ close }}" popupIndex="1"/>
</FlexboxLayout>
<Label text="GRAY"></Label>
<FlexboxLayout flexDirection="row" justifyContent="space-between">
<Button flexGrow="1" text="open" tap="{{ open }}" popupIndex="2"/>
<Button flexGrow="1" text="front" tap="{{ bringToFront }}" popupIndex="2"/>
<Button flexGrow="1" text="close" tap="{{ close }}" popupIndex="2"/>
</FlexboxLayout>
</StackLayout>
</ScrollView>
</StackLayout>
</GridLayout>
</Page>

View File

@ -95,7 +95,7 @@ export namespace ad {
const boxShadow = view.style.boxShadow; const boxShadow = view.style.boxShadow;
if (boxShadow) { if (boxShadow) {
drawBoxShadow(nativeView, boxShadow); drawBoxShadow(nativeView, view, boxShadow);
} }
// TODO: Can we move BorderWidths as separate native setter? // TODO: Can we move BorderWidths as separate native setter?
@ -226,15 +226,14 @@ function createNativeCSSValueArray(css: string): native.Array<org.nativescript.w
return nativeArray; return nativeArray;
} }
function drawBoxShadow(nativeView: android.view.View, boxShadow: BoxShadow) { function drawBoxShadow(nativeView: android.view.View, view: View, boxShadow: BoxShadow) {
const color = boxShadow.color; const color = boxShadow.color;
const shadowOpacity = color.a; const shadowOpacity = color.a;
const shadowColor = new Color(shadowOpacity, color.r, color.g, color.b); const shadowColor = new Color(shadowOpacity, color.r, color.g, color.b);
// TODO: corner radius here should reflect the view's corner radius const cornerRadius = view.borderRadius; // this should be applied to the main view as well (try 20 with a transparent background on the xml to see the effect)
const cornerRadius = 0; // this should be applied to the main view as well (try 20 with a transparent background on the xml to see the effect)
const config = { const config = {
shadowColor: shadowColor.android, shadowColor: shadowColor.android,
cornerRadius, cornerRadius: cornerRadius,
spreadRadius: boxShadow.spreadRadius, spreadRadius: boxShadow.spreadRadius,
blurRadius: boxShadow.blurRadius, blurRadius: boxShadow.blurRadius,
offsetX: boxShadow.offsetX, offsetX: boxShadow.offsetX,

View File

@ -8,8 +8,6 @@ import { isDataURI, isFileOrResourcePath, layout } from '../../utils';
import { ImageSource } from '../../image-source'; import { ImageSource } from '../../image-source';
import { CSSValue, parse as cssParse } from '../../css-value'; import { CSSValue, parse as cssParse } from '../../css-value';
import { BoxShadow } from './box-shadow'; import { BoxShadow } from './box-shadow';
import { Screen } from '../../platform';
import { StackLayout } from '../layouts/stack-layout';
export * from './background-common'; export * from './background-common';
@ -92,11 +90,10 @@ export namespace ios {
const boxShadow = view.style.boxShadow; const boxShadow = view.style.boxShadow;
if (boxShadow) { if (boxShadow) {
// this is required (if not, shadow will get cutoff at parent's dimensions) // this is required (if not, shadow will get cutoff at parent's dimensions)
// nativeView.clipsToBounds doesn't work // nativeView.clipsToBounds doesn't work
view.setProperty('clipToBounds', false); view.setProperty('clipToBounds', false);
drawBoxShadow(nativeView, boxShadow, background); drawBoxShadow(nativeView, view, boxShadow, background);
} }
} }
} }
@ -720,40 +717,29 @@ function drawNoRadiusNonUniformBorders(nativeView: NativeView, background: Backg
} }
// TODO: use sublayer if its applied to a layout // TODO: use sublayer if its applied to a layout
function drawBoxShadow(nativeView: NativeView, boxShadow: BoxShadow, background: BackgroundDefinition, useSubLayer: boolean = false) { function drawBoxShadow(nativeView: NativeView, view: View, boxShadow: BoxShadow, background: BackgroundDefinition, useSubLayer: boolean = false) {
const layer: CALayer = nativeView.layer; const layer: CALayer = nativeView.layer;
layer.masksToBounds = false; layer.masksToBounds = false;
nativeView.clipsToBounds = false; nativeView.clipsToBounds = false;
if (!background.color.a) { if (!background.color?.a) {
// add white background if view has a transparent background // add white background if view has a transparent background
layer.backgroundColor = UIColor.whiteColor.CGColor; layer.backgroundColor = UIColor.whiteColor.CGColor;
} }
// shadow opacity is handled on the shadow's color instance // shadow opacity is handled on the shadow's color instance
layer.shadowOpacity = 1; layer.shadowOpacity = 1;
layer.shadowRadius = boxShadow.spreadRadius; layer.shadowRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
layer.shadowColor = boxShadow.color.ios.CGColor; layer.shadowColor = boxShadow.color.ios.CGColor;
// / 2 here since ios's shadow offset is bigger than android
// TODO: this is just for experimenting with the amount of offset,
// need to use some real calculation here to gain parity with android's
// implementation
const adjustedShadowOffset = { const adjustedShadowOffset = {
x: boxShadow.offsetX / 2, x: layout.toDeviceIndependentPixels(boxShadow.offsetX),
y: boxShadow.offsetY / 2, y: layout.toDeviceIndependentPixels(boxShadow.offsetY),
}; };
layer.shadowOffset = CGSizeMake(adjustedShadowOffset.x, adjustedShadowOffset.y); layer.shadowOffset = CGSizeMake(adjustedShadowOffset.x, adjustedShadowOffset.y);
// this should match the view's border radius // this should match the view's border radius
const cornerRadius = 0; const cornerRadius = 0; // layout.toDeviceIndependentPixels(view.style.borderRadius);
// This doesn't handle the offsets properly
// factor in shadowRadius and the offsets so shadow don't spread too far
// layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(CGRectMake(
// nativeView.bounds.origin.x + boxShadow.spreadRadius + adjustedShadowOffset.x,
// nativeView.bounds.origin.y + boxShadow.spreadRadius + adjustedShadowOffset.y,
// nativeView.bounds.size.width - boxShadow.spreadRadius - adjustedShadowOffset.x,
// nativeView.bounds.size.height - boxShadow.spreadRadius - adjustedShadowOffset.y), cornerRadius).CGPath;
// This has the nice glow with box shadow of 0,0 // This has the nice glow with box shadow of 0,0
layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(nativeView.bounds, cornerRadius).CGPath; layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(nativeView.bounds, cornerRadius).CGPath;