From 2bf7b07bf600f82a8a8a1f52e2b7663c6cdf569b Mon Sep 17 00:00:00 2001 From: PanayotCankov Date: Fri, 4 Nov 2016 21:15:11 +0200 Subject: [PATCH] Add validation/parsing for the FlexboxLayout properties Make FlexboxLayout work with CSS properties, shorhands are not yet supported Make shorthand properties Remove redundant logging --- package.json | 1 + .../flexbox-layout/flexbox-layout-common.ts | 319 ++++++++++++------ .../flexbox-layout/flexbox-layout.android.ts | 42 +-- .../flexbox-layout/flexbox-layout.d.ts | 48 +++ .../flexbox-layout/flexbox-layout.ios.ts | 30 +- tns-core-modules/utils/types.d.ts | 7 + tns-core-modules/utils/types.ts | 4 + 7 files changed, 308 insertions(+), 143 deletions(-) diff --git a/package.json b/package.json index 1d5cac0cf..0dea4ec8f 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "grunt-tslint": "3.0.3", "grunt-typedoc": "0.2.4", "grunt-untar": "0.0.1", + "http-server": "^0.9.0", "markdown-snippet-injector": "0.1.1", "mocha": "2.2.5", "nativescript-typedoc-theme": "0.0.7", diff --git a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts index c2cb96e04..f849e00f4 100644 --- a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts +++ b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts @@ -1,9 +1,21 @@ import {LayoutBase} from "ui/layouts/layout-base"; import {View} from "ui/core/view"; import {PropertyMetadata} from "ui/core/proxy"; -import {Property, PropertyMetadataSettings, PropertyChangeData} from "ui/core/dependency-observable"; +import {Property, PropertyMetadataSettings} from "ui/core/dependency-observable"; import {registerSpecialProperty} from "ui/builder/special-properties"; import {isAndroid} from "platform"; +import {isString, isBoolean} from "utils/types"; +import styleProperty = require("ui/styling/style-property"); +import * as style from "ui/styling/style"; +import * as flexbox from "ui/layouts/flexbox-layout"; + +declare module "ui/layouts/flexbox-layout" { + export function _onNativeOrderPropertyChanged(view: View, newValue: number): void; + export function _onNativeFlexGrowPropertyChanged(view: View, newValue: number): void; + export function _onNativeFlexShrinkPropertyChanged(view: View, newValue: number): void; + export function _onNativeAlignSelfPropertyChanged(view: View, newValue: AlignSelf): void; + export function _onNativeFlexWrapBeforePropertyChanged(view: View, newValue: boolean): void; +} export type Basis = "auto" | number; @@ -11,8 +23,16 @@ const ORDER_DEFAULT = 1; const FLEX_GROW_DEFAULT = 0.0; const FLEX_SHRINK_DEFAULT = 1.0; -// on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call). -var affectsLayout = isAndroid ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout; +function makeValidator(... values: T[]): (value: any) => value is T { + const set = new Set(values); + return (value: any): value is T => set.has(value); +} +function makeParser(isValid: (value: any) => boolean, def: T): (value: any) => T { + return value => { + const lower = value && value.toLowerCase(); + return isValid(lower) ? lower : def; + } +} export type FlexDirection = "row" | "row-reverse" | "column" | "column-reverse"; export namespace FlexDirection { @@ -20,17 +40,9 @@ export namespace FlexDirection { export const ROW_REVERSE: "row-reverse" = "row-reverse"; export const COLUMN: "column" = "column"; export const COLUMN_REVERSE: "column-reverse" = "column-reverse"; -} -let validFlexDirection = { - "row": true, - "row-reverse": true, - "column": true, - "column-reverse": true -}; - -function validateFlexDirection(value: any): boolean { - return value in validFlexDirection; + export const isValid = makeValidator(ROW, ROW_REVERSE, COLUMN, COLUMN_REVERSE); + export const parse = makeParser(isValid, ROW); } export type FlexWrap = "nowrap" | "wrap" | "wrap-reverse"; @@ -38,16 +50,9 @@ export namespace FlexWrap { export const NOWRAP: "nowrap" = "nowrap"; export const WRAP: "wrap" = "wrap"; export const WRAP_REVERSE: "wrap-reverse" = "wrap-reverse"; -} -let validFlexWrap = { - "nowrap": true, - "wrap": true, - "wrap-reverse": true -}; - -function validateFlexWrap(value: any): boolean { - return value in validFlexWrap; + export const isValid = makeValidator(NOWRAP, WRAP, WRAP_REVERSE); + export const parse = makeParser(isValid, NOWRAP); } export type JustifyContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around"; @@ -57,14 +62,9 @@ export namespace JustifyContent { export const CENTER: "center" = "center"; export const SPACE_BETWEEN: "space-between" = "space-between"; export const SPACE_AROUND: "space-around" = "space-around"; -} -let validJustifyContent = { - "flex-start": true, - "flex-end": true, - "center": true, - "space-between": true, - "space-around": true + export const isValid = makeValidator(FLEX_START, FLEX_END, CENTER, SPACE_BETWEEN, SPACE_AROUND); + export const parse = makeParser(isValid, FLEX_START); } export type FlexBasisPercent = number; @@ -72,10 +72,6 @@ export namespace FlexBasisPercent { export const DEFAULT: number = -1; } -function validateJustifyContent(value: any): boolean { - return value in validJustifyContent; -} - export type AlignItems = "flex-start" | "flex-end" | "center" | "baseline" | "stretch"; export namespace AlignItems { export const FLEX_START: "flex-start" = "flex-start"; @@ -83,18 +79,9 @@ export namespace AlignItems { export const CENTER: "center" = "center"; export const BASELINE: "baseline" = "baseline"; export const STRETCH: "stretch" = "stretch"; -} -let validAlignItems = { - "flex-start": true, - "flex-end": true, - "center": true, - "baseline": true, - "stretch": true -}; - -function validateAlignItems(value: any): boolean { - return value in validAlignItems; + export const isValid = makeValidator(FLEX_START, FLEX_END, CENTER, BASELINE, STRETCH); + export const parse = makeParser(isValid, FLEX_START); } export type AlignContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "stretch"; @@ -105,19 +92,52 @@ export namespace AlignContent { export const SPACE_BETWEEN: "space-between" = "space-between"; export const SPACE_AROUND: "space-around" = "space-around"; export const STRETCH: "stretch" = "stretch"; + + export const isValid = makeValidator(FLEX_START, FLEX_END, CENTER, SPACE_BETWEEN, SPACE_AROUND, STRETCH); + export const parse = makeParser(isValid, FLEX_START); } -let validAlignContent = { - "flex-start": true, - "flex-end": true, - "center": true, - "space-between": true, - "space-around": true, - "stretch": true -}; +export type Order = number; +export namespace Order { + export function isValid(value): boolean { + return isFinite(parseInt(value)); + } + export const parse = parseInt; +} -function validateAlignContent(value: any): boolean { - return value in validAlignContent; +export type FlexGrow = number; +export namespace FlexGrow { + export function isValid(value: any): boolean { + const parsed = parseInt(value); + return isFinite(parsed) && value >= 0; + } + export const parse = parseFloat; +} + +export type FlexShrink = number; +export namespace FlexShrink { + export function isValid(value: any): boolean { + const parsed = parseInt(value); + return isFinite(parsed) && value >= 0; + } + export const parse = parseFloat; +} + +export type FlexWrapBefore = boolean; +export namespace FlexWrapBefore { + export function isValid(value) { + if (isBoolean(value)) { + return true; + } + if (isString(value)) { + const str = value.trim().toLowerCase(); + return str === "true" || str === "false"; + } + return false; + } + export function parse(value: string): FlexWrapBefore { + return value && value.toString().trim().toLowerCase() === "true"; + } } export type AlignSelf = "auto" | AlignItems; @@ -128,6 +148,9 @@ export namespace AlignSelf { export const CENTER: "center" = "center"; export const BASELINE: "baseline" = "baseline"; export const STRETCH: "stretch" = "stretch"; + + export const isValid = makeValidator(AUTO, FLEX_START, FLEX_END, CENTER, BASELINE, STRETCH); + export const parse = makeParser(isValid, AUTO); } function validateArgs(element: View): View { @@ -142,113 +165,101 @@ function validateArgs(element: View): View { */ export abstract class FlexboxLayoutBase extends LayoutBase { - public static flexDirectionProperty = new Property("flexDirection", "FlexboxLayout", new PropertyMetadata("row", affectsLayout, undefined, validateFlexDirection, (args: any) => args.object.setNativeFlexDirection(args.newValue))); - public static flexWrapProperty = new Property("flexWrap", "FlexboxLayout", new PropertyMetadata("nowrap", affectsLayout, undefined, validateFlexWrap, (args: any) => args.object.setNativeFlexWrap(args.newValue))); - public static justifyContentProperty = new Property("justifyContent", "FlexboxLayout", new PropertyMetadata("flex-start", affectsLayout, undefined, validateJustifyContent, (args: any) => args.object.setNativeJustifyContent(args.newValue))); - public static alignItemsProperty = new Property("alignItems", "FlexboxLayout", new PropertyMetadata("stretch", affectsLayout, undefined, validateAlignItems, (args: any) => args.object.setNativeAlignItems(args.newValue))); - public static alignContentProperty = new Property("alignContent", "FlexboxLayout", new PropertyMetadata("stretch", affectsLayout, undefined, validateAlignContent, (args: any) => args.object.setNativeAlignContent(args.newValue))); - - // TODO: Validation: - public static orderProperty = new Property("order", "FlexboxLayout", new PropertyMetadata(ORDER_DEFAULT, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler)); - public static flexGrowProperty = new Property("flexGrow", "FlexboxLayout", new PropertyMetadata(FLEX_GROW_DEFAULT, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler)); - public static flexShrinkProperty = new Property("flexShrink", "FlexboxLayout", new PropertyMetadata(FLEX_SHRINK_DEFAULT, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler)); - public static flexWrapBeforeProperty = new Property("flexWrapBefore", "FlexboxLayout", new PropertyMetadata(false, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler)); - public static alignSelfProperty = new Property("alignSelf", "FlexboxLayout", new PropertyMetadata(AlignSelf.AUTO, PropertyMetadataSettings.None, FlexboxLayoutBase.childHandler)); - constructor() { super(); } get flexDirection(): FlexDirection { - return this._getValue(FlexboxLayoutBase.flexDirectionProperty); + return this.style._getValue(flexDirectionProperty); } set flexDirection(value: FlexDirection) { - this._setValue(FlexboxLayoutBase.flexDirectionProperty, value); + this.style._setValue(flexDirectionProperty, value); } get flexWrap(): FlexWrap { - return this._getValue(FlexboxLayoutBase.flexWrapProperty); + return this.style._getValue(flexWrapProperty); } set flexWrap(value: FlexWrap) { - this._setValue(FlexboxLayoutBase.flexWrapProperty, value); + this.style._setValue(flexWrapProperty, value); } get justifyContent(): JustifyContent { - return this._getValue(FlexboxLayoutBase.justifyContentProperty); + return this.style._getValue(justifyContentProperty); } set justifyContent(value: JustifyContent) { - this._setValue(FlexboxLayoutBase.justifyContentProperty, value); + this.style._setValue(justifyContentProperty, value); } get alignItems(): AlignItems { - return this._getValue(FlexboxLayoutBase.alignItemsProperty); + return this.style._getValue(alignItemsProperty); } set alignItems(value: AlignItems) { - this._setValue(FlexboxLayoutBase.alignItemsProperty, value); + this.style._setValue(alignItemsProperty, value); } get alignContent(): AlignContent { - return this._getValue(FlexboxLayoutBase.alignContentProperty); + return this.style._getValue(alignContentProperty); } set alignContent(value: AlignContent) { - this._setValue(FlexboxLayoutBase.alignContentProperty, value); + this.style._setValue(alignContentProperty, value); } public static setOrder(view: View, order: number) { - validateArgs(view)._setValue(FlexboxLayoutBase.orderProperty, order); + validateArgs(view).style._setValue(orderProperty, order); } public static getOrder(view: View): number { - return validateArgs(view)._getValue(FlexboxLayoutBase.orderProperty); + return validateArgs(view).style._getValue(orderProperty); } public static setFlexGrow(view: View, grow: number) { - validateArgs(view)._setValue(FlexboxLayoutBase.flexGrowProperty, grow); + validateArgs(view).style._setValue(flexGrowProperty, grow); } public static getFlexGrow(view: View) { - return validateArgs(view)._getValue(FlexboxLayoutBase.flexGrowProperty); + return validateArgs(view).style._getValue(flexGrowProperty); } public static setFlexShrink(view: View, shrink: number) { - validateArgs(view)._setValue(FlexboxLayoutBase.flexShrinkProperty, shrink); + validateArgs(view).style._setValue(flexShrinkProperty, shrink); } public static getFlexShrink(view: View): number { - return validateArgs(view)._getValue(FlexboxLayoutBase.flexShrinkProperty); + return validateArgs(view).style._getValue(flexShrinkProperty); } public static setAlignSelf(view: View, align: AlignSelf) { - validateArgs(view)._setValue(FlexboxLayoutBase.alignSelfProperty, align); + validateArgs(view).style._setValue(alignSelfProperty, align); } public static getAlignSelf(view: View): AlignSelf { - return validateArgs(view)._getValue(FlexboxLayoutBase.alignSelfProperty); + return validateArgs(view).style._getValue(alignSelfProperty); } public static setFlexWrapBefore(view: View, wrap: boolean) { - view._setValue(FlexboxLayoutBase.flexWrapBeforeProperty, wrap); + validateArgs(view).style._setValue(flexWrapBeforeProperty, wrap); } public static getFlexWrapBefore(view: View): boolean { - return view._getValue(FlexboxLayoutBase.flexWrapBeforeProperty); + return validateArgs(view).style._getValue(flexWrapBeforeProperty); } - protected abstract setNativeFlexDirection(flexDirection: FlexDirection); - protected abstract setNativeFlexWrap(flexWrap: FlexWrap); - protected abstract setNativeJustifyContent(justifyContent: JustifyContent); - protected abstract setNativeAlignItems(alignItems: AlignItems); - protected abstract setNativeAlignContent(alignContent: AlignContent); - - private static childHandler(args: PropertyChangeData) { - let element = args.object as View; - if (!(element instanceof View)) { - throw new Error("Element is not View or its descendant."); - } - let flexbox = element.parent; - if (flexbox instanceof FlexboxLayoutBase) { - flexbox.invalidate(); - } - } - - protected abstract invalidate(); + abstract _setNativeFlexDirection(flexDirection: FlexDirection); + abstract _setNativeFlexWrap(flexWrap: FlexWrap); + abstract _setNativeJustifyContent(justifyContent: JustifyContent); + abstract _setNativeAlignItems(alignItems: AlignItems); + abstract _setNativeAlignContent(alignContent: AlignContent); } +const flexboxAffectsLayout = isAndroid ? PropertyMetadataSettings.None : PropertyMetadataSettings.AffectsLayout; + +export const flexDirectionProperty = new styleProperty.Property("flexDirection", "flex-direction", new PropertyMetadata(FlexDirection.ROW, flexboxAffectsLayout, undefined, FlexDirection.isValid), FlexDirection.parse); +export const flexWrapProperty = new styleProperty.Property("flexWrap", "flex-wrap", new PropertyMetadata(FlexWrap.NOWRAP, flexboxAffectsLayout, undefined, FlexWrap.isValid), FlexWrap.parse); +export const justifyContentProperty = new styleProperty.Property("justifyContent", "justify-content", new PropertyMetadata(JustifyContent.FLEX_START, flexboxAffectsLayout, undefined, JustifyContent.isValid), JustifyContent.parse); +export const alignItemsProperty = new styleProperty.Property("alignItems", "align-items", new PropertyMetadata(AlignItems.STRETCH, flexboxAffectsLayout, undefined, AlignItems.isValid), AlignItems.parse); +export const alignContentProperty = new styleProperty.Property("alignContent", "align-content", new PropertyMetadata(AlignContent.STRETCH, flexboxAffectsLayout, undefined, AlignContent.isValid), AlignContent.parse); + +export const orderProperty = new styleProperty.Property("order", "order", new PropertyMetadata(ORDER_DEFAULT, PropertyMetadataSettings.None, undefined, Order.isValid), Order.parse); +export const flexGrowProperty = new styleProperty.Property("flexGrow", "flex-grow", new PropertyMetadata(FLEX_GROW_DEFAULT, PropertyMetadataSettings.None, undefined, FlexGrow.isValid), FlexGrow.parse); +export const flexShrinkProperty = new styleProperty.Property("flexShrink", "flex-shrink", new PropertyMetadata(FLEX_SHRINK_DEFAULT, PropertyMetadataSettings.None, undefined, FlexShrink.isValid), FlexShrink.parse); +export const flexWrapBeforeProperty = new styleProperty.Property("flexWrapBefore", "flex-wrap-before", new PropertyMetadata(false, PropertyMetadataSettings.None, undefined, FlexWrapBefore.isValid), FlexWrapBefore.parse); +export const alignSelfProperty = new styleProperty.Property("alignSelf", "align-self", new PropertyMetadata(AlignSelf.AUTO, PropertyMetadataSettings.None, undefined, AlignSelf.isValid), AlignSelf.parse); + registerSpecialProperty("order", (instance, propertyValue) => { FlexboxLayoutBase.setOrder(instance, !isNaN(+propertyValue) && +propertyValue); }); @@ -262,6 +273,100 @@ registerSpecialProperty("alignSelf", (instance, propertyValue) => { FlexboxLayoutBase.setAlignSelf(instance, propertyValue); }); registerSpecialProperty("flexWrapBefore", (instance, propertyValue) => { - FlexboxLayoutBase.setFlexWrapBefore(instance, propertyValue); + FlexboxLayoutBase.setFlexWrapBefore(instance, isString(propertyValue) ? FlexWrapBefore.parse(propertyValue) : propertyValue); }); -// No flex-basis in our implementation. \ No newline at end of file + +const flexboxGuard = (handler: (flexbox: FlexboxLayoutBase, newValue: any) => void) => (view: View, newValue: any) => view instanceof FlexboxLayoutBase ? handler(view, newValue) : void 0; +style.registerHandler(flexDirectionProperty, new style.StylePropertyChangedHandler( + flexboxGuard((flexbox, newValue) => flexbox._setNativeFlexDirection(newValue)), + flexboxGuard((flexbox, newValue) => flexbox._setNativeFlexDirection(FlexDirection.ROW))), "FlexboxLayout"); +style.registerHandler(flexWrapProperty, new style.StylePropertyChangedHandler( + flexboxGuard((flexbox, newValue) => flexbox._setNativeFlexWrap(newValue)), + flexboxGuard((flexbox, newValue) => flexbox._setNativeFlexWrap(FlexWrap.NOWRAP))), "FlexboxLayout"); +style.registerHandler(justifyContentProperty, new style.StylePropertyChangedHandler( + flexboxGuard((flexbox, newValue) => flexbox._setNativeJustifyContent(newValue)), + flexboxGuard((flexbox, newValue) => flexbox._setNativeJustifyContent(JustifyContent.FLEX_START))), "FlexboxLayout"); +style.registerHandler(alignItemsProperty, new style.StylePropertyChangedHandler( + flexboxGuard((flexbox, newValue) => flexbox._setNativeAlignItems(newValue)), + flexboxGuard((flexbox, newValue) => flexbox._setNativeAlignItems(AlignItems.STRETCH))), "FlexboxLayout"); +style.registerHandler(alignContentProperty, new style.StylePropertyChangedHandler( + flexboxGuard((flexbox, newValue) => flexbox._setNativeAlignContent(newValue)), + flexboxGuard((flexbox, newValue) => flexbox._setNativeAlignContent(AlignContent.STRETCH))), "FlexboxLayout"); + +style.registerHandler(orderProperty, new style.StylePropertyChangedHandler( + (view, value) => flexbox._onNativeOrderPropertyChanged(view, value), + (view, value) => flexbox._onNativeOrderPropertyChanged(view, 1)), "View"); +style.registerHandler(flexGrowProperty, new style.StylePropertyChangedHandler( + (view, value) => flexbox._onNativeFlexGrowPropertyChanged(view, value), + (view, value) => flexbox._onNativeFlexGrowPropertyChanged(view, 0)), "View"); +style.registerHandler(flexShrinkProperty, new style.StylePropertyChangedHandler( + (view, value) => flexbox._onNativeFlexShrinkPropertyChanged(view, value), + (view, value) => flexbox._onNativeFlexShrinkPropertyChanged(view, 1)), "View"); +style.registerHandler(flexWrapBeforeProperty, new style.StylePropertyChangedHandler( + (view, value) => flexbox._onNativeFlexWrapBeforePropertyChanged(view, value), + (view, value) => flexbox._onNativeFlexWrapBeforePropertyChanged(view, false)), "View"); +style.registerHandler(alignSelfProperty, new style.StylePropertyChangedHandler( + (view, value) => flexbox._onNativeAlignSelfPropertyChanged(view, value), + (view, value) => flexbox._onNativeAlignSelfPropertyChanged(view, AlignSelf.AUTO)), "View"); + +// flex-flow: || +styleProperty.registerShorthandCallback("flex-flow", value => { + const properties: styleProperty.KeyValuePair[] = []; + const trimmed = value && value.trim(); + if (trimmed) { + let values = trimmed.split(/\s+/); + if (values.length >= 1 && FlexDirection.isValid(values[0])) { + properties.push({ property: flexDirectionProperty, value: FlexDirection.parse(values[0]) }); + } + if (value.length >= 2 && FlexWrap.isValid(values[1])) { + properties.push({ property: flexWrapProperty, value: FlexWrap.parse(values[1]) }); + } + } + return properties; +}); + +// flex: inital | auto | none | || +styleProperty.registerShorthandCallback("flex", value => { + const properties: styleProperty.KeyValuePair[] = []; + const trimmed = value && value.trim(); + if (trimmed) { + let values = trimmed.split(/\s+/); + if (values.length === 1) { + switch(values[0]) { + case "inital": + properties.push({ property: flexGrowProperty, value: 0}); + properties.push({ property: flexShrinkProperty, value: 1}); + // properties.push({ property: flexBasisProperty, value: FlexBasis.AUTO}) + break; + case "auto": + properties.push({ property: flexGrowProperty, value: 1}); + properties.push({ property: flexShrinkProperty, value: 1}); + // properties.push({ property: flexBasisProperty, value: FlexBasis.AUTO}) + break; + case "none": + properties.push({ property: flexGrowProperty, value: 0}); + properties.push({ property: flexShrinkProperty, value: 0}); + // properties.push({ property: flexBasisProperty, value: FlexBasis.AUTO}) + break; + default: + if (FlexGrow.isValid(values[0])) { + properties.push({ property: flexGrowProperty, value: FlexGrow.parse(values[0])}); + properties.push({ property: flexShrinkProperty, value: 1}); + // properties.push({ property: flexBasisProperty, value: 0}) + } + } + } + if (values.length >= 2) { + if (FlexGrow.isValid(values[0]) && FlexShrink.isValid(values[1])) { + properties.push({ property: flexGrowProperty, value: FlexGrow.parse(values[0])}); + properties.push({ property: flexShrinkProperty, value: FlexShrink.parse(values[1])}); + } + } + // if (value.length >= 3) { + // properties.push({ property: flexBasisProperty, value: FlexBasis.parse(values[2])}) + // } + } + return properties; +}); + +// No flex-basis in our implementation. diff --git a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts index 785421ffb..8e2a48bcd 100644 --- a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts +++ b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts @@ -8,8 +8,6 @@ import { AlignSelf, FlexboxLayoutBase } from "./flexbox-layout-common"; -import {PropertyMetadata} from "ui/core/proxy"; -import {PropertyChangeData} from "ui/core/dependency-observable"; import {layout} from "utils/utils"; function setLayoutParamsProperty(view: View, setter: (lp: org.nativescript.widgets.FlexboxLayout.LayoutParams) => void) { @@ -23,32 +21,26 @@ function setLayoutParamsProperty(view: View, setter: (lp: org.nativescript.widge } } -function onNativeOrderPropertyChanged({object, newValue}: PropertyChangeData): void { - setLayoutParamsProperty(object, lp => lp.order = newValue); +export function _onNativeOrderPropertyChanged(view: View, newValue: number): void { + setLayoutParamsProperty(view, lp => lp.order = newValue); } -function onNativeFlexGrowPropertyChanged({object, newValue}: PropertyChangeData): void { - setLayoutParamsProperty(object, lp => lp.flexGrow = newValue); +export function _onNativeFlexGrowPropertyChanged(view: View, newValue: number): void { + setLayoutParamsProperty(view, lp => lp.flexGrow = newValue); } -function onNativeFlexShrinkPropertyChanged({object, newValue}: PropertyChangeData): void { - setLayoutParamsProperty(object, lp => lp.flexShrink = newValue); +export function _onNativeFlexShrinkPropertyChanged(view: View, newValue: number): void { + setLayoutParamsProperty(view, lp => lp.flexShrink = newValue); } -function onNativeAlignSelfPropertyChanged({object, newValue}: PropertyChangeData): void { - setLayoutParamsProperty(object, lp => lp.alignSelf = alignSelfMap[newValue]); +export function _onNativeAlignSelfPropertyChanged(view: View, newValue: AlignSelf): void { + setLayoutParamsProperty(view, lp => lp.alignSelf = alignSelfMap[newValue]); } -function onNativeFlexWrapBeforePropertyChanged({object, newValue}: PropertyChangeData): void { - setLayoutParamsProperty(object, lp => lp.wrapBefore = newValue); +export function _onNativeFlexWrapBeforePropertyChanged(view: View, newValue: boolean): void { + setLayoutParamsProperty(view, lp => lp.wrapBefore = newValue); } -(FlexboxLayoutBase.orderProperty.metadata).onSetNativeValue = onNativeOrderPropertyChanged; -(FlexboxLayoutBase.flexGrowProperty.metadata).onSetNativeValue = onNativeFlexGrowPropertyChanged; -(FlexboxLayoutBase.flexShrinkProperty.metadata).onSetNativeValue = onNativeFlexShrinkPropertyChanged; -(FlexboxLayoutBase.alignSelfProperty.metadata).onSetNativeValue = onNativeAlignSelfPropertyChanged; -(FlexboxLayoutBase.flexWrapBeforeProperty.metadata).onSetNativeValue = onNativeFlexWrapBeforePropertyChanged; - export * from "./flexbox-layout-common"; import FlexboxLayoutWidget = org.nativescript.widgets.FlexboxLayout; @@ -114,30 +106,26 @@ export class FlexboxLayout extends FlexboxLayoutBase { this._layout = new org.nativescript.widgets.FlexboxLayout(this._context); } - protected setNativeFlexDirection(flexDirection: FlexDirection) { + _setNativeFlexDirection(flexDirection: FlexDirection) { let value = flexDirectionMap[flexDirection]; this.android.setFlexDirection(value); } - protected setNativeFlexWrap(flexWrap: FlexWrap) { + _setNativeFlexWrap(flexWrap: FlexWrap) { this.android.setFlexWrap(flexWrapMap[flexWrap]); } - protected setNativeJustifyContent(justifyContent: JustifyContent) { + _setNativeJustifyContent(justifyContent: JustifyContent) { this.android.setJustifyContent(justifyContentMap[justifyContent]); } - protected setNativeAlignItems(alignItems: AlignItems) { + _setNativeAlignItems(alignItems: AlignItems) { this.android.setAlignItems(alignItemsMap[alignItems]); } - protected setNativeAlignContent(alignContent: AlignContent) { + _setNativeAlignContent(alignContent: AlignContent) { this.android.setAlignContent(alignContentMap[alignContent]); } - - protected invalidate() { - // no operation - } } export function _setAndroidLayoutParams(lp: org.nativescript.widgets.FlexboxLayout.LayoutParams, view: View) { diff --git a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.d.ts b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.d.ts index 47692dae2..934396c11 100644 --- a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.d.ts +++ b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.d.ts @@ -10,6 +10,8 @@ declare module "ui/layouts/flexbox-layout" { export const ROW_REVERSE: "row-reverse"; export const COLUMN: "column"; export const COLUMN_REVERSE: "column-reverse"; + export function isValid(value: any): boolean; + export function parse(value: string): FlexDirection; } export type FlexWrap = "nowrap" | "wrap" | "wrap-reverse"; @@ -17,6 +19,8 @@ declare module "ui/layouts/flexbox-layout" { export const NOWRAP: "nowrap"; export const WRAP: "wrap"; export const WRAP_REVERSE: "wrap-reverse"; + export function isValid(value: any): boolean; + export function parse(value: string): FlexWrap; } export type JustifyContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around"; @@ -26,6 +30,8 @@ declare module "ui/layouts/flexbox-layout" { export const CENTER: "center"; export const SPACE_BETWEEN: "space-between"; export const SPACE_AROUND: "space-around"; + export function isValid(value: any): boolean; + export function parse(value: string): JustifyContent; } export type AlignItems = "flex-start" | "flex-end" | "center" | "baseline" | "stretch"; @@ -35,6 +41,8 @@ declare module "ui/layouts/flexbox-layout" { export const CENTER: "center"; export const BASELINE: "baseline"; export const STRETCH: "stretch"; + export function isValid(value: any): boolean; + export function parse(value: string): AlignItems; } export type AlignContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "stretch"; @@ -45,6 +53,44 @@ declare module "ui/layouts/flexbox-layout" { export const SPACE_BETWEEN: "space-between"; export const SPACE_AROUND: "space-around"; export const STRETCH: "stretch"; + export function isValid(value: any): boolean; + export function parse(value: string): AlignContent; + } + + /** + * A flex order integer. + */ + export type Order = number; + export namespace Order { + export function isValid(value: any): boolean; + export function parse(value: string): Order; + } + + /** + * A flex-grow number. Negative values are invalid. + */ + export type FlexGrow = number; + export namespace FlexGrow { + export function isValid(value: any): boolean; + export function parse(value: string): FlexGrow; + } + + /** + * A flex-shrink number. Negative values are invalid. + */ + export type FlexShrink = number; + export namespace FlexShrink { + export function isValid(value: any): boolean; + export function parse(value: string): FlexShrink; + } + + /** + * A flex-wrap-before value. True, false or their string presentations "true" or "false". + */ + export type FlexWrapBefore = boolean; + export namespace FlexWrapBefore { + export function isValid(value: any): boolean; + export function parse(value: string): FlexWrapBefore; } export type AlignSelf = "auto" | AlignItems; @@ -55,6 +101,8 @@ declare module "ui/layouts/flexbox-layout" { export const CENTER: "center"; export const BASELINE: "baseline"; export const STRETCH: "stretch"; + export function isValid(value: any): boolean; + export function parse(value: string): AlignSelf; } export class FlexboxLayout extends LayoutBase { diff --git a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.ios.ts b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.ios.ts index 5ffd2828f..eb20ccb2e 100644 --- a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.ios.ts +++ b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.ios.ts @@ -24,6 +24,22 @@ import UNSPECIFIED = utils.layout.UNSPECIFIED; import MEASURED_SIZE_MASK = utils.layout.MEASURED_SIZE_MASK; import MEASURED_STATE_TOO_SMALL = utils.layout.MEASURED_STATE_TOO_SMALL; +function childHandler(view) { + if (!(view instanceof View)) { + throw new Error("Element is not View or its descendant."); + } + let flexbox = view.parent; + if (flexbox instanceof FlexboxLayoutBase) { + flexbox.requestLayout(); + } +} + +export const _onNativeOrderPropertyChanged = childHandler; +export const _onNativeFlexGrowPropertyChanged = childHandler; +export const _onNativeFlexShrinkPropertyChanged = childHandler; +export const _onNativeAlignSelfPropertyChanged = childHandler; +export const _onNativeFlexWrapBeforePropertyChanged = childHandler; + const MATCH_PARENT = -1; const WRAP_CONTENT = -2; @@ -87,23 +103,19 @@ export class FlexboxLayout extends FlexboxLayoutBase { private _flexLines: FlexLine[] = []; private _childrenFrozen: boolean[]; - protected invalidate() { - this.requestLayout(); - } - - protected setNativeFlexDirection(flexDirection: FlexDirection) { + _setNativeFlexDirection(flexDirection: FlexDirection) { // lint happy no-op } - protected setNativeFlexWrap(flexWrap: FlexWrap) { + _setNativeFlexWrap(flexWrap: FlexWrap) { // lint happy no-op } - protected setNativeJustifyContent(justifyContent: JustifyContent) { + _setNativeJustifyContent(justifyContent: JustifyContent) { // lint happy no-op } - protected setNativeAlignItems(alignItems: AlignItems) { + _setNativeAlignItems(alignItems: AlignItems) { // lint happy no-op } - protected setNativeAlignContent(alignContent: AlignContent) { + _setNativeAlignContent(alignContent: AlignContent) { // lint happy no-op } diff --git a/tns-core-modules/utils/types.d.ts b/tns-core-modules/utils/types.d.ts index d6d725bbf..5cc4677bd 100644 --- a/tns-core-modules/utils/types.d.ts +++ b/tns-core-modules/utils/types.d.ts @@ -13,6 +13,13 @@ */ export function isNumber(value: any): boolean; + /** + * A function that checks if something is a valid boolean. + * @param value The value which will be checked. + * Returns true if value is a boolean. + */ + export function isBoolean(value: any): boolean; + /** * A function that checks if something is a function. * @param value The value which will be checked. diff --git a/tns-core-modules/utils/types.ts b/tns-core-modules/utils/types.ts index 0c526e2a9..b2086484f 100644 --- a/tns-core-modules/utils/types.ts +++ b/tns-core-modules/utils/types.ts @@ -6,6 +6,10 @@ export function isNumber(value: any): boolean { return typeof value === "number" || value instanceof Number; } +export function isBoolean(value: any): boolean { + return typeof value === "boolean" || value instanceof Boolean; +} + export function isFunction(value: any): boolean { if (!value) { return false;