mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00

* chore: move tns-core-modules to nativescript-core * chore: preparing compat generate script * chore: add missing definitions * chore: no need for http-request to be private * chore: packages chore * test: generate tests for tns-core-modules * chore: add anroid module for consistency * chore: add .npmignore * chore: added privateModulesWhitelist * chore(webpack): added bundle-entry-points * chore: scripts * chore: tests changed to use @ns/core * test: add scoped-packages test project * test: fix types * test: update test project * chore: build scripts * chore: update build script * chore: npm scripts cleanup * chore: make the compat pgk work with old wp config * test: generate diff friendly tests * chore: create barrel exports * chore: move files after rebase * chore: typedoc config * chore: compat mode * chore: review of barrels * chore: remove tns-core-modules import after rebase * chore: dev workflow setup * chore: update developer-workflow * docs: experiment with API extractor * chore: api-extractor and barrel exports * chore: api-extractor configs * chore: generate d.ts rollup with api-extractor * refactor: move methods inside Frame * chore: fic tests to use Frame static methods * refactor: create Builder class * refactor: use Builder class in tests * refactor: include Style in ui barrel * chore: separate compat build script * chore: fix tslint errors * chore: update NATIVESCRIPT_CORE_ARGS * chore: fix compat pack * chore: fix ui-test-app build with linked modules * chore: Application, ApplicationSettings, Connectivity and Http * chore: export Trace, Profiling and Utils * refactor: Static create methods for ImageSource * chore: fix deprecated usages of ImageSource * chore: move Span and FormattedString to ui * chore: add events-args and ImageSource to index files * chore: check for CLI >= 6.2 when building for IOS * chore: update travis build * chore: copy Pod file to compat package * chore: update error msg ui-tests-app * refactor: Apply suggestions from code review Co-Authored-By: Martin Yankov <m.i.yankov@gmail.com> * chore: typings and refs * chore: add missing d.ts files for public API * chore: adress code review FB * chore: update api-report * chore: dev-workflow for other apps * chore: api update * chore: update api-report
458 lines
14 KiB
TypeScript
458 lines
14 KiB
TypeScript
import {
|
|
ActionBar as ActionBarDefinition,
|
|
ActionItems as ActionItemsDefinition,
|
|
ActionItem as ActionItemDefinition,
|
|
NavigationButton, IOSActionItemSettings, AndroidActionItemSettings, AndroidActionBarSettings,
|
|
} from ".";
|
|
|
|
import { profile } from "../../profiling";
|
|
|
|
export * from "../core/view";
|
|
|
|
import {
|
|
View,
|
|
ViewBase,
|
|
Property,
|
|
unsetValue,
|
|
booleanConverter,
|
|
horizontalAlignmentProperty,
|
|
verticalAlignmentProperty,
|
|
CSSType,
|
|
traceWrite,
|
|
traceCategories,
|
|
traceMessageType
|
|
} from "../core/view";
|
|
import { ShorthandProperty, CssProperty, Style } from "../core/properties/properties";
|
|
import { Length } from "../core/view";
|
|
|
|
export module knownCollections {
|
|
export const actionItems = "actionItems";
|
|
}
|
|
|
|
@CSSType("ActionBar")
|
|
export class ActionBarBase extends View implements ActionBarDefinition {
|
|
private _actionItems: ActionItems;
|
|
private _navigationButton: NavigationButton;
|
|
private _titleView: View;
|
|
|
|
public title: string;
|
|
public flat: boolean;
|
|
public iosIconRenderingMode: "automatic" | "alwaysOriginal" | "alwaysTemplate";
|
|
|
|
public effectiveContentInsetLeft: number;
|
|
public effectiveContentInsetRight: number;
|
|
|
|
get navigationButton(): NavigationButton {
|
|
return this._navigationButton;
|
|
}
|
|
set navigationButton(value: NavigationButton) {
|
|
if (this._navigationButton !== value) {
|
|
if (this._navigationButton) {
|
|
this._removeView(this._navigationButton);
|
|
this._navigationButton.actionBar = undefined;
|
|
}
|
|
|
|
this._navigationButton = value;
|
|
|
|
if (this._navigationButton) {
|
|
this._navigationButton.actionBar = this;
|
|
this._addView(this._navigationButton);
|
|
}
|
|
|
|
this.update();
|
|
}
|
|
}
|
|
|
|
get actionItems(): ActionItems {
|
|
return this._actionItems;
|
|
}
|
|
set actionItems(value: ActionItems) {
|
|
throw new Error("actionItems property is read-only");
|
|
}
|
|
|
|
get titleView(): View {
|
|
return this._titleView;
|
|
}
|
|
set titleView(value: View) {
|
|
if (this._titleView !== value) {
|
|
if (this._titleView) {
|
|
this._removeView(this._titleView);
|
|
this._titleView.style[horizontalAlignmentProperty.cssName] = unsetValue;
|
|
this._titleView.style[verticalAlignmentProperty.cssName] = unsetValue;
|
|
}
|
|
|
|
this._titleView = value;
|
|
|
|
if (value) {
|
|
// Addview will reset CSS properties so we first add it then set aligments with lowest priority.
|
|
this._addView(value);
|
|
const style = value.style;
|
|
|
|
if (!horizontalAlignmentProperty.isSet(style)) {
|
|
style[horizontalAlignmentProperty.cssName] = "center";
|
|
}
|
|
|
|
if (!verticalAlignmentProperty.isSet(style)) {
|
|
style[verticalAlignmentProperty.cssName] = "middle";
|
|
}
|
|
}
|
|
|
|
this.update();
|
|
}
|
|
}
|
|
|
|
public get androidContentInset(): string | Length {
|
|
return this.style.androidContentInset;
|
|
}
|
|
public set androidContentInset(value: string | Length) {
|
|
this.style.androidContentInset = value;
|
|
}
|
|
|
|
public get androidContentInsetLeft(): Length {
|
|
return this.style.androidContentInsetLeft;
|
|
}
|
|
public set androidContentInsetLeft(value: Length) {
|
|
this.style.androidContentInsetLeft = value;
|
|
}
|
|
|
|
public get androidContentInsetRight(): Length {
|
|
return this.style.androidContentInsetRight;
|
|
}
|
|
public set androidContentInsetRight(value: Length) {
|
|
this.style.androidContentInsetRight = value;
|
|
}
|
|
|
|
get ios(): any {
|
|
return undefined;
|
|
}
|
|
|
|
get android(): AndroidActionBarSettings {
|
|
return undefined;
|
|
}
|
|
|
|
get _childrenCount(): number {
|
|
let actionViewsCount = 0;
|
|
this._actionItems.getItems().forEach((actionItem) => {
|
|
if (actionItem.actionView) {
|
|
actionViewsCount++;
|
|
}
|
|
});
|
|
|
|
return actionViewsCount + (this.titleView ? 1 : 0);
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this._actionItems = new ActionItems(this);
|
|
}
|
|
|
|
public static onTitleChanged;
|
|
|
|
public update() {
|
|
//
|
|
}
|
|
|
|
public _onTitlePropertyChanged() {
|
|
//
|
|
}
|
|
|
|
public _addArrayFromBuilder(name: string, value: Array<any>) {
|
|
if (name === "actionItems") {
|
|
this.actionItems.setItems(value);
|
|
}
|
|
}
|
|
|
|
public eachChildView(callback: (child: View) => boolean) {
|
|
const titleView = this.titleView;
|
|
if (titleView) {
|
|
callback(titleView);
|
|
}
|
|
}
|
|
|
|
public eachChild(callback: (child: ViewBase) => boolean) {
|
|
const titleView = this.titleView;
|
|
if (titleView) {
|
|
callback(titleView);
|
|
}
|
|
|
|
const navigationButton = this._navigationButton;
|
|
if (navigationButton) {
|
|
callback(navigationButton);
|
|
}
|
|
|
|
this.actionItems.getItems().forEach((actionItem) => {
|
|
callback(actionItem);
|
|
});
|
|
}
|
|
|
|
public _isEmpty(): boolean {
|
|
if (this.title ||
|
|
this.titleView ||
|
|
(this.android && this.android.icon) ||
|
|
this.navigationButton ||
|
|
this.actionItems.getItems().length > 0) {
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
export class ActionItems implements ActionItemsDefinition {
|
|
private _items = new Array<ActionItemDefinition>();
|
|
private _actionBar: ActionBarDefinition;
|
|
|
|
constructor(actionBar: ActionBarDefinition) {
|
|
this._actionBar = actionBar;
|
|
}
|
|
|
|
public addItem(item: ActionItemDefinition): void {
|
|
if (!item) {
|
|
throw new Error("Cannot add empty item");
|
|
}
|
|
|
|
this._items.push(item);
|
|
item.actionBar = this._actionBar;
|
|
|
|
this._actionBar._addView(item);
|
|
|
|
this.invalidate();
|
|
}
|
|
|
|
public removeItem(item: ActionItemDefinition): void {
|
|
if (!item) {
|
|
throw new Error("Cannot remove empty item");
|
|
}
|
|
|
|
const itemIndex = this._items.indexOf(item);
|
|
if (itemIndex < 0) {
|
|
throw new Error("Cannot find item to remove");
|
|
}
|
|
|
|
this._items.splice(itemIndex, 1);
|
|
this._actionBar._removeView(item);
|
|
|
|
item.actionBar = undefined;
|
|
this.invalidate();
|
|
}
|
|
|
|
public getItems(): Array<ActionItemDefinition> {
|
|
return this._items.slice();
|
|
}
|
|
|
|
public getVisibleItems(): Array<ActionItemDefinition> {
|
|
const visibleItems = [];
|
|
this._items.forEach((item) => {
|
|
if (isVisible(item)) {
|
|
visibleItems.push(item);
|
|
}
|
|
});
|
|
|
|
return visibleItems;
|
|
}
|
|
|
|
public getItemAt(index: number): ActionItemDefinition {
|
|
if (index < 0 || index >= this._items.length) {
|
|
return undefined;
|
|
}
|
|
|
|
return this._items[index];
|
|
}
|
|
|
|
public setItems(items: Array<ActionItemDefinition>) {
|
|
// Remove all existing items
|
|
while (this._items.length > 0) {
|
|
this.removeItem(this._items[this._items.length - 1]);
|
|
}
|
|
|
|
// Add new items
|
|
for (let i = 0; i < items.length; i++) {
|
|
this.addItem(items[i]);
|
|
}
|
|
|
|
this.invalidate();
|
|
}
|
|
|
|
private invalidate() {
|
|
if (this._actionBar) {
|
|
this._actionBar.update();
|
|
}
|
|
}
|
|
}
|
|
|
|
export class ActionItemBase extends ViewBase implements ActionItemDefinition {
|
|
public static tapEvent = "tap";
|
|
|
|
private _actionBar: ActionBarDefinition;
|
|
private _actionView: View;
|
|
|
|
public ios: IOSActionItemSettings;
|
|
public android: AndroidActionItemSettings;
|
|
|
|
public text: string;
|
|
public icon: string;
|
|
public visibility: string;
|
|
|
|
get actionView(): View {
|
|
return this._actionView;
|
|
}
|
|
set actionView(value: View) {
|
|
if (this._actionView !== value) {
|
|
if (this._actionView) {
|
|
this._actionView.style[horizontalAlignmentProperty.cssName] = unsetValue;
|
|
this._actionView.style[verticalAlignmentProperty.cssName] = unsetValue;
|
|
this._removeView(this._actionView);
|
|
}
|
|
|
|
this._actionView = value;
|
|
|
|
if (this._actionView) {
|
|
this._addView(this._actionView);
|
|
}
|
|
|
|
if (this._actionBar) {
|
|
this._actionBar.update();
|
|
}
|
|
}
|
|
}
|
|
|
|
get actionBar(): ActionBarDefinition {
|
|
return this._actionBar;
|
|
}
|
|
set actionBar(value: ActionBarDefinition) {
|
|
if (value !== this._actionBar) {
|
|
this._actionBar = value;
|
|
}
|
|
}
|
|
|
|
@profile
|
|
public onLoaded() {
|
|
if (this._actionView) {
|
|
this._actionView.style[horizontalAlignmentProperty.cssName] = "center";
|
|
this._actionView.style[verticalAlignmentProperty.cssName] = "middle";
|
|
}
|
|
super.onLoaded();
|
|
}
|
|
|
|
public _raiseTap() {
|
|
this._emit(ActionItemBase.tapEvent);
|
|
}
|
|
|
|
public _addChildFromBuilder(name: string, value: any) {
|
|
this.actionView = value;
|
|
}
|
|
|
|
public _onVisibilityChanged(visibility: string) {
|
|
if (this.actionBar) {
|
|
this.actionBar.update();
|
|
}
|
|
}
|
|
|
|
public eachChild(callback: (child: ViewBase) => boolean) {
|
|
if (this._actionView) {
|
|
callback(this._actionView);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function isVisible(item: ActionItemDefinition) {
|
|
return item.visibility === "visible";
|
|
}
|
|
|
|
function onTitlePropertyChanged(actionBar: ActionBarBase, oldValue: string, newValue: string) {
|
|
actionBar._onTitlePropertyChanged();
|
|
}
|
|
|
|
export const titleProperty = new Property<ActionBarBase, string>({ name: "title", valueChanged: onTitlePropertyChanged });
|
|
titleProperty.register(ActionBarBase);
|
|
|
|
function onItemChanged(item: ActionItemBase, oldValue: string, newValue: string) {
|
|
if (item.actionBar) {
|
|
item.actionBar.update();
|
|
}
|
|
}
|
|
|
|
function onVisibilityChanged(item: ActionItemBase, oldValue: string, newValue: string) {
|
|
item._onVisibilityChanged(newValue);
|
|
}
|
|
|
|
export function traceMissingIcon(icon: string) {
|
|
traceWrite("Could not load action bar icon: " + icon,
|
|
traceCategories.Error,
|
|
traceMessageType.error);
|
|
}
|
|
|
|
function convertToContentInset(this: void, value: string | Length): [CssProperty<any, any>, any][] {
|
|
if (typeof value === "string" && value !== "auto") {
|
|
let insets = value.split(/[ ,]+/);
|
|
|
|
return [
|
|
[androidContentInsetLeftProperty, Length.parse(insets[0])],
|
|
[androidContentInsetRightProperty, Length.parse(insets[1] || insets[0])]
|
|
];
|
|
}
|
|
else {
|
|
return [
|
|
[androidContentInsetLeftProperty, value],
|
|
[androidContentInsetRightProperty, value]
|
|
];
|
|
}
|
|
}
|
|
|
|
export const iosIconRenderingModeProperty = new Property<ActionBarBase, "automatic" | "alwaysOriginal" | "alwaysTemplate">({ name: "iosIconRenderingMode", defaultValue: "alwaysOriginal" });
|
|
iosIconRenderingModeProperty.register(ActionBarBase);
|
|
|
|
export const textProperty = new Property<ActionItemBase, string>({ name: "text", defaultValue: "", valueChanged: onItemChanged });
|
|
textProperty.register(ActionItemBase);
|
|
|
|
export const iconProperty = new Property<ActionItemBase, string>({ name: "icon", valueChanged: onItemChanged });
|
|
iconProperty.register(ActionItemBase);
|
|
|
|
export const visibilityProperty = new Property({ name: "visibility", defaultValue: "visible", valueChanged: onVisibilityChanged });
|
|
visibilityProperty.register(ActionItemBase);
|
|
|
|
export const flatProperty = new Property<ActionBarBase, boolean>({ name: "flat", defaultValue: false, valueConverter: booleanConverter });
|
|
flatProperty.register(ActionBarBase);
|
|
|
|
const androidContentInsetProperty = new ShorthandProperty<Style, string | Length>({
|
|
name: "androidContentInset", cssName: "android-content-inset",
|
|
getter: function (this: Style) {
|
|
if (Length.equals(this.androidContentInsetLeft, this.androidContentInsetRight)) {
|
|
return this.androidContentInsetLeft;
|
|
}
|
|
|
|
return `${Length.convertToString(this.androidContentInsetLeft)} ${Length.convertToString(this.androidContentInsetRight)}`;
|
|
},
|
|
converter: convertToContentInset
|
|
});
|
|
androidContentInsetProperty.register(Style);
|
|
|
|
export const androidContentInsetLeftProperty = new CssProperty<Style, Length>({
|
|
name: "androidContentInsetLeft", cssName: "android-content-inset-left",
|
|
defaultValue: "auto", equalityComparer: Length.equals,
|
|
valueChanged: (target, oldValue, newValue) => {
|
|
const view = <ActionBarBase>target.viewRef.get();
|
|
if (view) {
|
|
view.effectiveContentInsetLeft = Length.toDevicePixels(newValue);
|
|
} else {
|
|
traceWrite(`${newValue} not set to view's property because ".viewRef" is cleared`, traceCategories.Style, traceMessageType.warn);
|
|
}
|
|
}, valueConverter: Length.parse
|
|
});
|
|
androidContentInsetLeftProperty.register(Style);
|
|
|
|
export const androidContentInsetRightProperty = new CssProperty<Style, Length>({
|
|
name: "androidContentInsetRight", cssName: "android-content-inset-right",
|
|
defaultValue: "auto", equalityComparer: Length.equals,
|
|
valueChanged: (target, oldValue, newValue) => {
|
|
const view = <ActionBarBase>target.viewRef.get();
|
|
if (view) {
|
|
view.effectiveContentInsetRight = Length.toDevicePixels(newValue);
|
|
} else {
|
|
traceWrite(`${newValue} not set to view's property because ".viewRef" is cleared`, traceCategories.Style, traceMessageType.warn);
|
|
}
|
|
}, valueConverter: Length.parse
|
|
});
|
|
androidContentInsetRightProperty.register(Style);
|