├── src ├── mixins │ ├── index.ts │ └── ti-global.ts ├── services │ ├── index.ts │ └── DeviceEnvironment.ts ├── facades │ ├── index.ts │ ├── picker │ │ ├── index.ts │ │ ├── DatePicker.ts │ │ └── TimePicker.ts │ └── dialog │ │ ├── DialogInterface.ts │ │ ├── PresetDialogInterface.ts │ │ ├── index.ts │ │ ├── AbstractDialog.ts │ │ ├── AlertDialog.ts │ │ ├── DialogAction.ts │ │ ├── ConfirmDialog.ts │ │ ├── AbstractPresetDialog.ts │ │ └── BaseDialog.ts ├── log │ ├── index.ts │ ├── LoggerInterface.ts │ └── Logger.ts ├── directives │ ├── layout │ │ ├── index.ts │ │ ├── vertical.ts │ │ └── horizontal.ts │ ├── tab-group │ │ ├── index.ts │ │ ├── tab.ts │ │ └── tab-group.ts │ ├── picker │ │ ├── index.ts │ │ ├── picker-row.ts │ │ ├── picker-column.ts │ │ └── picker.ts │ ├── table-view │ │ ├── index.ts │ │ ├── table-view-row.ts │ │ ├── table-view.ts │ │ └── table-view-section.ts │ ├── list-view │ │ ├── index.ts │ │ ├── ti-item-template.ts │ │ ├── list-item.ts │ │ └── list-section.ts │ ├── refresh-control.ts │ ├── toolbar.ts │ ├── scrollable-view.ts │ ├── index.ts │ ├── dialog.ts │ └── platform.ts ├── router │ ├── index.ts │ ├── directives │ │ ├── index.ts │ │ └── TitaniumRouterLinkDirective.ts │ ├── components │ │ └── EmptyOutlet.ts │ ├── NavigationOptions.ts │ ├── adapters │ │ └── ComponentAdapter.ts │ ├── TitaniumRouter.ts │ ├── TitaniumRouterModule.ts │ └── NavigationAwareRouteReuseStrategy.ts ├── renderer │ ├── index.ts │ └── TitaniumRendererFactory.ts ├── compiler │ ├── index.ts │ ├── FileSystemResourceLoader.ts │ └── TitaniumElementSchemaRegistry.ts ├── common │ ├── index.ts │ ├── DetachedLoaderComponent.ts │ ├── EmulatedPathLocationStrategy.ts │ ├── TitaniumPlatformLocation.ts │ └── HistoryStack.ts ├── core │ ├── TitaniumSanitizer.ts │ └── TitaniumPlatformRef.ts ├── forms │ ├── directives.ts │ ├── FormsModule.ts │ ├── directives │ │ └── NgModel.ts │ └── accessors │ │ └── ControlValueAccessor.ts ├── utility │ ├── number.ts │ └── string.ts ├── TitaniumErrorHandler.ts ├── TitaniumCommonModule.ts ├── platform │ ├── platform-titanium.ts │ ├── platform-titanium-dynamic.ts │ └── providers.ts ├── index.ts └── TitaniumModule.ts ├── .github ├── FUNDING.yml └── workflows │ ├── cla.yaml │ └── publish.yml ├── .npmignore ├── ti-angular-example ├── src │ ├── app │ │ ├── app.component.html │ │ ├── modules │ │ │ ├── controls │ │ │ │ ├── components │ │ │ │ │ ├── platform │ │ │ │ │ │ ├── platform.module.d.ts │ │ │ │ │ │ ├── routes.d.ts │ │ │ │ │ │ ├── platform.component.html │ │ │ │ │ │ ├── ios │ │ │ │ │ │ │ ├── stepper │ │ │ │ │ │ │ │ ├── stepper.component.ts │ │ │ │ │ │ │ │ └── stepper.component.html │ │ │ │ │ │ │ ├── live-photo │ │ │ │ │ │ │ │ ├── live-photo.component.html │ │ │ │ │ │ │ │ └── live-photo.component.ts │ │ │ │ │ │ │ ├── button-bar │ │ │ │ │ │ │ │ ├── button-bar.component.ts │ │ │ │ │ │ │ │ └── button-bar.component.html │ │ │ │ │ │ │ └── blur-view │ │ │ │ │ │ │ │ ├── blur-view.component.html │ │ │ │ │ │ │ │ └── blur-view.component.ts │ │ │ │ │ │ ├── android │ │ │ │ │ │ │ ├── card-view │ │ │ │ │ │ │ │ ├── card-view.component.ts │ │ │ │ │ │ │ │ └── card-view.component.html │ │ │ │ │ │ │ ├── search-view │ │ │ │ │ │ │ │ ├── search-view.component.html │ │ │ │ │ │ │ │ └── search-view.component.ts │ │ │ │ │ │ │ └── progress-indicator │ │ │ │ │ │ │ │ ├── progress-indicator.component.html │ │ │ │ │ │ │ │ └── progress-indicator.component.ts │ │ │ │ │ │ ├── routes.ios.ts │ │ │ │ │ │ ├── routes.android.ts │ │ │ │ │ │ ├── platform-routing.module.ts │ │ │ │ │ │ ├── platform.module.ios.ts │ │ │ │ │ │ ├── platform.module.android.ts │ │ │ │ │ │ └── platform.component.ts │ │ │ │ │ ├── views │ │ │ │ │ │ ├── views.component.html │ │ │ │ │ │ ├── image-view │ │ │ │ │ │ │ ├── image-view.component.html │ │ │ │ │ │ │ └── image-view.component.ts │ │ │ │ │ │ ├── view │ │ │ │ │ │ │ ├── view.component.ts │ │ │ │ │ │ │ └── view.component.html │ │ │ │ │ │ ├── web-view │ │ │ │ │ │ │ ├── web-view.component.html │ │ │ │ │ │ │ └── web-view.component.ts │ │ │ │ │ │ ├── list-view │ │ │ │ │ │ │ ├── item.component.ts │ │ │ │ │ │ │ ├── list-view.component.html │ │ │ │ │ │ │ └── list-view.component.ts │ │ │ │ │ │ ├── scrollable-view │ │ │ │ │ │ │ ├── scrollable-view.component.ts │ │ │ │ │ │ │ └── scrollable-view.component.html │ │ │ │ │ │ ├── table-view │ │ │ │ │ │ │ ├── table-view.component.ts │ │ │ │ │ │ │ └── table-view.component.html │ │ │ │ │ │ ├── views.component.ts │ │ │ │ │ │ └── scroll-view │ │ │ │ │ │ │ ├── scroll-view.component.ts │ │ │ │ │ │ │ └── scroll-view.component.html │ │ │ │ │ ├── utility │ │ │ │ │ │ ├── button-bar │ │ │ │ │ │ │ ├── button-bar.component.ts │ │ │ │ │ │ │ └── button-bar.component.html │ │ │ │ │ │ ├── search-bar │ │ │ │ │ │ │ ├── search-bar.component.html │ │ │ │ │ │ │ └── search-bar.component.ts │ │ │ │ │ │ ├── progress-indicators │ │ │ │ │ │ │ ├── format-bytes.pipe.ts │ │ │ │ │ │ │ ├── progress-indicators.component.ts │ │ │ │ │ │ │ └── progress-indicators.component.html │ │ │ │ │ │ ├── toolbar │ │ │ │ │ │ │ ├── toolbar.component.html │ │ │ │ │ │ │ └── toolbar.component.ts │ │ │ │ │ │ ├── refresh-control │ │ │ │ │ │ │ ├── refresh-control.component.ts │ │ │ │ │ │ │ └── refresh-control.component.html │ │ │ │ │ │ ├── utility-views.component.ts │ │ │ │ │ │ ├── utility.module.ts │ │ │ │ │ │ ├── masked-image │ │ │ │ │ │ │ ├── masked-image.component.html │ │ │ │ │ │ │ └── masked-image.component.ts │ │ │ │ │ │ └── utility-routing.module.ts │ │ │ │ │ ├── input │ │ │ │ │ │ ├── input-demo.component.ts │ │ │ │ │ │ ├── input.module.ts │ │ │ │ │ │ ├── input-demo.component.html │ │ │ │ │ │ ├── inputs.component.ts │ │ │ │ │ │ └── inputs.component.html │ │ │ │ │ └── dialogs │ │ │ │ │ │ ├── dialogs.component.html │ │ │ │ │ │ └── dialogs.component.ts │ │ │ │ ├── controls.component.html │ │ │ │ ├── controls.component.ts │ │ │ │ ├── controls.module.ts │ │ │ │ └── controls-routing.module.ts │ │ │ ├── home │ │ │ │ ├── home.component.html │ │ │ │ ├── home.component.ts │ │ │ │ └── home.module.ts │ │ │ ├── phone │ │ │ │ ├── components │ │ │ │ │ └── camera │ │ │ │ │ │ ├── camera.component.ts │ │ │ │ │ │ └── camera.component.html │ │ │ │ ├── phone.component.ts │ │ │ │ ├── phone.component.html │ │ │ │ ├── phone-routing.module.ts │ │ │ │ └── phone.module.ts │ │ │ └── intro │ │ │ │ ├── intro.module.ts │ │ │ │ ├── intro-routing.module.ts │ │ │ │ ├── services │ │ │ │ └── intro-guard.service.ts │ │ │ │ ├── intro.component.ts │ │ │ │ └── intro.component.html │ │ ├── app.component.ts │ │ ├── app-routing.module.ts │ │ └── app.module.ts │ ├── shared │ │ ├── services │ │ │ └── console-logger.service.ts │ │ ├── components │ │ │ ├── window.component.html │ │ │ ├── window.component.ts │ │ │ ├── button.component.ts │ │ │ ├── label.component.ts │ │ │ ├── nav-table.component.html │ │ │ ├── alert.component.html │ │ │ ├── nav-table.component.ts │ │ │ ├── alert.component.ts │ │ │ └── icon.component.ts │ │ └── shared.module.ts │ ├── assets │ │ ├── android │ │ │ ├── appicon.png │ │ │ └── default.9.png │ │ ├── images │ │ │ ├── angular.png │ │ │ ├── icons │ │ │ │ ├── tab.png │ │ │ │ ├── phone.png │ │ │ │ ├── console.png │ │ │ │ ├── controls.png │ │ │ │ ├── mashups.png │ │ │ │ ├── phone@2x.png │ │ │ │ ├── phone@3x.png │ │ │ │ ├── tab@2x.png │ │ │ │ ├── tab@3x.png │ │ │ │ ├── console@2x.png │ │ │ │ ├── console@3x.png │ │ │ │ ├── mashups@2x.png │ │ │ │ ├── mashups@3x.png │ │ │ │ ├── controls@2x.png │ │ │ │ ├── controls@3x.png │ │ │ │ ├── shortcutItemIcon.png │ │ │ │ ├── shortcutItemIcon@2x.png │ │ │ │ └── shortcutItemIcon@3x.png │ │ │ ├── transparent.png │ │ │ └── titanium-logo.png │ │ └── fonts │ │ │ ├── FontAwesome5FreeSolid.otf │ │ │ ├── FontAwesome5BrandsRegular.otf │ │ │ └── FontAwesome5FreeRegular.otf │ ├── polyfills.ts │ └── main.ts ├── DefaultIcon.png ├── iTunesConnect.png ├── MarketplaceArtwork.png ├── platform │ └── android │ │ └── res │ │ ├── drawable-hdpi │ │ └── baseline_search_white_48.png │ │ ├── drawable-mdpi │ │ └── baseline_search_white_48.png │ │ ├── drawable-xhdpi │ │ └── baseline_search_white_48.png │ │ ├── drawable-xxhdpi │ │ └── baseline_search_white_48.png │ │ ├── drawable-xxxhdpi │ │ └── baseline_search_white_48.png │ │ ├── values │ │ ├── colors.xml │ │ └── styles.xml │ │ └── drawable │ │ └── baseline_search_24.xml ├── .gitignore ├── typings │ └── node │ │ └── index.d.ts ├── README.md ├── tsconfig.json ├── package.json └── tiapp.xml ├── assets └── titanium-angular.png ├── Jenkinsfile ├── typings └── node │ └── index.d.ts ├── .gitignore ├── CHANGELOG.md ├── tsconfig.json ├── README.md └── package.json /src/mixins/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ti-global'; 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: tidev 2 | liberapay: tidev 3 | -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DeviceEnvironment'; 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | assets/ 2 | src/ 3 | test/ 4 | ti-angular-example/ 5 | tsconfig.json -------------------------------------------------------------------------------- /src/facades/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dialog'; 2 | export * from './picker'; -------------------------------------------------------------------------------- /src/log/index.ts: -------------------------------------------------------------------------------- 1 | export * from './LoggerInterface'; 2 | export * from './Logger'; -------------------------------------------------------------------------------- /ti-angular-example/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/directives/layout/index.ts: -------------------------------------------------------------------------------- 1 | export * from './horizontal'; 2 | export * from './vertical'; -------------------------------------------------------------------------------- /src/facades/picker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DatePicker'; 2 | export * from './TimePicker'; -------------------------------------------------------------------------------- /src/directives/tab-group/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from './tab'; 3 | export * from './tab-group'; 4 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TitaniumRouter'; 2 | export * from './TitaniumRouterModule'; -------------------------------------------------------------------------------- /src/renderer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TitaniumRenderer'; 2 | export * from './TitaniumRendererFactory'; -------------------------------------------------------------------------------- /assets/titanium-angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/assets/titanium-angular.png -------------------------------------------------------------------------------- /ti-angular-example/src/shared/services/console-logger.service.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ConsoleLogger { 3 | 4 | } -------------------------------------------------------------------------------- /src/compiler/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TitaniumElementSchemaRegistry' 2 | export * from './FileSystemResourceLoader'; -------------------------------------------------------------------------------- /ti-angular-example/DefaultIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/DefaultIcon.png -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/platform.module.d.ts: -------------------------------------------------------------------------------- 1 | export declare class PlatformModule {} 2 | -------------------------------------------------------------------------------- /src/directives/picker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './picker'; 2 | export * from './picker-column'; 3 | export * from './picker-row'; 4 | -------------------------------------------------------------------------------- /src/router/directives/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TitaniumRouterLinkDirective'; 2 | export * from './TitaniumRouterOutletDirective'; -------------------------------------------------------------------------------- /ti-angular-example/iTunesConnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/iTunesConnect.png -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | 2 | library 'pipeline-library' 3 | 4 | buildNPMPackage { 5 | projectKey = 'TIMOB' 6 | updateJIRATickets = false 7 | } 8 | -------------------------------------------------------------------------------- /src/facades/dialog/DialogInterface.ts: -------------------------------------------------------------------------------- 1 | export interface DialogInterface { 2 | addAction(DialogAction); 3 | show(options: any): void 4 | } -------------------------------------------------------------------------------- /ti-angular-example/MarketplaceArtwork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/MarketplaceArtwork.png -------------------------------------------------------------------------------- /src/directives/table-view/index.ts: -------------------------------------------------------------------------------- 1 | export * from './table-view-row'; 2 | export * from './table-view-section'; 3 | export * from './table-view'; 4 | -------------------------------------------------------------------------------- /ti-angular-example/src/assets/android/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/android/appicon.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/angular.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/tab.png -------------------------------------------------------------------------------- /typings/node/index.d.ts: -------------------------------------------------------------------------------- 1 | // empty NodeJS.Global definition to make zone.js typings happy 2 | declare namespace NodeJS { 3 | interface Global { 4 | } 5 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/routes.d.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from "@angular/router"; 2 | 3 | export const platformRoutes: Routes; -------------------------------------------------------------------------------- /ti-angular-example/src/assets/android/default.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/android/default.9.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/phone.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/transparent.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/console.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/controls.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/mashups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/mashups.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/phone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/phone@2x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/phone@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/phone@3x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/tab@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/tab@2x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/tab@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/tab@3x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/titanium-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/titanium-logo.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/console@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/console@2x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/console@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/console@3x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/mashups@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/mashups@2x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/mashups@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/mashups@3x.png -------------------------------------------------------------------------------- /src/directives/list-view/index.ts: -------------------------------------------------------------------------------- 1 | export * from './list-item'; 2 | export * from './list-section'; 3 | export * from './list-view'; 4 | export * from './ti-item-template'; 5 | -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/controls@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/controls@2x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/controls@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/controls@3x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/fonts/FontAwesome5FreeSolid.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/fonts/FontAwesome5FreeSolid.otf -------------------------------------------------------------------------------- /ti-angular-example/src/assets/fonts/FontAwesome5BrandsRegular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/fonts/FontAwesome5BrandsRegular.otf -------------------------------------------------------------------------------- /ti-angular-example/src/assets/fonts/FontAwesome5FreeRegular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/fonts/FontAwesome5FreeRegular.otf -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/shortcutItemIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/shortcutItemIcon.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/shortcutItemIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/shortcutItemIcon@2x.png -------------------------------------------------------------------------------- /ti-angular-example/src/assets/images/icons/shortcutItemIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/src/assets/images/icons/shortcutItemIcon@3x.png -------------------------------------------------------------------------------- /src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DetachedLoaderComponent'; 2 | export * from './EmulatedPathLocationStrategy'; 3 | export * from './HistoryStack'; 4 | export * from './TitaniumPlatformLocation'; -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/views.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/router/components/EmptyOutlet.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ template: '' }) 4 | export class EmptyOutletComponent { 5 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/platform.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/image-view/image-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/drawable-hdpi/baseline_search_white_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/platform/android/res/drawable-hdpi/baseline_search_white_48.png -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/drawable-mdpi/baseline_search_white_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/platform/android/res/drawable-mdpi/baseline_search_white_48.png -------------------------------------------------------------------------------- /ti-angular-example/.gitignore: -------------------------------------------------------------------------------- 1 | # Build dirs and files 2 | /build 3 | /Resources 4 | 5 | # OS files 6 | .DS_Store 7 | Thumbs.db 8 | 9 | # Angular AoT generated files 10 | **/*.ngfactory.ts 11 | **/*.ngsummary.json 12 | -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/drawable-xhdpi/baseline_search_white_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/platform/android/res/drawable-xhdpi/baseline_search_white_48.png -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/drawable-xxhdpi/baseline_search_white_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/platform/android/res/drawable-xxhdpi/baseline_search_white_48.png -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/drawable-xxxhdpi/baseline_search_white_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tidev/titanium-angular/HEAD/ti-angular-example/platform/android/res/drawable-xxxhdpi/baseline_search_white_48.png -------------------------------------------------------------------------------- /ti-angular-example/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | */ 5 | 6 | import 'core-js/es7/reflect'; -------------------------------------------------------------------------------- /src/core/TitaniumSanitizer.ts: -------------------------------------------------------------------------------- 1 | import { Sanitizer } from '@angular/core'; 2 | 3 | export class TitaniumSanitizer extends Sanitizer { 4 | sanitize(_context: any, value: string): string { 5 | return value; 6 | } 7 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: "ti-app", 5 | templateUrl: "./app.component.html" 6 | }) 7 | export class AppComponent { 8 | 9 | } -------------------------------------------------------------------------------- /ti-angular-example/src/main.ts: -------------------------------------------------------------------------------- 1 | import './polyfills'; 2 | import { platformTitaniumDynamic } from 'titanium-angular'; 3 | import { AppModule } from './app/app.module'; 4 | 5 | platformTitaniumDynamic() 6 | .bootstrapModule(AppModule); -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/view/view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | 3 | @Component({ 4 | templateUrl: './view.component.html' 5 | }) 6 | export class ViewComponent { 7 | 8 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/home/home.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/phone/components/camera/camera.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | 3 | @Component({ 4 | templateUrl: './camera.component.html' 5 | }) 6 | export class CameraComponent { 7 | 8 | } -------------------------------------------------------------------------------- /ti-angular-example/typings/node/index.d.ts: -------------------------------------------------------------------------------- 1 | // Alias NodeJS.Global definition to make zone.js typings happy 2 | // including @types/node clashes with @types/titanium 3 | declare namespace NodeJS { 4 | type Global = Titanium.Global 5 | } 6 | -------------------------------------------------------------------------------- /src/facades/dialog/PresetDialogInterface.ts: -------------------------------------------------------------------------------- 1 | export interface PresetDialogOptions { 2 | title: string; 3 | message: string; 4 | } 5 | 6 | export interface PresetDialogInterface { 7 | show(): Promise; 8 | 9 | 10 | } -------------------------------------------------------------------------------- /src/router/NavigationOptions.ts: -------------------------------------------------------------------------------- 1 | import { NavigationTransition } from 'titanium-navigator'; 2 | 3 | export interface NavigationOptions { 4 | clearHistory?: boolean; 5 | animated?: boolean; 6 | transition?: NavigationTransition; 7 | } -------------------------------------------------------------------------------- /src/log/LoggerInterface.ts: -------------------------------------------------------------------------------- 1 | export interface LoggerInterface { 2 | info(...message: any): void; 3 | debug(...message: any): void; 4 | trace(...message: any): void; 5 | warn(...message: any): void; 6 | error(...message: any): void; 7 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/phone/phone.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'PhoneTab', 5 | templateUrl: 'phone.component.html' 6 | }) 7 | export class PhoneComponent { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/image-view/image-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | 3 | @Component({ 4 | templateUrl: './image-view.component.html' 5 | }) 6 | export class ImageViewComponent { 7 | 8 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/controls.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/stepper/stepper.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: './stepper.component.html' 5 | }) 6 | export class StepperComponent { 7 | value = 0 8 | } 9 | -------------------------------------------------------------------------------- /src/forms/directives.ts: -------------------------------------------------------------------------------- 1 | import { DefaultValueAccessor } from './accessors/DefaultValueAccessor'; 2 | import { NgModel } from './directives/NgModel'; 3 | 4 | export const TEMPLATE_DRIVEN_DIRECTIVES = [NgModel]; 5 | 6 | export const SHARED_FORM_DIRECTIVES = [DefaultValueAccessor]; -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/window.component.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/utility/number.ts: -------------------------------------------------------------------------------- 1 | export function isNumeric(value: any) { 2 | return !isNaN(value); 3 | } 4 | 5 | export function toNumber(value: any) { 6 | if (Number.isSafeInteger(value)) { 7 | return Number.parseInt(value, 10); 8 | } else { 9 | return Number.parseFloat(value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/cla.yaml: -------------------------------------------------------------------------------- 1 | name: Check CLA 2 | on: 3 | - pull_request 4 | 5 | jobs: 6 | check-cla: 7 | runs-on: ubuntu-latest 8 | name: Verify contributor 9 | 10 | steps: 11 | - uses: tidev/tidev-cla-action@v1 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | ViewChild 4 | } from '@angular/core'; 5 | 6 | @Component({ 7 | templateUrl: "./home.component.html" 8 | }) 9 | export class HomeComponent { 10 | logClick() { 11 | console.log('clicked') 12 | } 13 | } -------------------------------------------------------------------------------- /src/directives/layout/vertical.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'vertical-layout,VerticalLayout', 5 | template: ` 6 | 7 | 8 | 9 | ` 10 | }) 11 | export class VerticalLayoutComponent {} 12 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/live-photo/live-photo.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Select 4 | -------------------------------------------------------------------------------- /src/facades/dialog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AbstractDialog'; 2 | export * from './AbstractPresetDialog'; 3 | export * from './AlertDialog'; 4 | export * from './BaseDialog'; 5 | export * from './ConfirmDialog'; 6 | export * from './DialogAction'; 7 | export * from './DialogInterface'; 8 | export * from './PresetDialogInterface'; -------------------------------------------------------------------------------- /src/utility/string.ts: -------------------------------------------------------------------------------- 1 | export function camelize(value: string): string { 2 | return value.replace(/-(\w)/g, (match, firstSubMatch) => firstSubMatch ? firstSubMatch.toUpperCase() : ''); 3 | } 4 | 5 | export function capitalizeFirstLetter(value: string): string { 6 | return value.charAt(0).toUpperCase() + value.slice(1); 7 | } -------------------------------------------------------------------------------- /src/directives/layout/horizontal.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'horizontal-layout,HorizontalLayout', 5 | template: ` 6 | 7 | 8 | 9 | ` 10 | }) 11 | export class HorizontalLayoutComponent {} 12 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/phone/components/camera/camera.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/forms/FormsModule.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | 3 | import { TEMPLATE_DRIVEN_DIRECTIVES, SHARED_FORM_DIRECTIVES } from './directives' 4 | 5 | @NgModule({ 6 | declarations: [TEMPLATE_DRIVEN_DIRECTIVES, SHARED_FORM_DIRECTIVES], 7 | exports: [TEMPLATE_DRIVEN_DIRECTIVES] 8 | }) 9 | export class FormModule { 10 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/web-view/web-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/window.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, Input, ViewChild } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'base-window', 5 | templateUrl: 'window.component.html' 6 | }) 7 | export class BaseWindow { 8 | @Input() title: string 9 | @ViewChild('win') winRef: ElementRef 10 | } 11 | -------------------------------------------------------------------------------- /src/directives/picker/picker-row.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: 'picker-row,PickerRow' 5 | }) 6 | export class PickerRowDirective { 7 | pickerRow: Titanium.UI.PickerRow; 8 | 9 | constructor(el: ElementRef) { 10 | this.pickerRow = el.nativeElement.titaniumView; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/button-bar/button-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | @Component({ 3 | templateUrl: './button-bar.component.html' 4 | }) 5 | export class ButtonBarComponent { 6 | doSomething(e) { 7 | Ti.API.info(`Clicked button with index ${e.index}`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/mixins/ti-global.ts: -------------------------------------------------------------------------------- 1 | interface TiGlobal { 2 | /** 3 | * Titanium global 4 | */ 5 | Ti: typeof Ti 6 | } 7 | 8 | type Constructor = new(...args: any[]) => T; 9 | 10 | export function WithTiGlobal>(Base: T = (class {} as any)) { 11 | return class extends Base implements TiGlobal { 12 | Ti = Ti 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/button-bar/button-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: './button-bar.component.html' 5 | }) 6 | export class ButtonBarComponent { 7 | doSomething(e) { 8 | Ti.API.info(`Clicked button with index ${e.index}`); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/android/card-view/card-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | 4 | @Component({ 5 | templateUrl: './card-view.component.html' 6 | }) 7 | export class CardViewComponent extends WithTiGlobal() { 8 | items = new Array(10) 9 | } 10 | -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #1976d2 4 | #004ba0 5 | #63a4ff 6 | #de0032 7 | #fafafa 8 | -------------------------------------------------------------------------------- /src/directives/refresh-control.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: 'refresh-control,RefreshControl' 5 | }) 6 | export class RefreshControlDirective { 7 | 8 | public refreshControl: Titanium.UI.RefreshControl; 9 | 10 | constructor(el: ElementRef) { 11 | this.refreshControl = el.nativeElement.titaniumView; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/blur-view/blur-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/android/search-view/search-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Apple 4 | Banana 5 | Orange 6 | Raspberry 7 | 8 | 9 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/stepper/stepper.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/TitaniumErrorHandler.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ErrorHandler 3 | } from '@angular/core'; 4 | 5 | import { 6 | Logger 7 | } from './log' 8 | 9 | export class TitaniumErrorHandler extends ErrorHandler { 10 | 11 | private logger: Logger; 12 | 13 | constructor(logger: Logger) { 14 | super(); 15 | this.logger = logger; 16 | } 17 | 18 | handleError(error: any): void { 19 | this.logger.error(error); 20 | } 21 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/view/view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/android/progress-indicator/progress-indicator.component.html: -------------------------------------------------------------------------------- 1 | 2 | Load 3 | 4 | 11 | -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/button.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'base-button', 5 | template: ` 6 | 15 | ` 16 | }) 17 | export class BaseButton {} 18 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/input/input-demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | 4 | @Component({ 5 | selector: 'input-demo', 6 | templateUrl: 'input-demo.component.html' 7 | }) 8 | export class InputDemo extends WithTiGlobal() { 9 | @Input() name: string 10 | @Input() hint = '' 11 | 12 | state = '' 13 | 14 | get info() { 15 | return this.state.length ? this.state : this.hint; 16 | } 17 | } -------------------------------------------------------------------------------- /src/TitaniumCommonModule.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 3 | 4 | import { TITANIUM_DIRECTIVES } from './directives'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | ...TITANIUM_DIRECTIVES 9 | ], 10 | imports: [ 11 | CommonModule 12 | ], 13 | exports: [ 14 | CommonModule, 15 | ...TITANIUM_DIRECTIVES 16 | ], 17 | schemas: [NO_ERRORS_SCHEMA] 18 | }) 19 | export class TitaniumCommonModule { 20 | 21 | } -------------------------------------------------------------------------------- /src/compiler/FileSystemResourceLoader.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { ResourceLoader } from "@angular/compiler"; 3 | 4 | @Injectable() 5 | export class FileSystemResourceLoader extends ResourceLoader { 6 | 7 | get(url: string): Promise { 8 | var file = Ti.Filesystem.getFile(url); 9 | if (!file.exists()) { 10 | throw new Error(`Could not find file ${url}`); 11 | } 12 | var blob = file.read(); 13 | return Promise.resolve(blob.text); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/search-bar/search-bar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /src/platform/platform-titanium.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createPlatformFactory, 3 | platformCore, 4 | PlatformRef, 5 | StaticProvider 6 | } from "@angular/core"; 7 | 8 | import { TitaniumPlatformRef } from '../core/TitaniumPlatformRef'; 9 | import { COMMON_PROVIDERS } from './providers'; 10 | 11 | export const _platformTitanium = createPlatformFactory( 12 | platformCore, 13 | 'titanium', 14 | [...COMMON_PROVIDERS] 15 | ); 16 | 17 | export function platformTitanium(): PlatformRef { 18 | return new TitaniumPlatformRef(_platformTitanium()); 19 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/phone/phone.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | Not implemented yet 10 | Check back later for even more examples. 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './directives'; 2 | export * from './facades'; 3 | export * from './log'; 4 | export * from './mixins' 5 | export * from './platform/platform-titanium'; 6 | export * from './platform/platform-titanium-dynamic'; 7 | export * from './renderer'; 8 | 9 | export { TitaniumRouter } from './router/TitaniumRouter' 10 | export { TitaniumRouterModule } from './router/TitaniumRouterModule'; 11 | 12 | export * from './services'; 13 | 14 | export { TitaniumCommonModule } from './TitaniumCommonModule'; 15 | export { TitaniumModule } from './TitaniumModule'; 16 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/routes.ios.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { BlurViewComponent } from './ios/blur-view/blur-view.component'; 4 | import { LivePhotoComponent } from './ios/live-photo/live-photo.component'; 5 | import { StepperComponent } from './ios/stepper/stepper.component'; 6 | 7 | export const platformRoutes: Routes = [ 8 | { path: 'blur-view', component: BlurViewComponent }, 9 | { path: 'live-photo', component: LivePhotoComponent }, 10 | { path: 'stepper', component: StepperComponent } 11 | ] 12 | -------------------------------------------------------------------------------- /src/facades/dialog/AbstractDialog.ts: -------------------------------------------------------------------------------- 1 | import { DialogAction } from './DialogAction'; 2 | import { DialogInterface } from './DialogInterface'; 3 | 4 | export abstract class AbstractDialog implements DialogInterface { 5 | protected _actions: DialogAction[] = []; 6 | 7 | get actions() { 8 | return this._actions; 9 | } 10 | 11 | set actions(actions: DialogAction[]) { 12 | this._actions = actions; 13 | } 14 | 15 | addAction(action: DialogAction): void { 16 | this._actions.push(action); 17 | } 18 | 19 | abstract show(): void; 20 | 21 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/input/input.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | 3 | import { InputDemo } from './input-demo.component'; 4 | import { InputsComponent } from './inputs.component'; 5 | import { SharedModule } from '@/shared/shared.module'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | SharedModule, 10 | ], 11 | declarations: [ 12 | InputDemo, 13 | InputsComponent 14 | ], 15 | exports: [ 16 | InputsComponent 17 | ], 18 | schemas: [NO_ERRORS_SCHEMA] 19 | }) 20 | export class InputModule { 21 | 22 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/intro/intro.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | 3 | import { IntroComponent } from './intro.component'; 4 | import { IntroRoutingModule } from './intro-routing.module'; 5 | import { SharedModule } from '@/shared/shared.module'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | SharedModule, 10 | IntroRoutingModule 11 | ], 12 | declarations: [ 13 | IntroComponent 14 | ], 15 | exports: [ 16 | IntroComponent 17 | ], 18 | schemas: [NO_ERRORS_SCHEMA] 19 | }) 20 | export class IntroModule { } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/progress-indicators/format-bytes.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'formatBytes' 5 | }) 6 | export class FormatBytesPipe implements PipeTransform { 7 | private k = 1024; 8 | 9 | private sizes = ['Bytes', 'KB', 'MB']; 10 | 11 | transform(bytes: number): any { 12 | if(bytes === 0) { 13 | return '0 Bytes'; 14 | } 15 | 16 | const i = Math.floor(Math.log(bytes) / Math.log(this.k)); 17 | 18 | return parseFloat((bytes / Math.pow(this.k, i)).toFixed(2)) + ' ' + this.sizes[i]; 19 | } 20 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | dist 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Compiled binary addons (http://nodejs.org/api/addons.html) 12 | build/Release 13 | 14 | # Dependency directories 15 | node_modules/ 16 | 17 | # Optional npm cache directory 18 | .npm 19 | 20 | # Optional eslint cache 21 | .eslintcache 22 | 23 | # Optional REPL history 24 | .node_repl_history 25 | 26 | # Output of 'npm pack' 27 | *.tgz 28 | 29 | # Yarn Integrity file 30 | .yarn-integrity 31 | 32 | # dotenv environment variables file 33 | .env 34 | 35 | # Other dotfiles 36 | .DS_Store 37 | -------------------------------------------------------------------------------- /ti-angular-example/README.md: -------------------------------------------------------------------------------- 1 | # Titanium Angular example app 2 | 3 | This sample app for Titanium Angular contains tons of examples how to use Titanium UI elements, data-binding and navigate through the app using routing. It also demonstrates how you can use shared components to easily style common UI elements and re-use them throughout your app. 4 | 5 | ## Getting started 6 | 7 | After cloning the repository, just install the required dependencies 8 | 9 | ```sh 10 | npm i 11 | ``` 12 | 13 | and then build and run 14 | 15 | ```sh 16 | # appc-cli 17 | appc run -p [android | ios] 18 | 19 | # ti-cli 20 | ti build -p [android | ios] 21 | ``` 22 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/routes.android.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { CardViewComponent } from './android/card-view/card-view.component'; 4 | import { ProgressIndicatorComponent } from './android/progress-indicator/progress-indicator.component'; 5 | import { SearchViewComponent } from './android/search-view/search-view.component'; 6 | 7 | export const platformRoutes: Routes = [ 8 | { path: 'card-view', component: CardViewComponent }, 9 | { path: 'progress-indicator', component: ProgressIndicatorComponent }, 10 | { path: 'search-view', component: SearchViewComponent } 11 | ] 12 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/button-bar/button-bar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /ti-angular-example/platform/android/res/drawable/baseline_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/log/Logger.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { LoggerInterface } from './LoggerInterface' 4 | 5 | @Injectable() 6 | export class Logger implements LoggerInterface { 7 | info(...message: any[]): void { 8 | console.log(...message); 9 | } 10 | 11 | debug(...message: any[]): void { 12 | console.debug(...message); 13 | } 14 | 15 | trace(...message: any[]): void { 16 | console.trace(...message); 17 | } 18 | 19 | warn(...message: any[]): void { 20 | console.warn(...message); 21 | } 22 | 23 | error(...message: any[]): void { 24 | console.error(...message); 25 | } 26 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { TitaniumCommonModule } from 'titanium-angular'; 3 | 4 | import { HomeComponent } from './home.component'; 5 | import { ControlsModule } from '../controls/controls.module'; 6 | import { PhoneModule } from '../phone/phone.module'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | TitaniumCommonModule, 11 | ControlsModule, 12 | PhoneModule 13 | ], 14 | declarations: [ 15 | HomeComponent 16 | ], 17 | exports: [ 18 | HomeComponent 19 | ], 20 | schemas: [NO_ERRORS_SCHEMA] 21 | }) 22 | export class HomeModule { } -------------------------------------------------------------------------------- /src/platform/platform-titanium-dynamic.ts: -------------------------------------------------------------------------------- 1 | import { createPlatformFactory, PlatformRef } from '@angular/core'; 2 | import { ɵplatformCoreDynamic as platformCoreDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { TitaniumPlatformRef } from '../core/TitaniumPlatformRef'; 5 | import { COMMON_PROVIDERS, TITANIUM_COMPILER_PROVIDERS } from './providers'; 6 | 7 | export const _platformTitaniumDynamic = createPlatformFactory( 8 | platformCoreDynamic, 9 | 'titaniumDynamic', 10 | [...COMMON_PROVIDERS, ...TITANIUM_COMPILER_PROVIDERS] 11 | ); 12 | 13 | export function platformTitaniumDynamic(): PlatformRef { 14 | return new TitaniumPlatformRef(_platformTitaniumDynamic()); 15 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/toolbar/toolbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Oh look, it's down there 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/toolbar/toolbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Logger, TitaniumRouter, WithTiGlobal } from 'titanium-angular'; 3 | 4 | @Component({ 5 | templateUrl: './toolbar.component.html' 6 | }) 7 | export class ToolbarComponent extends WithTiGlobal() { 8 | constructor(private logger: Logger, private router: TitaniumRouter) { 9 | super(); 10 | } 11 | 12 | send(event) { 13 | this.logger.info('Pressed "Send" button!'); 14 | } 15 | 16 | openCamera(event) { 17 | this.logger.info('Pressed "Camera" button!'); 18 | } 19 | 20 | cancel(event) { 21 | this.router.back(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/label.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'base-label', 5 | template: `` 6 | }) 7 | export class BaseLabel { 8 | @Input() color = '#444'; 9 | 10 | @Input() fontFamily?: string; 11 | 12 | @Input() fontSize?: string; 13 | 14 | @Input() fontStyle?: string; 15 | 16 | @Input() fontWeight?: string; 17 | 18 | get font(): Font { 19 | return { 20 | fontFamily: this.fontFamily, 21 | fontSize: this.fontSize, 22 | fontStyle: this.fontStyle, 23 | fontWeight: this.fontWeight 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/phone/phone-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes } from '@angular/router'; 3 | import { TitaniumRouterModule } from 'titanium-angular'; 4 | 5 | import { CameraComponent } from './components/camera/camera.component'; 6 | 7 | const phoneRoutes: Routes = [ 8 | { 9 | path: 'phone', 10 | children: [ 11 | { path: 'camera', component: CameraComponent }, 12 | ] 13 | } 14 | ]; 15 | 16 | @NgModule({ 17 | imports: [ 18 | TitaniumRouterModule.forChild(phoneRoutes) 19 | ], 20 | exports: [ 21 | TitaniumRouterModule 22 | ] 23 | }) 24 | export class PhoneRoutingModule { } -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/nav-table.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | {{ item.title }} 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/android/card-view/card-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 15 | Card title 16 | 17 | 18 | Secondary text 19 | 20 | 21 | Greyhound divisively hello coldly wonderfully marginally far upon excluding. 22 | 23 | 24 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes } from '@angular/router'; 3 | import { TitaniumRouterModule } from 'titanium-angular'; 4 | 5 | import { HomeComponent } from './modules/home/home.component'; 6 | import { IntroGuard } from './modules/intro/services/intro-guard.service'; 7 | 8 | const appRoutes: Routes = [ 9 | { path: '', redirectTo: '/home', pathMatch: 'full' }, 10 | { path: 'home', component: HomeComponent, canActivate: [IntroGuard] } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [TitaniumRouterModule.forRoot(appRoutes, { enableTracing: false })], 15 | exports: [TitaniumRouterModule] 16 | }) 17 | export class AppRoutingModule { 18 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { TitaniumModule } from 'titanium-angular'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { HomeModule } from './modules/home/home.module'; 7 | import { IntroModule } from './modules/intro/intro.module'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | TitaniumModule, 12 | HomeModule, 13 | IntroModule, 14 | AppRoutingModule 15 | ], 16 | declarations: [ 17 | AppComponent 18 | ], 19 | bootstrap: [AppComponent], 20 | schemas: [NO_ERRORS_SCHEMA] 21 | }) 22 | export class AppModule { 23 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/intro/intro-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes } from '@angular/router'; 3 | import { TitaniumRouterModule } from 'titanium-angular'; 4 | import { IntroComponent } from './intro.component'; 5 | import { IntroGuard } from './services/intro-guard.service'; 6 | 7 | const introRoutes: Routes = [ 8 | { 9 | path: 'intro', 10 | component: IntroComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | TitaniumRouterModule.forChild(introRoutes) 17 | ], 18 | exports: [ 19 | TitaniumRouterModule 20 | ], 21 | providers: [ 22 | IntroGuard 23 | ] 24 | }) 25 | export class IntroRoutingModule { } -------------------------------------------------------------------------------- /src/facades/dialog/AlertDialog.ts: -------------------------------------------------------------------------------- 1 | import { AbstractPresetDialog } from './AbstractPresetDialog'; 2 | import { PresetDialogOptions } from './PresetDialogInterface'; 3 | 4 | export interface AlertDialogOptions extends PresetDialogOptions { 5 | okButtonText?: string 6 | } 7 | 8 | export class AlertDialog extends AbstractPresetDialog { 9 | 10 | constructor(options: AlertDialogOptions) { 11 | super(options); 12 | 13 | this.initializeOkAction(options.okButtonText ? options.okButtonText : 'Ok'); 14 | } 15 | 16 | show(): Promise { 17 | return new Promise(resolve => { 18 | this._okAction.handler = () => resolve(); 19 | 20 | this._dialog.show(); 21 | }); 22 | } 23 | } -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/alert.component.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 18 | 19 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/directives/picker/picker-column.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | ContentChildren, 4 | Directive, 5 | ElementRef, 6 | QueryList 7 | } from '@angular/core'; 8 | 9 | import { PickerRowDirective } from './picker-row'; 10 | 11 | @Directive({ 12 | selector: 'picker-column,PickerColumn' 13 | }) 14 | export class PickerColumnDirective implements AfterContentInit { 15 | 16 | pickerColumn: Titanium.UI.PickerColumn; 17 | 18 | @ContentChildren(PickerRowDirective) rows: QueryList; 19 | 20 | constructor(el: ElementRef) { 21 | this.pickerColumn = el.nativeElement.titaniumView; 22 | } 23 | 24 | ngAfterContentInit() { 25 | this.rows.forEach(row => { 26 | this.pickerColumn.addRow(row.pickerRow); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/facades/dialog/DialogAction.ts: -------------------------------------------------------------------------------- 1 | export class DialogAction { 2 | title: string = ''; 3 | 4 | handler: Function = (event?: any) => {}; 5 | 6 | private _cancel: boolean = false; 7 | 8 | private _destructive: boolean = false; 9 | 10 | constructor(title: string, handler?: Function) { 11 | this.title = title; 12 | this.handler = handler ? handler : () => {}; 13 | } 14 | 15 | get isCancelAction() { 16 | return this._cancel; 17 | } 18 | 19 | set cancel(cancel: boolean) { 20 | this._cancel = cancel; 21 | } 22 | 23 | get isDestructiveAction(): boolean { 24 | return this._destructive; 25 | } 26 | 27 | set destructive(destructive: boolean) { 28 | this._destructive = destructive; 29 | } 30 | } -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | release: 4 | types: [ created ] 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | name: Publish 10 | 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Setup node 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: '16' 21 | registry-url: 'https://registry.npmjs.org' 22 | 23 | - name: Install dependencies 24 | run: npm ci 25 | if: steps.node-cache.outputs.cache-hit != 'true' 26 | 27 | - name: Publish to npm 28 | env: 29 | GH_TOKEN: ${{ github.token }} 30 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 31 | run: npm publish 32 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/phone/phone.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { TitaniumCommonModule } from 'titanium-angular'; 3 | 4 | import { CameraComponent } from './components/camera/camera.component'; 5 | import { PhoneComponent } from './phone.component'; 6 | import { PhoneRoutingModule } from './phone-routing.module'; 7 | 8 | import { SharedModule } from '@/shared/shared.module'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | TitaniumCommonModule, 13 | PhoneRoutingModule, 14 | SharedModule 15 | ], 16 | declarations: [ 17 | CameraComponent, 18 | PhoneComponent 19 | ], 20 | exports: [ 21 | PhoneComponent 22 | ], 23 | schemas: [NO_ERRORS_SCHEMA] 24 | }) 25 | export class PhoneModule { } -------------------------------------------------------------------------------- /src/directives/list-view/ti-item-template.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | Host, 4 | Input, 5 | TemplateRef 6 | } from '@angular/core'; 7 | 8 | import { ListViewComponent } from './list-view'; 9 | 10 | @Directive({ 11 | selector: '[tiTemplateName],[ti-template-name]' 12 | }) 13 | export class ListItemTemplateDirective { 14 | 15 | private templateRef: TemplateRef; 16 | 17 | private listView: ListViewComponent; 18 | 19 | constructor(templateRef: TemplateRef, @Host() listView: ListViewComponent) { 20 | this.templateRef = templateRef; 21 | this.listView = listView; 22 | } 23 | 24 | @Input() 25 | set tiTemplateName(name: string) { 26 | if (this.listView && this.templateRef) { 27 | this.listView.registerTemplate(name, this.templateRef); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/input/input-demo.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 18 | 19 | 24 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/platform-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes } from '@angular/router'; 3 | import { TitaniumRouterModule } from 'titanium-angular'; 4 | 5 | import { PlatformComponent } from './platform.component'; 6 | import { platformRoutes as routes } from './routes'; 7 | 8 | const platformRoutes: Routes = [ 9 | { 10 | path: 'controls', 11 | children: [ 12 | { 13 | path: 'platform', 14 | children: [ 15 | { path: '', component: PlatformComponent }, 16 | ...routes 17 | ] 18 | } 19 | ] 20 | } 21 | ] 22 | 23 | @NgModule({ 24 | imports: [ 25 | TitaniumRouterModule.forChild(platformRoutes) 26 | ], 27 | exports: [ 28 | TitaniumRouterModule 29 | ] 30 | }) 31 | export class PlatformRoutingModule { } -------------------------------------------------------------------------------- /src/directives/table-view/table-view-row.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Optional, OnInit, OnDestroy } from "@angular/core"; 2 | import { TitaniumElement } from 'titanium-vdom' 3 | 4 | import { TableViewDataSource } from './table-view'; 5 | import { TableViewSectionDirective } from "./table-view-section"; 6 | 7 | @Directive({ 8 | selector: 'table-view-row,TableViewRow', 9 | providers: [{ provide: TableViewDataSource, useExisting: TableViewRowDirective }] 10 | }) 11 | export class TableViewRowDirective extends TableViewDataSource { 12 | private element: TitaniumElement; 13 | 14 | constructor(el: ElementRef, @Optional() private parent: TableViewSectionDirective) { 15 | super(); 16 | 17 | this.element = el.nativeElement; 18 | } 19 | 20 | get dataSource(): Titanium.UI.TableViewRow { 21 | return this.element.titaniumView; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/nav-table.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { DeviceEnvironment, TitaniumRouter, WithTiGlobal } from 'titanium-angular'; 3 | 4 | export interface NavigationItem { 5 | id: string 6 | icon: string, 7 | iconStyle?: string 8 | title: string 9 | } 10 | 11 | @Component({ 12 | selector: 'nav-table', 13 | templateUrl: 'nav-table.component.html' 14 | }) 15 | export class NavTable extends WithTiGlobal() { 16 | @Input() prefix: string[]; 17 | 18 | @Input() items: NavigationItem[]; 19 | 20 | constructor(private router: TitaniumRouter, private device: DeviceEnvironment) { 21 | super(); 22 | } 23 | 24 | get hasChild() { 25 | return this.device.runs('android') ? false : true 26 | } 27 | 28 | onItemClick(e) { 29 | this.router.navigate([...this.prefix, this.items[e.index].id]); 30 | } 31 | } -------------------------------------------------------------------------------- /src/router/adapters/ComponentAdapter.ts: -------------------------------------------------------------------------------- 1 | import { ComponentRef } from "@angular/core"; 2 | import { ComponentAdapterInterface } from 'titanium-navigator'; 3 | import { ElementNode, findSingleVisualElement, TitaniumElement } from 'titanium-vdom'; 4 | 5 | export class ComponentAdapter implements ComponentAdapterInterface { 6 | getComponentName(component: ComponentRef) { 7 | return component.componentType.name; 8 | } 9 | 10 | detachComponent(component: any) { 11 | 12 | } 13 | 14 | getTopmostTitaniumElement(component: any): TitaniumElement { 15 | const componentElement: ElementNode = component.location.nativeElement; 16 | let candidateElement = null; 17 | try { 18 | candidateElement = findSingleVisualElement(componentElement); 19 | } catch (e) { 20 | console.warn(e); 21 | } 22 | 23 | return candidateElement; 24 | } 25 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [0.2.0](https://github.com/appcelerator/titanium-angular/compare/v0.1.0...v0.2.0) (2020-07-28) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * **slider:** add missing label text ([351b835](https://github.com/appcelerator/titanium-angular/commit/351b835a7aa8c29c37b64387f04975a71ea0b512)) 7 | * **theme:** properly close color tag ([3ff00fc](https://github.com/appcelerator/titanium-angular/commit/3ff00fcab4ae88fde50f03a7ecee901edb3d520e)) 8 | * **theme:** Remove unsupported material theme ([03c85e3](https://github.com/appcelerator/titanium-angular/commit/03c85e3fdc1f9b64452a4fd805e0b9b16e869e7c)) 9 | 10 | 11 | ### Features 12 | 13 | * support angular 9 ([#10](https://github.com/appcelerator/titanium-angular/issues/10)) ([718e52d](https://github.com/appcelerator/titanium-angular/commit/718e52d15f11203c56f41ece3b0866b807681408)) 14 | 15 | 16 | 17 | # 0.1.0 (2018-04-19) 18 | 19 | 20 | Initial release 21 | -------------------------------------------------------------------------------- /ti-angular-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "downlevelIteration": true, 8 | "experimentalDecorators": true, 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "target": "es2015", 13 | "lib": [ 14 | "es2018", 15 | "dom" 16 | ], 17 | "preserveSymlinks": true, 18 | "typeRoots": [ 19 | "typings", 20 | "node_modules/@types" 21 | ], 22 | "paths": { 23 | "@/*": ["src/*"] 24 | } 25 | }, 26 | "files": [ 27 | "src/main.ts", 28 | "src/polyfills.ts" 29 | ], 30 | "include": [ 31 | "src/**/*.ts" 32 | ], 33 | "angularCompilerOptions": { 34 | "enableIvy": false, 35 | "fullTemplateTypeCheck": true, 36 | "strictInjectionParameters": true 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/services/DeviceEnvironment.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable 3 | } from '@angular/core'; 4 | 5 | /** 6 | * Utility class to query info about the device environment. 7 | * 8 | * Internally uses Ti.Platform.*. 9 | */ 10 | @Injectable() 11 | export class DeviceEnvironment { 12 | 13 | /** 14 | * Returns the platform name of this device. 15 | * 16 | * Can be either android, ios or windows. 17 | */ 18 | get platformName() { 19 | return Ti.Platform.osname; 20 | } 21 | 22 | /** 23 | * Checks if the current devie runs the specified OS / platform name 24 | * 25 | * @param name Platform name to check, can be either android, ios or windows. 26 | */ 27 | runs(name: string): boolean { 28 | if (name === 'ios') { 29 | return ['iphone', 'ipad'].indexOf(this.platformName) !== -1; 30 | } 31 | 32 | return name === this.platformName; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/forms/directives/NgModel.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | EventEmitter, 4 | Input, 5 | OnChanges, 6 | Output, 7 | SimpleChanges 8 | } from "@angular/core"; 9 | 10 | @Directive({ 11 | selector: '[ngModel]:not([formControlName]):not([formControl])', 12 | exportAs: 'ngModel' 13 | }) 14 | export class NgModel implements OnChanges { 15 | viewModel: any 16 | 17 | @Input() name = ''; 18 | 19 | @Input() isDisabled = false; 20 | 21 | @Input('ngModel') model: any; 22 | 23 | @Output('ngModelChange') update = new EventEmitter(); 24 | 25 | ngOnChanges(changes: SimpleChanges) { 26 | /* 27 | this._checkForErrors(); 28 | if (!this._registered) this._setUpControl(); 29 | if ('isDisabled' in changes) { 30 | this._updateDisabled(changes); 31 | } 32 | 33 | if (isPropertyUpdated(changes, this.viewModel)) { 34 | this._updateValue(this.model); 35 | this.viewModel = this.model; 36 | } 37 | */ 38 | } 39 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/android/progress-indicator/progress-indicator.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, ViewChild } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | 4 | // @fixme: ProgressIndicator type is missing value property 5 | type ProgressIndicator = Ti.UI.Android.ProgressIndicator & { value: number } 6 | 7 | @Component({ 8 | templateUrl: 'progress-indicator.component.html' 9 | }) 10 | export class ProgressIndicatorComponent extends WithTiGlobal() { 11 | @ViewChild('dialog') 12 | set dialog(el: ElementRef) { 13 | this._dialogIndicator = el.nativeElement.titaniumView; 14 | } 15 | 16 | private _dialogIndicator: ProgressIndicator 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | loadThat() { 23 | this._dialogIndicator.show(); 24 | // do some async loading here ... 25 | setTimeout(() => this._dialogIndicator.hide(), 1500); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/common/DetachedLoaderComponent.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | ComponentFactory, 4 | ComponentFactoryResolver, 5 | ComponentRef, 6 | Type, 7 | ViewContainerRef 8 | } from '@angular/core'; 9 | 10 | @Component({ 11 | selector: 'DetachedView', 12 | template: `` 13 | }) 14 | export class DetachedLoaderComponent { 15 | constructor( 16 | private resolver: ComponentFactoryResolver, 17 | private containerRef: ViewContainerRef 18 | ) { } 19 | 20 | public loadComponent(componentType: Type): ComponentRef { 21 | const factory = this.resolver.resolveComponentFactory(componentType); 22 | return this.loadWithFactory(factory); 23 | } 24 | 25 | public loadWithFactory(factory: ComponentFactory): ComponentRef { 26 | return this.containerRef.createComponent(factory, this.containerRef.length, this.containerRef.parentInjector, null); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ti-angular-example/src/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | 3 | import { BaseButton } from './components/button.component'; 4 | import { FontAwesomeIcon } from './components/icon.component'; 5 | import { Alert } from './components/alert.component'; 6 | import { BaseLabel } from './components/label.component'; 7 | import { BaseWindow } from './components/window.component' 8 | import { NavTable } from './components/nav-table.component'; 9 | import { TitaniumCommonModule } from 'titanium-angular'; 10 | 11 | const COMPONENTS = [ 12 | Alert, 13 | BaseButton, 14 | BaseLabel, 15 | BaseWindow, 16 | FontAwesomeIcon, 17 | NavTable 18 | ] 19 | 20 | @NgModule({ 21 | imports: [ 22 | TitaniumCommonModule 23 | ], 24 | declarations: [ 25 | ...COMPONENTS 26 | ], 27 | exports: [ 28 | ...COMPONENTS, 29 | TitaniumCommonModule 30 | ], 31 | schemas: [NO_ERRORS_SCHEMA] 32 | }) 33 | export class SharedModule { 34 | 35 | } -------------------------------------------------------------------------------- /src/facades/picker/DatePicker.ts: -------------------------------------------------------------------------------- 1 | export interface DatePickerOptions { 2 | minDate?: Date, 3 | maxDate?: Date, 4 | value?: Date 5 | } 6 | 7 | export class DatePicker { 8 | 9 | private picker: Titanium.UI.Picker; 10 | 11 | constructor(options?: DatePickerOptions) { 12 | options = options ? options : {}; 13 | this.picker = Ti.UI.createPicker({ 14 | type: Titanium.UI.PICKER_TYPE_DATE, 15 | minDate: options.minDate, 16 | maxDate: options.maxDate, 17 | value: options.value 18 | }); 19 | } 20 | 21 | show(): Promise { 22 | return new Promise((resolve, reject) => { 23 | this.picker.showDatePickerDialog({ 24 | callback: (event) => { 25 | if (event.cancel) { 26 | return reject(); 27 | } 28 | 29 | resolve(event.value); 30 | }}); 31 | }); 32 | } 33 | 34 | 35 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/search-bar/search-bar.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | Component, 4 | OnInit, 5 | ViewChild, 6 | } from '@angular/core'; 7 | import { ListViewComponent } from 'titanium-angular'; 8 | 9 | @Component({ 10 | templateUrl: 'search-bar.component.html' 11 | }) 12 | export class SearchBarComponent implements OnInit, AfterViewInit { 13 | names = [ 14 | 'Sebastian', 15 | 'Alexandra', 16 | 'Alexej', 17 | 'Nina', 18 | 'Tobi' 19 | ] 20 | 21 | items = [] 22 | 23 | @ViewChild(ListViewComponent) listView: ListViewComponent 24 | 25 | ngOnInit() { 26 | this.items = this.names.map(name => { 27 | return { 28 | name: { text: name }, 29 | properties: { 30 | searchableText: name 31 | } 32 | } 33 | }); 34 | } 35 | 36 | ngAfterViewInit() { 37 | const listView = this.listView.listView 38 | listView.searchView = Ti.UI.createSearchBar(); 39 | } 40 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/intro/services/intro-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | ActivatedRouteSnapshot, 4 | CanActivate, 5 | RouterStateSnapshot 6 | } from '@angular/router'; 7 | import { TitaniumRouter } from 'titanium-angular'; 8 | 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class IntroGuard implements CanActivate { 13 | 14 | get introShown(): boolean { 15 | return Ti.App.Properties.getBool('introShown'); 16 | } 17 | 18 | set introShown(value: boolean) { 19 | Ti.App.Properties.setBool('introShown', value); 20 | } 21 | 22 | constructor(private router: TitaniumRouter) { 23 | } 24 | 25 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { 26 | if (this.introShown) { 27 | return true; 28 | } 29 | 30 | this.introShown = true; 31 | this.router.navigate(['/intro']); 32 | 33 | return false; 34 | } 35 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/blur-view/blur-view.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: './blur-view.component.html' 5 | }) 6 | export class BlurViewComponent implements AfterViewInit { 7 | @ViewChild('blurView', { static: false }) blurViewRef: ElementRef; 8 | 9 | blurLabels: string[] = [ 10 | 'Extra Light', 11 | 'Light', 12 | 'Dark' 13 | ] 14 | 15 | private blurView: Titanium.UI.iOS.BlurView; 16 | 17 | ngAfterViewInit() { 18 | this.blurView = this.blurViewRef.nativeElement.titaniumView; 19 | } 20 | 21 | applyBlurEffect(event) { 22 | const blurEffects = [ 23 | Ti.UI.iOS.BLUR_EFFECT_STYLE_EXTRA_LIGHT, 24 | Ti.UI.iOS.BLUR_EFFECT_STYLE_LIGHT, 25 | Ti.UI.iOS.BLUR_EFFECT_STYLE_DARK, 26 | ] 27 | this.blurView.setEffect(blurEffects[event.index]); 28 | } 29 | } -------------------------------------------------------------------------------- /src/facades/picker/TimePicker.ts: -------------------------------------------------------------------------------- 1 | export interface TimePickerOptions { 2 | minDate?: Date, 3 | maxDate?: Date, 4 | value?: Date 5 | } 6 | 7 | export class TimePicker { 8 | 9 | private picker: Titanium.UI.Picker; 10 | 11 | constructor(options?: TimePickerOptions) { 12 | options = options ? options : {}; 13 | this.picker = Ti.UI.createPicker({ 14 | type: Titanium.UI.PICKER_TYPE_DATE, 15 | minDate: options.minDate, 16 | maxDate: options.maxDate, 17 | value: options.value 18 | }); 19 | } 20 | 21 | show(): Promise { 22 | return new Promise((resolve, reject) => { 23 | this.picker.showTimePickerDialog({ 24 | callback: (event) => { 25 | if (event.cancel) { 26 | return reject(); 27 | } 28 | 29 | resolve(event.value); 30 | } 31 | }); 32 | }); 33 | } 34 | 35 | 36 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/ios/live-photo/live-photo.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | Component, 4 | ElementRef, 5 | ViewChild 6 | } from '@angular/core'; 7 | 8 | @Component({ 9 | templateUrl: './live-photo.component.html' 10 | }) 11 | export class LivePhotoComponent implements AfterViewInit { 12 | @ViewChild('livePhoto', { static: false }) livePhotoViewRef: ElementRef; 13 | 14 | private livePhotoView: Titanium.UI.iOS.LivePhotoView; 15 | 16 | ngAfterViewInit() { 17 | this.livePhotoView = this.livePhotoViewRef.nativeElement.titaniumView; 18 | } 19 | 20 | selectLivePhoto() { 21 | Ti.Media.openPhotoGallery({ 22 | mediaTypes: [Ti.Media.MEDIA_TYPE_PHOTO, Ti.Media.MEDIA_TYPE_LIVEPHOTO], 23 | success: e => { 24 | if (e.livePhoto) { 25 | this.livePhotoView.livePhoto = e.livePhoto; 26 | } else { 27 | console.warn('No live photo selected'); 28 | } 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/web-view/web-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { DeviceEnvironment, Logger } from 'titanium-angular'; 3 | 4 | @Component({ 5 | templateUrl: './web-view.component.html' 6 | }) 7 | export class WebViewComponent { 8 | constructor(private logger: Logger, private device: DeviceEnvironment) { 9 | 10 | } 11 | 12 | onBeforeLoad(event) { 13 | if (!this.device.runs('windows')) { 14 | this.logger.debug(`Ti.UI.WebView will start loading content (event: ${JSON.stringify(event)}).`); 15 | } else { 16 | this.logger.debug(`Ti.UI.WebView will start loading content.`); 17 | } 18 | } 19 | 20 | onLoad(event) { 21 | if (!this.device.runs('windows')) { 22 | this.logger.debug(`Ti.UI.WebView completed loading content (event: ${JSON.stringify(event)}).`); 23 | } else { 24 | this.logger.debug(`Ti.UI.WebView completed loading content.`); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/platform.module.ios.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { TitaniumCommonModule } from 'titanium-angular'; 3 | 4 | import { BlurViewComponent } from './ios/blur-view/blur-view.component'; 5 | import { LivePhotoComponent } from './ios/live-photo/live-photo.component'; 6 | import { StepperComponent } from './ios/stepper/stepper.component'; 7 | import { PlatformComponent } from './platform.component'; 8 | import { PlatformRoutingModule } from './platform-routing.module'; 9 | 10 | import { SharedModule } from '@/shared/shared.module'; 11 | 12 | const PLATFORM_COMPONENTS = [ 13 | PlatformComponent, 14 | BlurViewComponent, 15 | LivePhotoComponent, 16 | StepperComponent 17 | ] 18 | 19 | @NgModule({ 20 | imports: [ 21 | PlatformRoutingModule, 22 | SharedModule, 23 | TitaniumCommonModule 24 | ], 25 | declarations: [ 26 | ...PLATFORM_COMPONENTS 27 | ], 28 | exports: [ 29 | ...PLATFORM_COMPONENTS 30 | ], 31 | schemas: [NO_ERRORS_SCHEMA] 32 | }) 33 | export class PlatformModule { 34 | 35 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/controls.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | 4 | import { NavigationItem } from '@/shared/components/nav-table.component' 5 | 6 | @Component({ 7 | selector: 'ControlsTab', 8 | templateUrl: 'controls.component.html' 9 | }) 10 | export class ControlsComponent extends WithTiGlobal() implements OnInit { 11 | 12 | items: NavigationItem[]; 13 | 14 | routePrefix = ['controls']; 15 | 16 | ngOnInit() { 17 | this.items = [ 18 | { 19 | id: 'views', 20 | icon: 'layer', 21 | title: 'Structural Views' 22 | }, { 23 | id: 'inputs', 24 | icon: 'clipboard-list', 25 | title: 'Input Elements' 26 | }, { 27 | id: 'utility', 28 | icon: 'cogs', 29 | title: 'Utility Views' 30 | }, { 31 | id: 'platform', 32 | icon: 'flask', 33 | title: 'Platform' 34 | }, { 35 | id: 'dialogs', 36 | icon: 'clone', 37 | title: 'Dialogs' 38 | } 39 | ] 40 | } 41 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/list-view/item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'ItemComponent', 5 | template: ` 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ` 20 | }) 21 | export class ItemComponent { 22 | 23 | } -------------------------------------------------------------------------------- /src/directives/table-view/table-view.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentChecked, 3 | ContentChildren, 4 | Directive, 5 | ElementRef, 6 | QueryList, 7 | } from '@angular/core'; 8 | import { TitaniumElement } from 'titanium-vdom' 9 | 10 | export abstract class TableViewDataSource { 11 | abstract get dataSource(): Titanium.UI.TableViewRow | Titanium.UI.TableViewSection 12 | } 13 | 14 | @Directive({ 15 | selector: 'table-view,TableView' 16 | }) 17 | export class TableViewDirective implements AfterContentChecked { 18 | private tableView: Ti.UI.TableView 19 | 20 | @ContentChildren(TableViewDataSource) 21 | private children: QueryList 22 | 23 | constructor(el: ElementRef) { 24 | this.tableView = el.nativeElement.titaniumView; 25 | } 26 | 27 | ngAfterContentChecked() { 28 | if (this.children && this.children.length) { 29 | const data = (this.children.map(rowOrSection => rowOrSection.dataSource)) as any; 30 | this.tableView.data = data; 31 | } 32 | } 33 | 34 | // @TODO: make use of iterable differs to add/remove rows/section on 35 | // subsequent content changes 36 | } 37 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/refresh-control/refresh-control.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'refresh-control.component.html' 5 | }) 6 | export class RefreshControlComponent { 7 | dataSource = [ 8 | { name: 'JJ', surname: 'Peters' }, 9 | { name: 'Nikita', surname: 'Kamprad' }, 10 | { name: 'Colin', surname: 'Jeffs' }, 11 | { name: 'Einar', surname: 'Selvik' } 12 | ] 13 | 14 | get items() { 15 | return this.dataSource.map(sourceItem => { 16 | return { 17 | name: { 18 | text: sourceItem.name 19 | }, 20 | surname: { 21 | text: sourceItem.surname 22 | } 23 | } 24 | }); 25 | } 26 | 27 | loadMore(e) { 28 | setTimeout(() => { 29 | // You can push new data ... 30 | this.dataSource.push({ name: 'Michael', surname: 'Roth' }); 31 | // ... or replace the whole data source 32 | // self.dataSource = [{ name: 'Jordan', surname: 'Dryer' }, { Name: 'Derek', surname: 'Archambault' }] 33 | e.source.endRefreshing(); 34 | }, 1000); 35 | } 36 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/scrollable-view/scrollable-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | 4 | interface Card { 5 | id: number 6 | icon: string 7 | title: string, 8 | text: string 9 | } 10 | 11 | @Component({ 12 | templateUrl: './scrollable-view.component.html' 13 | }) 14 | export class ScrollableViewComponent extends WithTiGlobal() implements OnInit{ 15 | 16 | cards: Card[]; 17 | 18 | ngOnInit() { 19 | this.cards = [ 20 | { 21 | id: 1, 22 | icon: 'angle-double-left', 23 | title: 'Step 1', 24 | text: 'Swipe left to continue' 25 | }, { 26 | id: 2, 27 | icon: 'fire', 28 | title: 'Step 2', 29 | text: 'Awesome, you got the concept' 30 | }, { 31 | id: 3, 32 | icon: 'thumbs-up', 33 | title: 'Step 3', 34 | text: 'That\'s it, now go back!' 35 | } 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/android/search-view/search-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; 2 | import { BaseWindow } from '@/shared/components/window.component'; 3 | 4 | @Component({ 5 | templateUrl: 'search-view.component.html' 6 | }) 7 | export class SearchViewComponent implements AfterViewInit { 8 | @ViewChild(BaseWindow) win: BaseWindow; 9 | @ViewChild('table') tableRef: ElementRef; 10 | 11 | constructor() { } 12 | 13 | ngAfterViewInit() { 14 | const search = Ti.UI.Android.createSearchView({ 15 | hintText: 'Fruit' 16 | }); 17 | 18 | const table = this.tableRef.nativeElement.titaniumView; 19 | table.search = search; 20 | 21 | const win = this.win.winRef.nativeElement.titaniumView; 22 | win.activity.onCreateOptionsMenu = e => { 23 | e.menu.add({ 24 | title: 'Table Search', 25 | actionView: search, 26 | icon: Ti.App.Android.R.drawable.baseline_search_white_48, 27 | showAsAction: Ti.Android.SHOW_AS_ACTION_IF_ROOM | Ti.Android.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW 28 | }) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/platform.module.android.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { TitaniumCommonModule } from 'titanium-angular'; 3 | 4 | import { CardViewComponent } from './android/card-view/card-view.component'; 5 | import { ProgressIndicatorComponent } from './android/progress-indicator/progress-indicator.component'; 6 | import { SearchViewComponent } from './android/search-view/search-view.component'; 7 | import { PlatformComponent } from './platform.component'; 8 | import { PlatformRoutingModule } from './platform-routing.module'; 9 | 10 | import { SharedModule } from '@/shared/shared.module'; 11 | 12 | const PLATFORM_COMPONENTS = [ 13 | PlatformComponent, 14 | CardViewComponent, 15 | ProgressIndicatorComponent, 16 | SearchViewComponent 17 | ] 18 | 19 | @NgModule({ 20 | imports: [ 21 | PlatformRoutingModule, 22 | SharedModule, 23 | TitaniumCommonModule 24 | ], 25 | declarations: [ 26 | ...PLATFORM_COMPONENTS 27 | ], 28 | exports: [ 29 | ...PLATFORM_COMPONENTS 30 | ], 31 | schemas: [NO_ERRORS_SCHEMA] 32 | }) 33 | export class PlatformModule { 34 | 35 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/dialogs/dialogs.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Preset dialogs 4 | Alert 5 | Confirm 6 | Prompt 7 | Custom dialogs 8 | 9 | Android custom view 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ti-angular-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "titanium-angular-dev-app", 3 | "version": "0.1.0", 4 | "description": "Test app for the Titanium Angular platform", 5 | "main": "app/src/main.ts", 6 | "scripts": { 7 | "test": "echo 'No tests specified'", 8 | "dev:ios": "ti build -p ios" 9 | }, 10 | "author": "Axway Appcelerator", 11 | "license": "Apache-2.0", 12 | "repository": "https://github.com/appcelerator/titanium-angular", 13 | "devDependencies": { 14 | "@angular/common": "~9.1.0", 15 | "@angular/compiler": "~9.1.0", 16 | "@angular/compiler-cli": "~9.1.0", 17 | "@angular/core": "~9.1.0", 18 | "@angular/platform-browser": "~9.1.0", 19 | "@angular/platform-browser-dynamic": "~9.1.0", 20 | "@angular/router": "~9.1.0", 21 | "@ngtools/webpack": "~9.1.0", 22 | "@titanium-sdk/webpack-plugin-angular": "^0.1.1", 23 | "@types/titanium": "^9.2.0", 24 | "reflect-metadata": "^0.1.13", 25 | "rxjs": "^6.5.4", 26 | "titanium-angular": "^0.2.0", 27 | "typescript": "~3.8.0", 28 | "webpack": "^4.43.0", 29 | "zone.js": "^0.11.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/scrollable-view/scrollable-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 13 | 18 | 19 | 24 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/directives/tab-group/tab.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | Directive, 4 | ElementRef, 5 | OnDestroy 6 | } from '@angular/core'; 7 | import { findSingleVisualElement, TitaniumElement } from 'titanium-vdom'; 8 | 9 | import { TabGroupDirective } from './tab-group'; 10 | 11 | @Directive({ 12 | selector: 'tab,Tab' 13 | }) 14 | export class TabDirective implements AfterContentInit, OnDestroy { 15 | 16 | public element: TitaniumElement; 17 | 18 | private tab: Titanium.UI.Tab; 19 | 20 | private owner: TabGroupDirective; 21 | 22 | constructor(el: ElementRef, owner: TabGroupDirective) { 23 | this.element = el.nativeElement; 24 | this.tab = this.element.titaniumView; 25 | this.owner = owner; 26 | } 27 | 28 | ngAfterContentInit() { 29 | const candidateElement = this.element.firstElementChild; 30 | const windowElement = >findSingleVisualElement(candidateElement); 31 | if (windowElement.nodeName !== 'WINDOW') { 32 | throw new Error('The first child of a Tab always must be a Window'); 33 | } 34 | 35 | this.tab.window = windowElement.titaniumView; 36 | this.owner.addTab(this.tab); 37 | } 38 | 39 | ngOnDestroy() { 40 | this.owner.removeTab(this.tab); 41 | } 42 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/table-view/table-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | 4 | interface Transaction { 5 | id: number, 6 | type: string, 7 | date: string, 8 | name: string, 9 | amount: string, 10 | status: string 11 | } 12 | 13 | @Component({ 14 | templateUrl: './table-view.component.html', 15 | }) 16 | export class TableViewComponent extends WithTiGlobal() implements OnInit { 17 | items: Transaction[]; 18 | 19 | ngOnInit() { 20 | this.items = [ 21 | { 22 | id: 1, 23 | type: 'deposit', 24 | date: '13/09/2018 16:23', 25 | name: 'Ann Gray', 26 | amount: '$290.00', 27 | status: 'Complete' 28 | }, { 29 | id: 2, 30 | type: 'withdrawl', 31 | date: '13/09/2018 12:53', 32 | name: 'Margo Healts', 33 | amount: '$135.50', 34 | status: 'Complete' 35 | } 36 | ] 37 | } 38 | 39 | iconForType(transactionType: string) { 40 | return transactionType === 'deposit' ? 'long-arrow-alt-down' : 'long-arrow-alt-up'; 41 | } 42 | colorForType(transactionType: string) { 43 | return transactionType === 'deposit' ? '#4fc08d' : '#f66'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/utility-views.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { NavigationItem } from '@/shared/components/nav-table.component'; 4 | 5 | @Component({ 6 | template: ` 7 | 8 | 9 | 10 | ` 11 | }) 12 | export class UtilityViews implements OnInit { 13 | items: NavigationItem[]; 14 | 15 | routePrefix = ['controls', 'utility']; 16 | 17 | ngOnInit() { 18 | this.items = [ 19 | { 20 | id: 'progress-indicators', 21 | icon: 'spinner', 22 | title: 'Progress Indicators' 23 | }, { 24 | id: 'masked-image', 25 | icon: 'fill-drip', 26 | title: 'Masked Image' 27 | }, { 28 | id: 'refresh-control', 29 | icon: 'sync-alt', 30 | title: 'Refresh Control' 31 | }, { 32 | id: 'search-bar', 33 | icon: 'search', 34 | title: 'Search Bar' 35 | }, { 36 | id: 'button-bar', 37 | icon: 'minus', 38 | title: 'Button Bar' 39 | }, { 40 | id: 'toolbar', 41 | icon: 'clone', 42 | title: 'Toolbar' 43 | } 44 | ]; 45 | } 46 | } -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/alert.component.ts: -------------------------------------------------------------------------------- 1 | import { WithTiGlobal } from 'titanium-angular'; 2 | import { Component, Input, ViewChild, ElementRef } from '@angular/core'; 3 | 4 | const colorTable = { 5 | info: '#2973b7', 6 | error: '#f66' 7 | } 8 | 9 | @Component({ 10 | selector: 'alert', 11 | templateUrl: './alert.component.html' 12 | }) 13 | export class Alert extends WithTiGlobal() { 14 | @Input() type = 'info' 15 | 16 | @ViewChild('borderView') 17 | set borderView(borderRef: ElementRef) { 18 | this._borderView = borderRef.nativeElement.titaniumView; 19 | } 20 | 21 | @ViewChild('contentView') 22 | set contentView(contentRef: ElementRef) { 23 | this._contentView = contentRef.nativeElement.titaniumView; 24 | } 25 | 26 | private _borderView!: Titanium.UI.View 27 | 28 | private _contentView!: Titanium.UI.View 29 | 30 | get primaryColor(): string { 31 | return colorTable[this.type]; 32 | } 33 | 34 | computeHeight() { 35 | if (!this._contentView || !this._borderView) { 36 | return; 37 | } 38 | 39 | const computedHeight = this._contentView.size.height + 24; 40 | const currentBorderHeight = this._borderView.size.height; 41 | if (currentBorderHeight !== computedHeight) { 42 | this._borderView.height = computedHeight; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/refresh-control/refresh-control.component.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | Pull down the table view to refresh the currently displayed data. You can use a RefreshControl in TableView, ListView and ScrollView elements. 8 | 9 | 10 | 11 | 15 | 16 | 17 | 23 | 24 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/utility.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | 3 | import { SharedModule } from '@/shared/shared.module'; 4 | 5 | import { UtilityRoutingModule } from './utility-routing.module'; 6 | import { UtilityViews } from './utility-views.component'; 7 | import { ButtonBarComponent } from './button-bar/button-bar.component'; 8 | import { MaskedImageComponent } from './masked-image/masked-image.component'; 9 | import { FormatBytesPipe } from './progress-indicators/format-bytes.pipe'; 10 | import { ProgressIndicatorsComponent } from './progress-indicators/progress-indicators.component'; 11 | import { RefreshControlComponent } from './refresh-control/refresh-control.component'; 12 | import { SearchBarComponent } from './search-bar/search-bar.component'; 13 | import { ToolbarComponent } from './toolbar/toolbar.component'; 14 | 15 | @NgModule({ 16 | imports: [ 17 | SharedModule, 18 | UtilityRoutingModule 19 | ], 20 | declarations: [ 21 | UtilityViews, 22 | ButtonBarComponent, 23 | FormatBytesPipe, 24 | MaskedImageComponent, 25 | ProgressIndicatorsComponent, 26 | SearchBarComponent, 27 | RefreshControlComponent, 28 | ToolbarComponent 29 | ], 30 | schemas: [NO_ERRORS_SCHEMA] 31 | }) 32 | export class UtilityModule { 33 | 34 | } -------------------------------------------------------------------------------- /src/directives/toolbar.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; 2 | import { InvisibleElement, TitaniumElement } from 'titanium-vdom'; 3 | 4 | @Component({ 5 | selector: 'toolbar,Toolbar', 6 | template: ` 7 | 8 | 9 | 10 | ` 11 | }) 12 | export class ToolbarComponent implements AfterViewInit { 13 | @ViewChild('container') container: ElementRef 14 | 15 | toolbar: Titanium.UI.Toolbar; 16 | 17 | constructor(el: ElementRef) { 18 | this.toolbar = el.nativeElement.titaniumView; 19 | } 20 | 21 | ngAfterViewInit() { 22 | const containerElement = this.container.nativeElement; 23 | const items = []; 24 | 25 | for (let element of containerElement.children) { 26 | if (element instanceof InvisibleElement) { 27 | element = element.firstVisualChild; 28 | } 29 | if (element instanceof TitaniumElement) { 30 | if (element.tagName !== 'BUTTON') { 31 | throw new Error('Only Buttons are allowed as items in a Toolbar'); 32 | } 33 | 34 | items.push(element.titaniumView); 35 | } 36 | } 37 | 38 | this.toolbar.items = items; 39 | } 40 | } -------------------------------------------------------------------------------- /src/facades/dialog/ConfirmDialog.ts: -------------------------------------------------------------------------------- 1 | import { AbstractPresetDialog } from './AbstractPresetDialog'; 2 | import { AlertDialogOptions } from './AlertDialog'; 3 | 4 | export interface ConfirmDialogOptions extends AlertDialogOptions { 5 | neutralButtonText?: string, 6 | cancelButtonText?: string 7 | } 8 | 9 | export enum ConfirmResult { 10 | Ok, 11 | Neutral, 12 | Cancel 13 | } 14 | 15 | export class ConfirmDialog extends AbstractPresetDialog { 16 | 17 | constructor(options: ConfirmDialogOptions) { 18 | super(options); 19 | 20 | this.initializeOkAction(options.okButtonText ? options.okButtonText : 'Ok'); 21 | if (options.neutralButtonText) { 22 | this.initializeNeutralAction(options.neutralButtonText); 23 | } 24 | this.initalizeCancelAction(options.cancelButtonText ? options.cancelButtonText : 'Cancel'); 25 | } 26 | 27 | show(): Promise { 28 | return new Promise(resolve => { 29 | this._okAction.handler = () => resolve(ConfirmResult.Ok); 30 | if (this._neutralAction !== null) { 31 | this._neutralAction.handler = () => resolve(ConfirmResult.Neutral); 32 | } 33 | this._cancelAction.handler = () => resolve(ConfirmResult.Cancel); 34 | 35 | this._dialog.show(); 36 | }); 37 | } 38 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "downlevelIteration": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "noImplicitUseStrict": true, 11 | "noEmitHelpers": false, 12 | "declaration": true, 13 | "removeComments": false, 14 | "noEmitOnError": true, 15 | "noImplicitAny": false, 16 | "lib": [ 17 | "dom", 18 | "es6", 19 | "es2015.iterable" 20 | ], 21 | "rootDir": "./src", 22 | "outDir": "./dist", 23 | "baseUrl": "./src", 24 | "types": [ 25 | "titanium", 26 | "node" 27 | ], 28 | "typeRoots": [ 29 | "./node_modules/@types", 30 | "./typings" 31 | ] 32 | }, 33 | "include": [ 34 | "src/**/*" 35 | ], 36 | "angularCompilerOptions": { 37 | "genDir": "./dist", 38 | "skipMetadataEmit": false, 39 | "skipTemplateCodegen": true, 40 | "strictMetadataEmit": true, 41 | "fullTemplateTypeCheck": true, 42 | "strictInjectionParameters": true, 43 | "enableResourceInlining": true, 44 | "enableIvy": false 45 | } 46 | } -------------------------------------------------------------------------------- /src/facades/dialog/AbstractPresetDialog.ts: -------------------------------------------------------------------------------- 1 | import { BaseDialog } from './BaseDialog'; 2 | import { DialogAction } from './DialogAction'; 3 | import { PresetDialogInterface } from './PresetDialogInterface' 4 | import { PresetDialogOptions } from './PresetDialogInterface'; 5 | 6 | export abstract class AbstractPresetDialog implements PresetDialogInterface { 7 | protected _dialog: BaseDialog; 8 | 9 | protected _okAction: DialogAction; 10 | 11 | protected _neutralAction: DialogAction = null; 12 | 13 | protected _cancelAction: DialogAction = null; 14 | 15 | constructor(options: PresetDialogOptions) { 16 | this._dialog = new BaseDialog(options.title, options.message); 17 | } 18 | 19 | public abstract show(): Promise; 20 | 21 | protected initializeOkAction(buttonTitle: string) { 22 | this._okAction = new DialogAction(buttonTitle); 23 | this._dialog.addAction(this._okAction); 24 | } 25 | 26 | protected initializeNeutralAction(buttonTitle: string) { 27 | this._neutralAction = new DialogAction(buttonTitle); 28 | this._dialog.addAction(this._neutralAction); 29 | } 30 | 31 | protected initalizeCancelAction(buttonTitle: string) { 32 | this._cancelAction = new DialogAction(buttonTitle); 33 | this._cancelAction.cancel = true; 34 | this._dialog.addAction(this._cancelAction); 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /src/directives/tab-group/tab-group.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input } from '@angular/core'; 2 | 3 | import { DeviceEnvironment } from '../../services'; 4 | 5 | @Directive({ 6 | selector: 'tab-group,TabGroup' 7 | }) 8 | export class TabGroupDirective { 9 | 10 | private tabGroup: Titanium.UI.TabGroup; 11 | 12 | private _selectedTab = 0; 13 | 14 | constructor(el: ElementRef, private device: DeviceEnvironment) { 15 | this.tabGroup = el.nativeElement.titaniumView; 16 | } 17 | 18 | get selectedTab(): number { 19 | return this._selectedTab; 20 | } 21 | 22 | @Input() 23 | set selectedTab(index) { 24 | this._selectedTab = index; 25 | this.tabGroup.activeTab = this.tabGroup.tabs[index]; 26 | } 27 | 28 | get titaniumView(): Titanium.UI.TabGroup { 29 | return this.tabGroup; 30 | } 31 | 32 | addTab(tab: Titanium.UI.Tab) { 33 | this.tabGroup.addTab(tab); 34 | const numberOfTabs = this.tabGroup.tabs.length; 35 | if (numberOfTabs > 1 && this.device.runs('ios')) { 36 | // FIXME: We need to switch the active tab once after adding additional 37 | // tabs to keep the currently active tab highlighted in the tab bar. 38 | this.tabGroup.activeTab = this.tabGroup.tabs[numberOfTabs -1]; 39 | this.tabGroup.activeTab = this.selectedTab; 40 | } 41 | } 42 | 43 | removeTab(tab: Titanium.UI.Tab) { 44 | this.tabGroup.removeTab(tab); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/directives/picker/picker.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | ContentChildren, 4 | Directive, 5 | ElementRef, 6 | QueryList, 7 | Input 8 | } from '@angular/core'; 9 | 10 | import { PickerColumnDirective } from './picker-column'; 11 | import { PickerRowDirective } from './picker-row'; 12 | 13 | @Directive({ 14 | selector: 'picker,Picker' 15 | }) 16 | export class PickerDirective implements AfterContentInit { 17 | 18 | private picker: Titanium.UI.Picker; 19 | 20 | @ContentChildren(PickerRowDirective) rows: QueryList; 21 | 22 | @ContentChildren(PickerColumnDirective) columns: QueryList; 23 | 24 | @Input() 25 | set type(value: number) { 26 | // @todo: create new picker proxy if changed after already rendered 27 | this.picker.type = value; 28 | } 29 | 30 | constructor(el: ElementRef) { 31 | this.picker = el.nativeElement.titaniumView; 32 | } 33 | 34 | ngAfterContentInit() { 35 | const pickerData = []; 36 | 37 | if (this.columns.length >= 0) { 38 | this.columns.forEach(column => { 39 | pickerData.push(column.pickerColumn); 40 | }); 41 | } 42 | 43 | if (this.rows.length >= 0) { 44 | this.rows.forEach(row => { 45 | pickerData.push(row.pickerRow); 46 | }); 47 | } 48 | 49 | this.picker.add(pickerData); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/compiler/TitaniumElementSchemaRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ElementSchemaRegistry } from "@angular/compiler"; 2 | import { SchemaMetadata } from "@angular/core"; 3 | 4 | export class TitaniumElementSchemaRegistry extends ElementSchemaRegistry { 5 | hasProperty(_tagName: string, _propName: string): boolean { 6 | return true; 7 | } 8 | 9 | hasElement(_tagName: string, _schemaMetas: SchemaMetadata[]): boolean { 10 | return true; 11 | } 12 | 13 | getMappedPropName(propName: string): string { 14 | return propName; 15 | } 16 | 17 | getDefaultComponentElementName(): string { 18 | return "ng-component"; 19 | } 20 | 21 | securityContext(_tagName: string, _propName: string): any { 22 | return 0; 23 | } 24 | 25 | validateProperty(_name: string): { error: boolean, msg?: string } { 26 | return { error: false }; 27 | } 28 | 29 | validateAttribute(_name: string): { error: boolean, msg?: string } { 30 | return { error: false }; 31 | } 32 | 33 | allKnownElementNames(): string[] { 34 | return []; 35 | } 36 | 37 | normalizeAnimationStyleProperty(propName: string): string { 38 | return propName; 39 | } 40 | 41 | normalizeAnimationStyleValue(_camelCaseProp: string, _userProvidedProp: string, val: string | number): { error: string, value: string } { 42 | return { error: null, value: val.toString() }; 43 | } 44 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/button-bar/button-bar.component.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | Simple labels 12 | 13 | 20 | 21 | 22 | 23 | 28 | Advanced configuration 29 | 30 | 36 | 37 | 38 | 43 | Tabbed bar (maintains state) 44 | 45 | 53 | 54 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/masked-image/masked-image.component.html: -------------------------------------------------------------------------------- 1 | 5 | 10 | 15 | Mode: {{ currentBlendModeName }} 16 | 17 | 22 | Image and tint 23 | 24 | 31 | 32 | 37 | 42 | Two images 43 | 44 | 51 | 52 | 56 | Change blend mode 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/views.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { NavigationItem } from '@/shared/components/nav-table.component'; 4 | import { DeviceEnvironment } from 'titanium-angular'; 5 | 6 | @Component({ 7 | templateUrl: './views.component.html' 8 | }) 9 | export class ViewsComponent implements OnInit { 10 | items: NavigationItem[]; 11 | 12 | routePrefix = ['controls', 'views']; 13 | 14 | constructor(private device: DeviceEnvironment) {} 15 | 16 | ngOnInit() { 17 | this.items = [ 18 | { 19 | id: 'view', 20 | icon: 'layer', 21 | title: 'View' 22 | }, 23 | { 24 | id: 'image-view', 25 | icon: 'image', 26 | title: 'Image View' 27 | }, 28 | { 29 | id: 'list-view', 30 | icon: 'list', 31 | title: 'List View' 32 | }, 33 | { 34 | id: 'table-view', 35 | icon: 'list-alt', 36 | title: 'Table View' 37 | }, 38 | { 39 | id: 'scroll-view', 40 | icon: 'arrows-alt-v', 41 | title: 'Scroll View' 42 | }, 43 | { 44 | id: 'scrollable-view', 45 | icon: 'elipsis-h', 46 | title: 'Scrollable View' 47 | }, 48 | { 49 | id: 'web-view', 50 | icon: this.device.runs('ios') ? 'safari' : 'chrome', 51 | iconStyle: 'brands', 52 | title: 'Web View' 53 | } 54 | ]; 55 | } 56 | } -------------------------------------------------------------------------------- /src/directives/scrollable-view.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | AfterViewInit, 4 | Component, 5 | ElementRef, 6 | ViewChild, 7 | } from '@angular/core'; 8 | 9 | import { 10 | findSingleVisualElement, 11 | InvisibleElement, 12 | TitaniumElement 13 | } from 'titanium-vdom'; 14 | 15 | @Component({ 16 | selector: 'scrollable-view,ScrollableView', 17 | template: ` 18 | 19 | 20 | 21 | ` 22 | }) 23 | export class ScrollableViewDirective implements AfterViewInit { 24 | 25 | @ViewChild('container', { read: ElementRef, static: false }) container: ElementRef 26 | 27 | scrollableView: Titanium.UI.ScrollableView; 28 | 29 | constructor(el: ElementRef) { 30 | this.scrollableView = el.nativeElement.titaniumView; 31 | } 32 | 33 | ngAfterViewInit() { 34 | const containerElement = this.container.nativeElement as TitaniumElement; 35 | const views = []; 36 | 37 | for (const child of containerElement.children) { 38 | if (child instanceof TitaniumElement) { 39 | views.push(child.titaniumView) 40 | } else if (child instanceof InvisibleElement) { 41 | const visualElement = findSingleVisualElement(child); 42 | views.push(visualElement.titaniumView); 43 | } 44 | } 45 | 46 | this.scrollableView.views = views; 47 | } 48 | } -------------------------------------------------------------------------------- /src/renderer/TitaniumRendererFactory.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | NgZone, 4 | RendererFactory2, 5 | RendererType2, 6 | } from '@angular/core'; 7 | import { TitaniumElementRegistry } from 'titanium-vdom'; 8 | 9 | import { Logger } from '../log'; 10 | import { TitaniumRenderer } from './TitaniumRenderer' 11 | 12 | @Injectable() 13 | export class TitaniumRendererFactory implements RendererFactory2 { 14 | 15 | private titaniumElementRegistry: TitaniumElementRegistry 16 | 17 | private defaultRenderer: TitaniumRenderer; 18 | 19 | private logger: Logger; 20 | 21 | constructor( 22 | titaniumElementRegistry: TitaniumElementRegistry, 23 | logger: Logger, 24 | private zone: NgZone 25 | ) { 26 | this.titaniumElementRegistry = titaniumElementRegistry; 27 | const meta = this.titaniumElementRegistry.getViewMetadata('table-view'); 28 | meta.detached = false; 29 | this.logger = logger; 30 | this.defaultRenderer = new TitaniumRenderer({ 31 | elementRegistry: this.titaniumElementRegistry, 32 | logger: this.logger, 33 | }, zone); 34 | } 35 | 36 | createRenderer(hostElement: any, type: RendererType2): TitaniumRenderer { 37 | if (!hostElement || !type) { 38 | return this.defaultRenderer; 39 | } 40 | return new TitaniumRenderer({ 41 | elementRegistry: this.titaniumElementRegistry, 42 | logger: this.logger, 43 | }, this.zone); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/platform/platform.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { WithTiGlobal, DeviceEnvironment } from 'titanium-angular'; 3 | 4 | import { NavigationItem } from '@/shared/components/nav-table.component' 5 | 6 | @Component({ 7 | selector: 'PlatformComponent', 8 | templateUrl: './platform.component.html' 9 | }) 10 | export class PlatformComponent extends WithTiGlobal() implements OnInit { 11 | 12 | items: NavigationItem[]; 13 | 14 | routePrefix = ['controls', 'platform']; 15 | 16 | constructor(private device: DeviceEnvironment) { 17 | super(); 18 | } 19 | 20 | ngOnInit() { 21 | if (this.device.runs('android')) { 22 | this.items = [ 23 | { 24 | id: 'card-view', 25 | icon: 'square', 26 | title: 'Card View' 27 | }, 28 | { 29 | id: 'progress-indicator', 30 | icon: 'spinner', 31 | title: 'Progress Indicator' 32 | }, 33 | { 34 | id: 'search-view', 35 | icon: 'search', 36 | title: 'Search View' 37 | } 38 | ] 39 | } else { 40 | this.items = [ 41 | { 42 | id: 'blur-view', 43 | icon: 'magic', 44 | title: 'Blur View' 45 | }, { 46 | id: 'live-photo', 47 | icon: 'image', 48 | title: 'Live Photo' 49 | }, { 50 | id: 'stepper', 51 | icon: 'sort', 52 | title: 'Stepper' 53 | } 54 | ] 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/TitaniumModule.ts: -------------------------------------------------------------------------------- 1 | // Explicitly require this early, otherwise Angular DI doens't work properly 2 | import 'reflect-metadata'; 3 | import 'zone.js'; 4 | import { 5 | ApplicationModule, 6 | ErrorHandler, 7 | NO_ERRORS_SCHEMA, 8 | NgModule, 9 | RendererFactory2, 10 | SystemJsNgModuleLoader, 11 | } from '@angular/core'; 12 | import { 13 | ViewportScroller, 14 | ɵNullViewportScroller as NullViewportScroller, 15 | } from "@angular/common"; 16 | import { TitaniumElementRegistry } from 'titanium-vdom'; 17 | 18 | import { DetachedLoaderComponent } from './common'; 19 | import { TitaniumRendererFactory } from './renderer'; 20 | import { TitaniumCommonModule } from './TitaniumCommonModule'; 21 | 22 | export function errorHandler() { 23 | return new ErrorHandler(); 24 | } 25 | 26 | @NgModule({ 27 | declarations: [ 28 | DetachedLoaderComponent 29 | ], 30 | providers: [ 31 | SystemJsNgModuleLoader, 32 | { provide: ErrorHandler, useFactory: errorHandler, deps: [] }, 33 | TitaniumRendererFactory, 34 | { provide: RendererFactory2, useExisting: TitaniumRendererFactory }, 35 | { provide: ViewportScroller, useClass: NullViewportScroller }, 36 | ], 37 | entryComponents: [ 38 | DetachedLoaderComponent 39 | ], 40 | imports: [ 41 | ApplicationModule, 42 | TitaniumCommonModule 43 | ], 44 | exports: [ 45 | ApplicationModule, 46 | TitaniumCommonModule, 47 | DetachedLoaderComponent 48 | ], 49 | schemas: [NO_ERRORS_SCHEMA] 50 | }) 51 | export class TitaniumModule { 52 | } -------------------------------------------------------------------------------- /src/common/EmulatedPathLocationStrategy.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LocationChangeListener, 3 | LocationStrategy, 4 | PlatformLocation 5 | } from '@angular/common'; 6 | import { Injectable } from '@angular/core'; 7 | 8 | import { HistoryStack } from './HistoryStack'; 9 | 10 | /** 11 | * A path location strategy that copies most of it's behavior from Angular's 12 | * PathLocationStrategy but doesn't require a base url since we don't have 13 | * that in our JS context. 14 | */ 15 | @Injectable() 16 | export class EmulatedPathLocationStrategy extends LocationStrategy { 17 | 18 | constructor(private _platformLocation: PlatformLocation) { 19 | super(); 20 | } 21 | 22 | path(includeHash?: boolean): string { 23 | return this._platformLocation.pathname; 24 | } 25 | 26 | prepareExternalUrl(internal: string): string { 27 | return internal; 28 | } 29 | 30 | pushState(state: any, title: string, url: string, queryParams: string): void { 31 | this._platformLocation.pushState(state, title, url); 32 | } 33 | 34 | replaceState(state: any, title: string, url: string, queryParams: string): void { 35 | this._platformLocation.replaceState(state, title, url); 36 | } 37 | 38 | forward(): void { 39 | throw new Error('Using forward() is not supported by the Titanium platform'); 40 | } 41 | 42 | back(): void { 43 | this._platformLocation.back(); 44 | } 45 | 46 | onPopState(fn: LocationChangeListener): void { 47 | this._platformLocation.onPopState(fn); 48 | } 49 | 50 | getBaseHref(): string { 51 | return ''; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/router/TitaniumRouter.ts: -------------------------------------------------------------------------------- 1 | import { Location } from '@angular/common'; 2 | import { Injectable } from '@angular/core'; 3 | import { Router, NavigationExtras, UrlTree } from '@angular/router'; 4 | import { NavigationManager } from 'titanium-navigator'; 5 | 6 | import { NavigationOptions } from './NavigationOptions'; 7 | 8 | export type TitaniumNavigationOptions = NavigationOptions & NavigationExtras; 9 | 10 | /** 11 | * A proxy around the Angular router to inject navigation options into our 12 | * navigation manager before delegating back to the original router. 13 | */ 14 | @Injectable() 15 | export class TitaniumRouter { 16 | 17 | private router: Router; 18 | 19 | private navigationManager: NavigationManager; 20 | 21 | private location: Location; 22 | 23 | constructor(router: Router, navigationManager: NavigationManager, location: Location) { 24 | this.router = router; 25 | this.navigationManager = navigationManager; 26 | this.location = location; 27 | } 28 | 29 | public navigate(commands: any[], options?: TitaniumNavigationOptions): Promise { 30 | if (options) { 31 | this.navigationManager.currentNavigationOptions = options; 32 | } 33 | return this.router.navigate(commands, options); 34 | } 35 | 36 | public navigateByUrl(url: string | UrlTree, options?: TitaniumNavigationOptions): Promise { 37 | if (options) { 38 | this.navigationManager.currentNavigationOptions = options; 39 | } 40 | return this.router.navigateByUrl(url, options); 41 | } 42 | 43 | public back() { 44 | this.location.back(); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/input/inputs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | import { InputDemo } from './input-demo.component'; 4 | 5 | @Component({ 6 | templateUrl: 'inputs.component.html' 7 | }) 8 | export class InputsComponent extends WithTiGlobal() { 9 | @ViewChild('switchDemo') switchDemo: InputDemo; 10 | @ViewChild('sliderDemo') sliderDemo: InputDemo; 11 | @ViewChild('buttonDemo') buttonDemo: InputDemo; 12 | @ViewChild('textFieldDemo') textFieldDemo: InputDemo; 13 | @ViewChild('textAreaDemo') textAreaDemo: InputDemo; 14 | 15 | private _switchValue = 0; 16 | private _sliderValue = 25; 17 | private _pickerValue = new Date(); 18 | private _textFieldValue = ''; 19 | 20 | get switchValue() { return this._switchValue; } 21 | set switchValue(newValue) { 22 | this._switchValue = newValue; 23 | if (this.switchDemo) { 24 | this.switchDemo.state = `Switch is now ${newValue ? 'On' : 'Off'}`; 25 | } 26 | } 27 | get sliderValue() { return this._sliderValue; } 28 | set sliderValue(newValue) { 29 | this._sliderValue = newValue; 30 | if (this.sliderDemo) { 31 | this.sliderDemo.state = `Slider value: ${newValue.toFixed(0)}`; 32 | } 33 | } 34 | get textFieldValue() { return this._textFieldValue; } 35 | set textFieldValue(newValue) { 36 | this._textFieldValue = newValue; 37 | if (this.textFieldDemo) { 38 | this.textFieldDemo.state = newValue !== '' ? `Text: ${newValue}` : ''; 39 | } 40 | } 41 | public textAreaValue = ''; 42 | 43 | onSubmit() { 44 | this.buttonDemo.state = 'Nicely done!' 45 | } 46 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/utility-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes } from '@angular/router'; 3 | import { TitaniumRouterModule } from 'titanium-angular'; 4 | 5 | import { UtilityViews } from './utility-views.component'; 6 | import { ButtonBarComponent } from './button-bar/button-bar.component'; 7 | import { MaskedImageComponent } from './masked-image/masked-image.component'; 8 | import { ProgressIndicatorsComponent } from './progress-indicators/progress-indicators.component'; 9 | import { SearchBarComponent } from './search-bar/search-bar.component'; 10 | import { RefreshControlComponent } from './refresh-control/refresh-control.component'; 11 | import { ToolbarComponent } from './toolbar/toolbar.component'; 12 | 13 | const utilityRoutes: Routes = [ 14 | { 15 | path: 'controls', 16 | children: [ 17 | { 18 | path: 'utility', 19 | children: [ 20 | { path: '', component: UtilityViews }, 21 | { path: 'button-bar', component: ButtonBarComponent }, 22 | { path: 'masked-image', component: MaskedImageComponent }, 23 | { path: 'progress-indicators', component: ProgressIndicatorsComponent }, 24 | { path: 'refresh-control', component: RefreshControlComponent }, 25 | { path: 'search-bar', component: SearchBarComponent }, 26 | { path: 'toolbar', component: ToolbarComponent } 27 | ] 28 | } 29 | ] 30 | } 31 | ]; 32 | 33 | @NgModule({ 34 | imports: [ 35 | TitaniumRouterModule.forChild(utilityRoutes) 36 | ], 37 | exports: [ 38 | TitaniumRouterModule 39 | ] 40 | }) 41 | export class UtilityRoutingModule { } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/scroll-view/scroll-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core' 2 | import { WithTiGlobal } from 'titanium-angular' 3 | 4 | interface Post { 5 | id: number, 6 | user: { 7 | username: string 8 | }, 9 | date: string, 10 | likes: number 11 | liked: boolean 12 | comments: number 13 | } 14 | 15 | @Component({ 16 | templateUrl: './scroll-view.component.html' 17 | }) 18 | export class ScrollViewComponent extends WithTiGlobal() { 19 | posts: Post[] 20 | 21 | ngOnInit() { 22 | this.posts = [{ 23 | id: 1, 24 | user: { 25 | username: 'Jon' 26 | }, 27 | date: 'a few minutes ago', 28 | likes: 3, 29 | liked: false, 30 | comments: 1 31 | }, { 32 | id: 2, 33 | user: { 34 | username: 'Ellen' 35 | }, 36 | date: '24 minutes ago', 37 | likes: 509, 38 | liked: true, 39 | comments: 20 40 | }, { 41 | id: 3, 42 | user: { 43 | username: 'Max' 44 | }, 45 | date: '3 hours ago', 46 | likes: 3409, 47 | liked: false, 48 | comments: 72 49 | }] 50 | } 51 | 52 | toggleLike(post: Post) { 53 | if (post.liked) { 54 | post.liked = false; 55 | post.likes -= 1; 56 | } else { 57 | post.liked = true; 58 | post.likes += 1; 59 | } 60 | 61 | // Here would be a good place to update your remote data source 62 | } 63 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/intro/intro.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | Component, 4 | ElementRef, 5 | OnInit, 6 | ViewChild, 7 | VERSION 8 | } from '@angular/core'; 9 | import { DeviceEnvironment } from 'titanium-angular'; 10 | 11 | @Component({ 12 | templateUrl: 'intro.component.html' 13 | }) 14 | export class IntroComponent implements AfterViewInit, OnInit { 15 | 16 | @ViewChild('window', { static: false }) windowElement: ElementRef; 17 | 18 | @ViewChild('gradient', { static: false }) gradientElement: ElementRef; 19 | 20 | @ViewChild('imageView', { static: false }) imageViewElement: ElementRef; 21 | 22 | skyGradient: Gradient; 23 | 24 | angularVersion = VERSION.full 25 | 26 | constructor(private device: DeviceEnvironment) { 27 | 28 | } 29 | 30 | ngOnInit() { 31 | this.skyGradient = { 32 | type: 'linear', 33 | startPoint: { x: '0%', y: '50%' }, 34 | endPoint: { x: '100%', y: '50%' }, 35 | colors: [ 36 | { color: '#0d47a1', offset: 0.0 }, 37 | { color: '#42a5f5', offset: 1.0 } 38 | ] 39 | }; 40 | } 41 | 42 | ngAfterViewInit() { 43 | if (this.device.runs('android')) { 44 | const window = this.windowElement.nativeElement.titaniumView; 45 | window.activity.onStart = () => { 46 | window.activity.actionBar.hide(); 47 | } 48 | } 49 | 50 | const gradientView: Titanium.UI.View = this.gradientElement.nativeElement.titaniumView; 51 | gradientView.transform = Titanium.UI.createMatrix2D({rotate: 8}); 52 | } 53 | 54 | onClick() { 55 | console.log('button onclick'); 56 | } 57 | } -------------------------------------------------------------------------------- /src/platform/providers.ts: -------------------------------------------------------------------------------- 1 | import { ElementSchemaRegistry, ResourceLoader } from '@angular/compiler'; 2 | import { 3 | COMPILER_OPTIONS, 4 | PLATFORM_INITIALIZER, 5 | Sanitizer, 6 | } from '@angular/core'; 7 | import { registerTitaniumElements, TitaniumElementRegistry } from 'titanium-vdom'; 8 | 9 | import { TitaniumSanitizer } from '../core/TitaniumSanitizer'; 10 | import { FileSystemResourceLoader, TitaniumElementSchemaRegistry } from '../compiler'; 11 | import { Logger } from '../log'; 12 | import { DeviceEnvironment } from '../services'; 13 | import { TitaniumDomAdapter } from './TitaniumDomAdapter' 14 | 15 | export function initDomAdapter() { 16 | TitaniumDomAdapter.makeCurrent(); 17 | } 18 | 19 | export function createElementRegistry(): TitaniumElementRegistry { 20 | const registry = TitaniumElementRegistry.getInstance(); 21 | registry.defaultViewMetadata = { 22 | detached: false 23 | }; 24 | registerTitaniumElements(registry); 25 | return registry; 26 | } 27 | 28 | export const COMMON_PROVIDERS = [ 29 | { provide: Logger, useClass: Logger, deps: [] }, 30 | { provide: DeviceEnvironment, useClass: DeviceEnvironment, deps: [] }, 31 | { provide: TitaniumElementRegistry, useFactory: createElementRegistry, deps: []}, 32 | { provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true }, 33 | { provide: Sanitizer, useClass: TitaniumSanitizer, deps: [] } 34 | ]; 35 | 36 | export const TITANIUM_COMPILER_PROVIDERS = [ 37 | { 38 | provide: COMPILER_OPTIONS, 39 | useValue: { 40 | providers: [ 41 | { provide: ResourceLoader, useClass: FileSystemResourceLoader, deps: [] }, 42 | { provide: ElementSchemaRegistry, useClass: TitaniumElementSchemaRegistry, deps: [] }, 43 | ] 44 | }, 45 | multi: true 46 | }, 47 | ]; -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/scroll-view/scroll-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/directives/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AlertDialogDirective, 3 | DialogActionDirective 4 | } from './dialog'; 5 | import { 6 | HorizontalLayoutComponent, 7 | VerticalLayoutComponent 8 | } from './layout'; 9 | import { 10 | ListViewComponent, 11 | ListItemDirective, 12 | ListSectionDirective, 13 | ListItemTemplateDirective 14 | } from './list-view'; 15 | import { 16 | PickerDirective, 17 | PickerColumnDirective, 18 | PickerRowDirective 19 | } from './picker'; 20 | import { 21 | PlatformFilterDirective 22 | } from './platform'; 23 | import { RefreshControlDirective } from './refresh-control'; 24 | import { 25 | ScrollableViewDirective 26 | } from './scrollable-view'; 27 | import { 28 | TabGroupDirective, 29 | TabDirective 30 | } from './tab-group'; 31 | import { 32 | TableViewDirective, 33 | TableViewRowDirective, 34 | TableViewSectionDirective 35 | } from './table-view'; 36 | import { 37 | ToolbarComponent 38 | } from './toolbar'; 39 | 40 | export * from './dialog'; 41 | export * from './layout'; 42 | export * from './list-view'; 43 | export * from './picker'; 44 | export * from './platform'; 45 | export * from './refresh-control'; 46 | export * from './scrollable-view'; 47 | export * from './tab-group'; 48 | export * from './table-view'; 49 | export * from './toolbar'; 50 | 51 | export const TITANIUM_DIRECTIVES = [ 52 | AlertDialogDirective, 53 | DialogActionDirective, 54 | HorizontalLayoutComponent, 55 | ListViewComponent, 56 | ListItemDirective, 57 | ListSectionDirective, 58 | ListItemTemplateDirective, 59 | PickerDirective, 60 | PickerColumnDirective, 61 | PickerRowDirective, 62 | PlatformFilterDirective, 63 | RefreshControlDirective, 64 | ScrollableViewDirective, 65 | TabGroupDirective, 66 | TabDirective, 67 | TableViewDirective, 68 | TableViewRowDirective, 69 | TableViewSectionDirective, 70 | ToolbarComponent, 71 | VerticalLayoutComponent 72 | ]; -------------------------------------------------------------------------------- /src/directives/table-view/table-view-section.ts: -------------------------------------------------------------------------------- 1 | import { Component,ElementRef, ViewChild } from "@angular/core"; 2 | import { findSingleVisualElementNoThrow, TitaniumElement } from 'titanium-vdom' 3 | 4 | import { TableViewDataSource } from './table-view'; 5 | 6 | @Component({ 7 | selector: 'table-view-section,TableViewSection', 8 | template: ` 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | `, 17 | providers: [{ provide: TableViewDataSource, useExisting: TableViewSectionDirective }] 18 | }) 19 | export class TableViewSectionDirective extends TableViewDataSource { 20 | private element: TitaniumElement; 21 | 22 | @ViewChild('headerViewContainer', { read: ElementRef, static: false }) headerViewRef: ElementRef 23 | 24 | @ViewChild('footerViewContainer', { read: ElementRef, static: false }) footerViewRef: ElementRef 25 | 26 | get titaniumView(): Titanium.UI.TableViewSection { 27 | return this.element.titaniumView; 28 | } 29 | 30 | constructor(el: ElementRef) { 31 | super(); 32 | 33 | this.element = el.nativeElement; 34 | } 35 | 36 | ngAfterViewInit() { 37 | const headerElement = findSingleVisualElementNoThrow(this.headerViewRef.nativeElement); 38 | const footerElement = findSingleVisualElementNoThrow(this.footerViewRef.nativeElement); 39 | if (headerElement) { 40 | this.titaniumView.headerView = headerElement.titaniumView; 41 | } 42 | if (footerElement) { 43 | this.titaniumView.footerView = footerElement.titaniumView; 44 | } 45 | } 46 | 47 | get dataSource(): Titanium.UI.TableViewSection { 48 | return this.element.titaniumView; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/facades/dialog/BaseDialog.ts: -------------------------------------------------------------------------------- 1 | import { AbstractDialog } from './AbstractDialog' 2 | 3 | export class BaseDialog extends AbstractDialog { 4 | 5 | title: string; 6 | 7 | message: string; 8 | 9 | private _alertDialog: Ti.UI.AlertDialog; 10 | 11 | constructor(title: string, message: string) { 12 | super(); 13 | 14 | this.title = title; 15 | this.message = message; 16 | this._alertDialog = Ti.UI.createAlertDialog({ 17 | title: this.title, 18 | message: this.message 19 | }); 20 | this._alertDialog.addEventListener('click', event => this.handleButtonClick(event)); 21 | } 22 | 23 | get titaniumView(): any { 24 | return this._alertDialog; 25 | } 26 | 27 | /** 28 | * @todo set view from template 29 | */ 30 | set androidView(androidView: Titanium.UI.View) { 31 | this._alertDialog.androidView = androidView; 32 | } 33 | 34 | set style(iosAlertStyle: number) { 35 | this._alertDialog.style = iosAlertStyle; 36 | } 37 | 38 | show(): void { 39 | const buttonNames = []; 40 | this._actions.forEach((action, index) => { 41 | buttonNames.push(action.title); 42 | if (action.isCancelAction) { 43 | this._alertDialog.cancel = index; 44 | } 45 | if (action.isDestructiveAction) { 46 | this._alertDialog.destructive = index; 47 | } 48 | }); 49 | if (buttonNames.length > 0) { 50 | this._alertDialog.buttonNames = buttonNames; 51 | } 52 | this._alertDialog.show(); 53 | } 54 | 55 | private handleButtonClick(event: any) { 56 | if (this._actions.length === 0) { 57 | return; 58 | } 59 | 60 | const targetAction = this._actions[event.index]; 61 | if (targetAction) { 62 | targetAction.handler(event); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/directives/dialog.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | Component, 4 | ContentChildren, 5 | Directive, 6 | EventEmitter, 7 | Input, 8 | OnInit, 9 | Output, 10 | QueryList 11 | } from '@angular/core'; 12 | 13 | import { BaseDialog, DialogAction } from '../facades/dialog'; 14 | 15 | @Directive({ 16 | selector: 'dialog-action,DialogAction' 17 | }) 18 | export class DialogActionDirective implements OnInit { 19 | 20 | @Input() title = ''; 21 | 22 | @Input() cancel = false; 23 | 24 | @Input() destructive = false; 25 | 26 | @Output() handler: EventEmitter = new EventEmitter(); 27 | 28 | action: DialogAction; 29 | 30 | ngOnInit() { 31 | this.action = new DialogAction(this.title, (event) => this.handler.emit(event)); 32 | this.action.cancel = this.cancel; 33 | this.action.destructive = this.destructive; 34 | } 35 | } 36 | 37 | @Component({ 38 | selector: 'alert-dialog,AlertDialog', 39 | template: ` 40 | 41 | 42 | 43 | 44 | ` 45 | }) 46 | export class AlertDialogDirective implements OnInit, AfterContentInit { 47 | 48 | private _title: string; 49 | 50 | private _message: string; 51 | 52 | private dialog: BaseDialog; 53 | 54 | @ContentChildren(DialogActionDirective) buttons: QueryList; 55 | 56 | @Input() 57 | set title(title: string) { 58 | this._title = title; 59 | } 60 | 61 | @Input() 62 | set message(message: string) { 63 | this._message = message; 64 | } 65 | 66 | show(): void { 67 | this.dialog.show(); 68 | } 69 | 70 | ngOnInit() { 71 | this.dialog = new BaseDialog(this._title, this._message); 72 | } 73 | 74 | ngAfterContentInit() { 75 | this.buttons.forEach(dialogAction => this.dialog.addAction(dialogAction.action)); 76 | } 77 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

Titanium Angular

4 | 5 | Use [Angular 9](https://angular.io/) to easily create native mobile apps with Axway Appcelerator Titanium. 6 | 7 | > ⚠️ This platform is currently in an early beta stage. Expect things to be broken or APIs to change as this project matures. DO NOT USE IN PRODUCTION! 8 | 9 | ## Requirements 10 | 11 | - [x] iOS or Android SDK installed 12 | - [x] Latest Titanium SDK master (install via `appc ti sdk install -b master`) 13 | 14 | ## Getting started 15 | 16 | You can checkout how Titanium Angular works by taking a look at the bundled example application. To get started, first prepare all bundled packages by installing their dependencies with `npm run bootstrap`. After that, go to the example project in `ti-angular-example`. You can now build and run the app with `appc run -p [android|ios]`. 17 | 18 | You can find more details in the [Titanium Angular Guide](https://docs.appcelerator.com/platform/latest/#!/guide/Titanium_and_Angular). 19 | 20 | ## Development Guide 21 | 22 | Clone the repo and run the following commands to start developing. 23 | 24 | ```bash 25 | # Install deps 26 | npm i 27 | 28 | # Compile and watch for changes 29 | npm run dev 30 | ``` 31 | 32 | ## Contributions 33 | 34 | Open source contributions are greatly appreciated! If you have a bugfix, improvement or new feature, please create 35 | [an issue](https://github.com/appcelerator/titanium-angular/issues/new) first and submit a [pull request](https://github.com/appcelerator/titanium-angular/pulls/new) against master. 36 | 37 | ## Getting Help 38 | 39 | If you have questions about the Angular platform on Titanium, feel free to reach out on Stackoverflow or the 40 | `#titanium-angular` channel on [TiSlack](http://tislack.org). In case you find a bug, create a [new issue](/issues/new) 41 | or open a [new JIRA ticket](https://jira.appcelerator.org). 42 | 43 | ## License 44 | 45 | Apache 2 46 | -------------------------------------------------------------------------------- /ti-angular-example/src/shared/components/icon.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | const fontFamilyMap = { 4 | brands: 'FontAwesome5BrandsRegular', 5 | regular: 'FontAwesome5FreeRegular', 6 | solid: 'FontAwesome5FreeSolid' 7 | }; 8 | 9 | const iconMap = { 10 | brands: { 11 | 'android': 'f17b', 12 | 'apple': 'f179', 13 | 'chrome': 'f268', 14 | 'safari': 'f267', 15 | 'vue': 'f41f' 16 | }, 17 | regular: { 18 | 19 | }, 20 | solid: { 21 | 'angle-double-left': 'f100', 22 | 'arrow-down': 'f063', 23 | 'arrows-alt-v': 'f338', 24 | 'clipboard-list': 'f46d', 25 | 'clone': 'f24d', 26 | 'code': 'f121', 27 | 'cogs': 'f085', 28 | 'comment-alt': 'f27a', 29 | 'elipsis-h': 'f141', 30 | 'exchange-alt': 'f362', 31 | 'fill-drip': 'f576', 32 | 'fire': 'f06d', 33 | 'flask': 'f0c3', 34 | 'grip-lines': 'f7a4', 35 | 'heart': 'f004', 36 | 'image': 'f03e', 37 | 'layer': 'f5fd', 38 | 'list': 'f03a', 39 | 'list-alt': 'f022', 40 | 'long-arrow-alt-down': 'f309', 41 | 'long-arrow-alt-up': 'f30c', 42 | 'magic': 'f0d0', 43 | 'microphone': 'f130', 44 | 'minus': 'f068', 45 | 'route': 'f4d7', 46 | 'search': 'f002', 47 | 'sort': 'f0dc', 48 | 'spinner': 'f110', 49 | 'sync-alt': 'f2f1', 50 | 'thumbs-up': 'f164', 51 | 'user-circle': 'f2bd' 52 | } 53 | }; 54 | 55 | @Component({ 56 | selector: 'fa-icon', 57 | template: `` 58 | }) 59 | export class FontAwesomeIcon { 60 | @Input() icon = ''; 61 | 62 | @Input() iconStyle = 'solid'; 63 | 64 | @Input() fontSize?: number; 65 | 66 | get font(): Font { 67 | return { 68 | fontFamily: fontFamilyMap[this.iconStyle], 69 | fontSize: this.fontSize 70 | } 71 | } 72 | 73 | get unicodeValue() { 74 | if (!iconMap[this.iconStyle] || !iconMap[this.iconStyle][this.icon]) { 75 | // fallback to font ligatures 76 | return this.icon; 77 | } 78 | 79 | return String.fromCharCode(parseInt(iconMap[this.iconStyle][this.icon], 16)); 80 | } 81 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/progress-indicators/progress-indicators.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; 2 | import { WithTiGlobal } from 'titanium-angular'; 3 | 4 | @Component({ 5 | templateUrl: 'progress-indicators.component.html' 6 | }) 7 | export class ProgressIndicatorsComponent extends WithTiGlobal() implements OnInit { 8 | bytesLoaded = 0 9 | 10 | bytesTotal = 33458326 11 | 12 | downloadState = 'Idle' 13 | 14 | private updateTimeout?: number; 15 | 16 | private downloadInterval?: number; 17 | 18 | private _activityIndicator!: Ti.UI.ActivityIndicator 19 | 20 | private _progressBar!: Ti.UI.ProgressBar 21 | 22 | @ViewChild('activityIndicator') 23 | set activityIndicator(activityRef: ElementRef) { 24 | this._activityIndicator = activityRef.nativeElement.titaniumView 25 | } 26 | 27 | @ViewChild('progressBar') 28 | set progressBar(barRef: ElementRef) { 29 | this._progressBar = barRef.nativeElement.titaniumView 30 | } 31 | 32 | ngOnInit() { } 33 | 34 | checkForUpdates() { 35 | if (this.updateTimeout) { 36 | return; 37 | } 38 | 39 | this._activityIndicator.show(); 40 | // Do some unquantifiable task 41 | this.updateTimeout = setTimeout(() => { 42 | this._activityIndicator.hide(); 43 | clearTimeout(this.updateTimeout); 44 | }, 3000); 45 | } 46 | 47 | downloadStuff() { 48 | if (this.downloadInterval) { 49 | return; 50 | } 51 | 52 | // Do your task with known duration or which is otherwise quantifiable 53 | this.bytesLoaded = 0; 54 | this.downloadState = 'Downloading'; 55 | this.downloadInterval = setInterval(() => { 56 | const incomingBytes = 200000; 57 | if (this.bytesLoaded + incomingBytes < this.bytesTotal) { 58 | this.bytesLoaded += incomingBytes; 59 | } else { 60 | this.bytesLoaded = this.bytesTotal; 61 | this.downloadState = 'Done'; 62 | clearInterval(this.downloadInterval); 63 | this.downloadInterval = null; 64 | } 65 | this._progressBar.value = this.bytesLoaded / this.bytesTotal * 100; 66 | }, 50); 67 | } 68 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "titanium-angular", 3 | "version": "0.2.0", 4 | "description": "Use the Titanium platform with Angular", 5 | "main": "dist/index", 6 | "typings": "dist/index", 7 | "scripts": { 8 | "test": "echo 'No tests specified'", 9 | "tsc": "tsc -p tsconfig.json", 10 | "clean": "rimraf ./dist", 11 | "build": "npm run clean && npm run syncd && ngc", 12 | "syncd": "cpx src/**/*.d.ts dist/", 13 | "dev": "npm run clean && npm run syncd && ngc --watch", 14 | "prepare": "npm run build", 15 | "bootstrap": "npm i && cd ti-angular-example/app && npm i", 16 | "ng-build": "ng build" 17 | }, 18 | "author": "Axway Appcelerator", 19 | "license": "Apache-2.0", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/appcelerator/titanium-angular.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/appcelerator/titanium-angular/issues" 26 | }, 27 | "files": [ 28 | "dist" 29 | ], 30 | "dependencies": { 31 | "titanium-navigator": "^0.3.1", 32 | "titanium-vdom": "^0.4.4" 33 | }, 34 | "peerDependencies": { 35 | "@angular/common": "~9.1.0", 36 | "@angular/compiler": "~9.1.0", 37 | "@angular/core": "~9.1.0", 38 | "@angular/platform-browser": "~9.1.0", 39 | "@angular/platform-browser-dynamic": "~9.1.0", 40 | "@angular/router": "~9.1.0", 41 | "reflect-metadata": "^0.1.12", 42 | "rxjs": "^6.5.4", 43 | "zone.js": "^0.10.3" 44 | }, 45 | "devDependencies": { 46 | "@angular/cli": "~9.1.0", 47 | "@angular/common": "~9.1.0", 48 | "@angular/compiler": "~9.1.0", 49 | "@angular/compiler-cli": "~9.1.0", 50 | "@angular/core": "~9.1.0", 51 | "@angular/language-service": "~9.1.0", 52 | "@angular/platform-browser": "~9.1.0", 53 | "@angular/platform-browser-dynamic": "~9.1.0", 54 | "@angular/router": "~9.1.0", 55 | "@types/titanium": "^9.0.0", 56 | "cpx": "^1.5.0", 57 | "reflect-metadata": "^0.1.12", 58 | "rimraf": "^2.6.2", 59 | "rxjs": "^6.5.4", 60 | "ts-node": "~7.0.0", 61 | "tsickle": "^0.38.1", 62 | "tslint": "~5.15.0", 63 | "typescript": "~3.8.3", 64 | "zone.js": "^0.10.3" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/dialogs/dialogs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core' 2 | import { 3 | AlertDialog, 4 | AlertDialogDirective, 5 | ConfirmDialog, 6 | ConfirmResult, 7 | WithTiGlobal, 8 | } from 'titanium-angular'; 9 | 10 | // @todo: replace with dialogs from titanized? 11 | 12 | @Component({ 13 | templateUrl: './dialogs.component.html' 14 | }) 15 | export class DialogsComponent extends WithTiGlobal() { 16 | 17 | @ViewChild('templateAlertDialog') templateAlertDialog: AlertDialogDirective; 18 | 19 | openAlertDialog() { 20 | const alertDialog = new AlertDialog({ 21 | title: 'Alert', 22 | message: 'This is awesome!' 23 | }); 24 | alertDialog.show().then(() => console.log(`Alert closed`)); 25 | } 26 | 27 | openConfirmDialog() { 28 | const confirmDialog = new ConfirmDialog({ 29 | title: 'Confirm', 30 | message: 'Do you want to continue?' 31 | }); 32 | confirmDialog.show().then((result: ConfirmResult) => { 33 | if (result === ConfirmResult.Ok) { 34 | console.log('Confirmed!'); 35 | } else if (result === ConfirmResult.Cancel) { 36 | console.log('Canceled!'); 37 | } 38 | }); 39 | } 40 | 41 | openPromptDialog() { 42 | const dialog = new AlertDialog({ 43 | title: 'Oh no!', 44 | message: 'Not implemented yet, check back later!' 45 | }); 46 | dialog.show(); 47 | } 48 | 49 | openCustomDialog() { 50 | const dialog = new AlertDialog({ 51 | title: 'Oh no!', 52 | message: 'Not implemented yet, check back later!' 53 | }); 54 | dialog.show(); 55 | } 56 | 57 | openTemplateAlertDialog() { 58 | this.templateAlertDialog.show(); 59 | } 60 | 61 | handleOkButton(event: any) { 62 | console.log('Yep, coll stuff indeed!', event.index, event.cancel); 63 | } 64 | 65 | handleCancelButton(event: any) { 66 | console.log('You don\'t seem to be impressed, bummer.', event.index, event.cancel); 67 | } 68 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/progress-indicators/progress-indicators.component.html: -------------------------------------------------------------------------------- 1 | 5 | 10 | 14 | Activity Indicator 15 | 16 | 22 | 26 | Check for updates! 27 | 28 | 29 | 33 | 37 | Progress Bar 38 | 39 | 46 | 47 | 51 | Some file to download 52 | 53 | 58 | {{ downloadState }} 59 | 60 | 61 | 70 | 76 | {{ bytesLoaded | formatBytes }} of {{ bytesTotal | formatBytes }} 77 | 78 | 79 | 84 | Start download 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/controls.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { TitaniumCommonModule } from 'titanium-angular'; 3 | 4 | import { ControlsComponent } from './controls.component'; 5 | import { ControlsRoutingModule } from './controls-routing.module'; 6 | 7 | import { DialogsComponent } from './components/dialogs/dialogs.component'; 8 | import { ImageViewComponent } from './components/views/image-view/image-view.component'; 9 | import { ItemComponent } from './components/views/list-view/item.component'; 10 | import { ListViewComponent } from './components/views/list-view/list-view.component'; 11 | import { ScrollViewComponent } from './components/views/scroll-view/scroll-view.component'; 12 | import { ScrollableViewComponent } from './components/views/scrollable-view/scrollable-view.component'; 13 | import { TableViewComponent } from './components/views/table-view/table-view.component'; 14 | import { ViewsComponent } from './components/views/views.component'; 15 | import { ViewComponent } from './components/views/view/view.component'; 16 | import { WebViewComponent } from './components/views/web-view/web-view.component'; 17 | 18 | import { InputModule } from './components/input/input.module'; 19 | import { PlatformModule } from './components/platform/platform.module'; 20 | import { SharedModule } from '@/shared/shared.module'; 21 | import { UtilityModule } from './components/utility/utility.module'; 22 | 23 | @NgModule({ 24 | imports: [ 25 | TitaniumCommonModule, 26 | ControlsRoutingModule, 27 | InputModule, 28 | PlatformModule, 29 | SharedModule, 30 | UtilityModule 31 | ], 32 | declarations: [ 33 | DialogsComponent, 34 | ControlsComponent, 35 | ImageViewComponent, 36 | ItemComponent, 37 | ListViewComponent, 38 | ScrollViewComponent, 39 | ScrollableViewComponent, 40 | TableViewComponent, 41 | ViewComponent, 42 | ViewsComponent, 43 | WebViewComponent 44 | ], 45 | exports: [ 46 | ControlsComponent 47 | ], 48 | schemas: [NO_ERRORS_SCHEMA] 49 | }) 50 | export class ControlsModule { } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/list-view/list-view.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 35 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/controls-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes } from '@angular/router'; 3 | import { TitaniumRouterModule } from 'titanium-angular'; 4 | 5 | import { DialogsComponent } from './components/dialogs/dialogs.component'; 6 | import { InputsComponent } from './components/input/inputs.component'; 7 | import { ImageViewComponent } from './components/views/image-view/image-view.component'; 8 | import { ListViewComponent } from './components/views/list-view/list-view.component'; 9 | import { ScrollViewComponent } from './components/views/scroll-view/scroll-view.component'; 10 | import { ScrollableViewComponent } from './components/views/scrollable-view/scrollable-view.component'; 11 | import { TableViewComponent } from './components/views/table-view/table-view.component'; 12 | import { ViewsComponent } from './components/views/views.component'; 13 | import { ViewComponent } from './components/views/view/view.component'; 14 | import { WebViewComponent } from './components/views/web-view/web-view.component'; 15 | 16 | const controlsRoutes: Routes = [ 17 | { 18 | path: 'controls', 19 | children: [ 20 | { 21 | path: 'views', 22 | children: [ 23 | { path: '', component: ViewsComponent }, 24 | { path: 'image-view', component: ImageViewComponent }, 25 | { path: 'list-view', component: ListViewComponent }, 26 | { path: 'table-view', component: TableViewComponent }, 27 | { path: 'scroll-view', component: ScrollViewComponent }, 28 | { path: 'scrollable-view', component: ScrollableViewComponent }, 29 | { path: 'view', component: ViewComponent }, 30 | { path: 'web-view', component: WebViewComponent } 31 | ] 32 | }, 33 | { path: 'inputs', component: InputsComponent }, 34 | { path: 'dialogs', component: DialogsComponent } 35 | ] 36 | } 37 | ]; 38 | 39 | @NgModule({ 40 | imports: [ 41 | TitaniumRouterModule.forChild(controlsRoutes) 42 | ], 43 | exports: [ 44 | TitaniumRouterModule 45 | ] 46 | }) 47 | export class ControlsRoutingModule { } -------------------------------------------------------------------------------- /src/common/TitaniumPlatformLocation.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LocationChangeListener, 3 | PlatformLocation 4 | } from '@angular/common'; 5 | import { Injectable } from '@angular/core'; 6 | import { NavigationManager } from 'titanium-navigator'; 7 | 8 | import { HistoryStack } from './HistoryStack'; 9 | 10 | /** 11 | * 12 | */ 13 | @Injectable() 14 | export class TitaniumPlatformLocation extends PlatformLocation { 15 | 16 | private _history: HistoryStack; 17 | 18 | private navigationManager: NavigationManager; 19 | 20 | constructor(history: HistoryStack, navigationManager: NavigationManager) { 21 | super(); 22 | 23 | this._history = history; 24 | this.navigationManager = navigationManager; 25 | } 26 | 27 | getBaseHrefFromDOM(): string { 28 | return ''; 29 | } 30 | 31 | onPopState(fn: LocationChangeListener): void { 32 | this._history.onPopState(fn); 33 | } 34 | 35 | onHashChange(fn: LocationChangeListener): void { 36 | console.log('TitaniumPlatformLocation.onHashChange - not implemented'); 37 | } 38 | 39 | get protocol(): string { 40 | return 'http'; 41 | } 42 | 43 | get hostname(): string { 44 | return 'localhost' 45 | } 46 | 47 | get href(): string { 48 | return ''; 49 | } 50 | 51 | get port(): string { 52 | return '80'; 53 | } 54 | 55 | get pathname(): string { 56 | const state = this._history.state; 57 | const path = state ? state.url : '/'; 58 | return path; 59 | } 60 | 61 | get search(): string { 62 | return ''; 63 | } 64 | 65 | get hash(): string { 66 | return ''; 67 | } 68 | 69 | getState() { 70 | return this._history.state; 71 | } 72 | 73 | replaceState(state: any, title: string, url: string): void { 74 | this._history.replaceState(state, title, url, null); 75 | } 76 | 77 | pushState(state: any, title: string, url: string): void { 78 | this._history.pushState(state, title, url, null); 79 | } 80 | 81 | forward(): void { 82 | throw new Error('Using forward() is not supported by the Titanium platform'); 83 | } 84 | 85 | back(): void { 86 | this.navigationManager.locationBackNavigation = true; 87 | this._history.back(); 88 | } 89 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/intro/intro.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | LET'S GO 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/directives/platform.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EmbeddedViewRef, 3 | Directive, 4 | Inject, 5 | Input, 6 | TemplateRef, 7 | ViewContainerRef 8 | } from '@angular/core'; 9 | 10 | import { DeviceEnvironment } from '../services'; 11 | 12 | export class PlatformContext { 13 | public filter: string = '' 14 | } 15 | 16 | @Directive({ 17 | selector: '[platform]' 18 | }) 19 | export class PlatformFilterDirective { 20 | 21 | private _context: PlatformContext = new PlatformContext(); 22 | 23 | private _viewContainer: ViewContainerRef = null; 24 | 25 | private _templateRef: TemplateRef = null 26 | 27 | private _viewRef: EmbeddedViewRef = null; 28 | 29 | private _device: DeviceEnvironment = null; 30 | 31 | constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef, @Inject(DeviceEnvironment) device: DeviceEnvironment) { 32 | this._context = new PlatformContext(); 33 | this._viewContainer = viewContainer; 34 | this._templateRef = templateRef; 35 | this._device = device; 36 | } 37 | 38 | @Input() 39 | set platform(filter: string) { 40 | this._context.filter = filter; 41 | this.updateView(); 42 | } 43 | 44 | private updateView() { 45 | this._viewContainer.clear(); 46 | 47 | const filterType = typeof this._context.filter; 48 | if (filterType !== 'string') { 49 | throw new Error(`Expected a string as the platform filter, but received ${typeof this._context}`); 50 | } 51 | 52 | let renderOnCurrentPlatform = false; 53 | this._context.filter.split(',').forEach(platformFilterString => { 54 | let platformName = platformFilterString.trim(); 55 | 56 | if (platformName[0] === '!') { 57 | platformName = platformName.slice(1); 58 | if (!this._device.runs(platformName)) { 59 | renderOnCurrentPlatform = true; 60 | } 61 | } else { 62 | if (this._device.runs(platformName)) { 63 | renderOnCurrentPlatform = true; 64 | } 65 | } 66 | }); 67 | 68 | if (renderOnCurrentPlatform) { 69 | this._viewRef = this._viewContainer.createEmbeddedView(this._templateRef, this._context); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /ti-angular-example/tiapp.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | com.appc.angular 5 | AngularDemo 6 | 1.0.0 7 | Axway Appcelerator 8 | https://github.com/appcelerator/titanium-angular 9 | 10 | 2018-present by Axway Appcelerator 11 | appicon.png 12 | false 13 | false 14 | true 15 | 11111111-1111-1111-1111-111111111111 16 | dp 17 | true 18 | 19 | false 20 | 21 | true 22 | true 23 | 24 | 25 | UISupportedInterfaceOrientations~iphone 26 | 27 | UIInterfaceOrientationPortrait 28 | 29 | UISupportedInterfaceOrientations~ipad 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationPortraitUpsideDown 33 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | 36 | UIRequiresPersistentWiFi 37 | 38 | UIPrerenderedIcon 39 | 40 | UIStatusBarHidden 41 | 42 | UIStatusBarStyle 43 | UIStatusBarStyleLightContent 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 9.2.1.GA 57 | 58 | true 59 | false 60 | true 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/directives/list-view/list-item.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | ElementRef, 4 | forwardRef, 5 | Inject, 6 | Input, 7 | OnChanges, 8 | SimpleChanges 9 | } from '@angular/core'; 10 | import { InvisibleElement } from 'titanium-vdom'; 11 | 12 | import { ListSectionDirective } from './list-section'; 13 | 14 | /** 15 | * A list item within a list section 16 | * 17 | * Since this does not map to a Titanium proxy that would update itself 18 | * on attribute changes, we use input properties and listen for changes 19 | * on the ListItem elements in the list section directive. 20 | */ 21 | @Directive({ 22 | selector: 'list-item,ListItem' 23 | }) 24 | export class ListItemDirective implements OnChanges { 25 | public element: InvisibleElement; 26 | 27 | @Input() accessoryType: number; 28 | 29 | @Input() canEdit: boolean; 30 | 31 | @Input() canInsert: boolean; 32 | 33 | @Input() canMove: boolean; 34 | 35 | @Input() color: string; 36 | 37 | @Input() editActions: RowActionType[]; 38 | 39 | @Input() font: Font; 40 | 41 | @Input() height: number | string; 42 | 43 | @Input() image: string; 44 | 45 | @Input() itemId: boolean; 46 | 47 | @Input() searchableText: string; 48 | 49 | @Input() selectedBackgroundColor: string; 50 | 51 | @Input() selectedBackgroundGradient: Gradient; 52 | 53 | @Input() selectedBackgroundImage: string; 54 | 55 | @Input() selectedColor: string; 56 | 57 | @Input() selectedSubtitleColor: string; 58 | 59 | @Input() selectionStyle: string; 60 | 61 | @Input() subtitle: string; 62 | 63 | @Input() subtitleColor: string; 64 | 65 | @Input() template: number; 66 | 67 | @Input() title: string; 68 | 69 | private owner: ListSectionDirective; 70 | 71 | private itemProperties: object = {}; 72 | 73 | private customProperties: object = {}; 74 | 75 | constructor(el: ElementRef, @Inject(forwardRef(() => ListSectionDirective)) owner: ListSectionDirective) { 76 | this.element = el.nativeElement; 77 | this.owner = owner; 78 | } 79 | 80 | get dataItem(): ListDataItem { 81 | const dataItem: ListDataItem = { 82 | template: this.template || Titanium.UI.LIST_ITEM_TEMPLATE_DEFAULT, 83 | properties: this.itemProperties 84 | } 85 | return dataItem; 86 | } 87 | 88 | ngOnChanges(changes: SimpleChanges) { 89 | for (let changedPropertyName in changes) { 90 | if (changedPropertyName === 'template') { 91 | continue; 92 | } 93 | const change = changes[changedPropertyName]; 94 | this.itemProperties[changedPropertyName] = change.currentValue; 95 | } 96 | 97 | this.owner.updateListItem(this); 98 | } 99 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/utility/masked-image/masked-image.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; 2 | import { DeviceEnvironment } from 'titanium-angular'; 3 | 4 | const commonBlendModes = [ 5 | 'BLEND_MODE_CLEAR', 6 | 'BLEND_MODE_COPY', 7 | 'BLEND_MODE_DARKEN', 8 | 'BLEND_MODE_DESTINATION_ATOP', 9 | 'BLEND_MODE_DESTINATION_IN', 10 | 'BLEND_MODE_DESTINATION_OUT', 11 | 'BLEND_MODE_DESTINATION_OVER', 12 | 'BLEND_MODE_LIGHTEN', 13 | 'BLEND_MODE_MULTIPLY', 14 | 'BLEND_MODE_NORMAL', 15 | 'BLEND_MODE_OVERLAY', 16 | 'BLEND_MODE_PLUS_LIGHTER', 17 | 'BLEND_MODE_SCREEN', 18 | 'BLEND_MODE_SOURCE_ATOP', 19 | 'BLEND_MODE_SOURCE_IN', 20 | 'BLEND_MODE_SOURCE_OUT', 21 | 'BLEND_MODE_XOR' 22 | ] 23 | const iosBlendModes = [ 24 | ...commonBlendModes, 25 | 'BLEND_MODE_COLOR', 26 | 'BLEND_MODE_COLOR_BURN', 27 | 'BLEND_MODE_COLOR_DODGE', 28 | 'BLEND_MODE_DIFFERENCE', 29 | 'BLEND_MODE_EXCLUSION', 30 | 'BLEND_MODE_HARD_LIGHT', 31 | 'BLEND_MODE_HUE', 32 | 'BLEND_MODE_LUMINOSITY', 33 | 'BLEND_MODE_PLUS_DARKER', 34 | 'BLEND_MODE_SATURATION', 35 | 'BLEND_MODE_SOFT_LIGHT', 36 | ] 37 | 38 | @Component({ 39 | templateUrl: 'masked-image.component.html' 40 | }) 41 | export class MaskedImageComponent implements OnInit { 42 | 43 | @ViewChild('tintImage') tintImageRef: ElementRef; 44 | 45 | @ViewChild('maskImage') maskImageRef: ElementRef; 46 | 47 | currentBlendModeIndex = 1; 48 | 49 | blendModes = [] 50 | 51 | constructor(private device: DeviceEnvironment) {} 52 | 53 | get currentBlendMode() { 54 | return Ti.UI[this.blendModes[this.currentBlendModeIndex]]; 55 | } 56 | 57 | get currentBlendModeName() { 58 | return this.blendModes[this.currentBlendModeIndex]; 59 | } 60 | 61 | ngOnInit() { 62 | this.blendModes = this.device.runs('ios') ? iosBlendModes : commonBlendModes; 63 | } 64 | 65 | fitTintImage() { 66 | const tintImage = this.tintImageRef.nativeElement.titaniumView as Titanium.UI.MaskedImage; 67 | const tintImageHeight = tintImage.size.height; 68 | tintImage.height = tintImageHeight - 20; 69 | tintImage.width = tintImageHeight - 20; 70 | } 71 | 72 | fitMaskImage() { 73 | const maskImage = this.maskImageRef.nativeElement.titaniumView as Titanium.UI.MaskedImage; 74 | const maskImageHeight = maskImage.size.height; 75 | maskImage.height = maskImageHeight - 100 76 | maskImage.width = maskImageHeight - 100; 77 | } 78 | 79 | switchBlendMode() { 80 | let newBlendModeIndex = this.currentBlendModeIndex + 1; 81 | this.currentBlendModeIndex = newBlendModeIndex >= this.blendModes.length ? 0 : newBlendModeIndex; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/core/TitaniumPlatformRef.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CompilerOptions, 3 | Injector, 4 | NgModule, 5 | NgModuleFactory, 6 | NgModuleRef, 7 | NgZone, 8 | PlatformRef, 9 | Type, 10 | CompilerFactory 11 | } from '@angular/core'; 12 | 13 | import { Logger } from '../log'; 14 | 15 | function compileNgModuleFactory(injector: Injector, options: CompilerOptions, moduleType: Type): Promise> { 16 | const compilerFactory: CompilerFactory = injector.get(CompilerFactory); 17 | const compiler = compilerFactory.createCompiler([options]); 18 | return compiler.compileModuleAsync(moduleType); 19 | } 20 | 21 | type BootstrapFunction = () => Promise>; 22 | export interface BootstrapOptions { 23 | /** 24 | * Optionally specify which `NgZone` should be used. 25 | * 26 | * - Provide your own `NgZone` instance. 27 | * - `zone.js` - Use default `NgZone` which requires `Zone.js`. 28 | * - `noop` - Use `NoopNgZone` which does nothing. 29 | */ 30 | ngZone?: NgZone | 'zone.js' | 'noop'; 31 | } 32 | 33 | export class TitaniumPlatformRef extends PlatformRef { 34 | 35 | private _runBootstrap: BootstrapFunction; 36 | private _platform: PlatformRef; 37 | 38 | constructor(platform: PlatformRef) { 39 | super(); 40 | 41 | this._platform = platform; 42 | } 43 | 44 | get injector(): Injector { 45 | return this._platform.injector; 46 | } 47 | 48 | get logger(): Logger { 49 | return this.injector.get(Logger); 50 | } 51 | 52 | get destroyed() { 53 | return this._platform.destroyed; 54 | } 55 | 56 | bootstrapModuleFactory(moduleFactory: NgModuleFactory, options?: BootstrapOptions): Promise> { 57 | this._runBootstrap = () => this._platform.bootstrapModuleFactory(moduleFactory); 58 | 59 | this.logger.trace('Bootstrapping module using factory'); 60 | return this.bootstrapApp(); 61 | } 62 | 63 | bootstrapModule(moduleType: Type, compilerOptions: CompilerOptions | CompilerOptions[] = []): Promise> { 64 | this._runBootstrap = () => this._platform.bootstrapModule(moduleType, compilerOptions); 65 | 66 | this.logger.trace('Bootstrapping module'); 67 | return this.bootstrapApp(); 68 | } 69 | 70 | bootstrapApp(): Promise> { 71 | // @todo: is this still needed? Is there any bootstrapping left? 72 | 73 | return this._runBootstrap(); 74 | } 75 | 76 | onDestroy(callback: () => void): void { 77 | this._platform.onDestroy(callback); 78 | } 79 | 80 | destroy() { 81 | this.logger.debug('destroy TitaniumPlatformRef'); 82 | } 83 | } -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/input/inputs.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 14 | 15 | 16 | 21 | 28 | 29 | 30 | 35 | 36 | Submit 37 | 38 | 39 | 40 | 45 | 60 | 61 | 65 | 74 | 75 | 76 | 81 | 82 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/table-view/table-view.component.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | A TableView allows you to use the dynamic component features of Angular at the cost of performance. Consider using a ListView if you are working with larger data sets. 8 | 9 | 10 | 11 | 12 | 17 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 45 | 46 | 47 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 67 | 73 | 74 | 75 | 79 | 83 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/router/directives/TitaniumRouterLinkDirective.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener, Input } from '@angular/core'; 2 | import { ActivatedRoute, Router, UrlTree } from '@angular/router'; 3 | import { NavigationTransition, TransitionType } from 'titanium-navigator'; 4 | 5 | import { Logger } from '../../log'; 6 | import { capitalizeFirstLetter } from '../../utility/string'; 7 | import { TitaniumNavigationOptions, TitaniumRouter } from '../TitaniumRouter'; 8 | 9 | @Directive({ 10 | selector: '[tiRouterLink]' 11 | }) 12 | export class TitaniumRouterLinkDirective { 13 | 14 | @Input() queryParams: { [k: string]: any }; 15 | @Input() fragment: string; 16 | @Input() replaceUrl: boolean; 17 | @Input() clearHistory: boolean; 18 | @Input() transition: boolean | string | NavigationTransition = false; 19 | 20 | private router: Router; 21 | private titaniumRouter: TitaniumRouter; 22 | private route: ActivatedRoute; 23 | private logger: Logger; 24 | private commands: any[] = []; 25 | 26 | constructor(router: Router, titaniumRouter: TitaniumRouter, route: ActivatedRoute, logger: Logger) { 27 | this.router = router; 28 | this.titaniumRouter = titaniumRouter; 29 | this.route = route; 30 | this.logger = logger; 31 | } 32 | 33 | @Input() 34 | set tiRouterLink(commands: any[] | string) { 35 | if (commands != null) { 36 | this.commands = Array.isArray(commands) ? commands : [commands]; 37 | } else { 38 | this.commands = []; 39 | } 40 | } 41 | 42 | get urlTree(): UrlTree { 43 | return this.router.createUrlTree(this.commands, { 44 | relativeTo: this.route, 45 | queryParams: this.queryParams, 46 | fragment: this.fragment 47 | }); 48 | } 49 | 50 | @HostListener('click') 51 | onClick(): void { 52 | const options: TitaniumNavigationOptions = { 53 | queryParams: this.queryParams, 54 | fragment: this.fragment, 55 | replaceUrl: this.replaceUrl, 56 | clearHistory: this.clearHistory, 57 | transition: this.convertTransition() 58 | }; 59 | this.titaniumRouter.navigateByUrl(this.urlTree, options) 60 | .catch(e => { 61 | this.logger.error(e); 62 | }); 63 | } 64 | 65 | private convertTransition(): NavigationTransition { 66 | if (typeof this.transition === 'boolean') { 67 | return { 68 | type: this.transition ? TransitionType.SlideLeft : TransitionType.None 69 | } 70 | } else if (typeof this.transition === 'string') { 71 | const enumMemberName = capitalizeFirstLetter(this.transition); 72 | if (!TransitionType[enumMemberName]) { 73 | throw new Error(`Invalid transition string specified. Valid transitions are: ${Object.keys(TransitionType).map(t => TransitionType[t]).join(', ')}`); 74 | } 75 | 76 | return { 77 | type: TransitionType[enumMemberName] 78 | } 79 | } else { 80 | return this.transition; 81 | } 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /src/router/TitaniumRouterModule.ts: -------------------------------------------------------------------------------- 1 | import { LocationStrategy, PlatformLocation } from '@angular/common'; 2 | import { 3 | Injector, 4 | ModuleWithProviders, 5 | NgModule, 6 | NO_ERRORS_SCHEMA, 7 | Provider 8 | } from '@angular/core'; 9 | import { ExtraOptions, RouteReuseStrategy, RouterModule, Routes, Router } from '@angular/router'; 10 | import { loadNavigatorProviders, NavigationManager } from 'titanium-navigator'; 11 | 12 | import { HistoryStack, TitaniumPlatformLocation, EmulatedPathLocationStrategy } from '../common'; 13 | import { Logger } from '../log'; 14 | import { TitaniumCommonModule } from '../TitaniumCommonModule'; 15 | import { TitaniumRouterLinkDirective, TitaniumRouterOutletDirective } from './directives'; 16 | import { TitaniumRouter } from './TitaniumRouter'; 17 | import { NavigationAwareRouteReuseStrategy } from './NavigationAwareRouteReuseStrategy'; 18 | import { EmptyOutletComponent } from './components/EmptyOutlet'; 19 | import { ComponentAdapter } from './adapters/ComponentAdapter'; 20 | import { RouterStateAdapter } from './adapters/RouterStateAdapter'; 21 | 22 | export function createNavigationManager(injector: Injector) { 23 | return new NavigationManager({ 24 | componentAdapter: new ComponentAdapter(), 25 | navigatorProviders: loadNavigatorProviders(tabGroup => new RouterStateAdapter(tabGroup, injector)) 26 | }); 27 | } 28 | 29 | const ROUTER_DIRECTIVES = [ 30 | TitaniumRouterLinkDirective, 31 | TitaniumRouterOutletDirective, 32 | EmptyOutletComponent 33 | ]; 34 | 35 | export const ROUTER_PROVIDERS: Provider[] = [ 36 | { provide: NavigationManager, useFactory: createNavigationManager, deps: [Injector] }, 37 | HistoryStack, 38 | { provide: TitaniumPlatformLocation, useClass: TitaniumPlatformLocation, deps: [HistoryStack, NavigationManager] }, 39 | { provide: PlatformLocation, useExisting: TitaniumPlatformLocation }, 40 | { provide: EmulatedPathLocationStrategy, useClass: EmulatedPathLocationStrategy, deps: [PlatformLocation] }, 41 | { provide: LocationStrategy, useExisting: EmulatedPathLocationStrategy }, 42 | { provide: NavigationAwareRouteReuseStrategy, useClass: NavigationAwareRouteReuseStrategy, deps: [NavigationManager, Logger] }, 43 | { provide: RouteReuseStrategy, useExisting: NavigationAwareRouteReuseStrategy }, 44 | TitaniumRouter 45 | ]; 46 | 47 | // @dynamic 48 | @NgModule({ 49 | declarations: ROUTER_DIRECTIVES, 50 | imports: [ 51 | RouterModule, 52 | TitaniumCommonModule, 53 | ], 54 | exports: [ 55 | RouterModule, 56 | ...ROUTER_DIRECTIVES 57 | ], 58 | entryComponents: [EmptyOutletComponent], 59 | schemas: [NO_ERRORS_SCHEMA] 60 | }) 61 | export class TitaniumRouterModule { 62 | static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders { 63 | return { 64 | ngModule: TitaniumRouterModule, 65 | providers: [...RouterModule.forRoot(routes, config).providers, ...ROUTER_PROVIDERS] 66 | } 67 | } 68 | 69 | static forChild(routes: Routes): ModuleWithProviders { 70 | return { 71 | ngModule: TitaniumRouterModule, 72 | providers: RouterModule.forChild(routes).providers 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ti-angular-example/src/app/modules/controls/components/views/list-view/list-view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core' 2 | import { 3 | Logger, 4 | DeviceEnvironment, 5 | ListViewComponent as ListViewDirective, 6 | ListSectionDirective, 7 | WithTiGlobal 8 | } from 'titanium-angular'; 9 | 10 | @Component({ 11 | templateUrl: './list-view.component.html', 12 | }) 13 | export class ListViewComponent extends WithTiGlobal() { 14 | 15 | @ViewChild(ListViewDirective, { static: false }) listView: ListViewDirective; 16 | 17 | @ViewChild('accessorySection', {read: ListSectionDirective, static: false}) accessorySection: ListSectionDirective; 18 | 19 | items: any[] = [ 20 | { 21 | profileImage: { 22 | image: 'https://picsum.photos/200' 23 | }, 24 | name: { 25 | text: 'Daisy Rey', 26 | font: { 27 | fontWeight: 'bold' 28 | } 29 | }, 30 | preview: { 31 | text: 'Can\'t wait to see you! \uE056' 32 | }, 33 | time: { 34 | color: 'black', 35 | text: '21:42', 36 | font: { 37 | fontSize: '12', 38 | fontWeight: 'bold' 39 | } 40 | }, 41 | newMessagesBadgeText: { 42 | text: '1', 43 | font: { 44 | fontSize: '12' 45 | } 46 | }, 47 | template: 'ComponentTemplate' 48 | }, { 49 | profileImage: { 50 | image: 'https://picsum.photos/200' 51 | }, 52 | name: { 53 | text: 'Frank', 54 | font: { 55 | fontWeight: 'bold' 56 | } 57 | }, 58 | preview: { 59 | text: '28 days, 6 hours, 42 minutes, 12 seconds' 60 | }, 61 | time: { 62 | text: '20:15', 63 | font: { 64 | fontSize: '12' 65 | } 66 | }, 67 | newMessagesBadge: { 68 | visible: false 69 | }, 70 | template: 'ComponentTemplate' 71 | }, { 72 | title: { 73 | text: 'Pear' 74 | }, 75 | detail: { 76 | text: '7', 77 | color: 'green' 78 | }, 79 | template: 'InlineTemplate' 80 | } 81 | ]; 82 | 83 | constructor(private logger: Logger, private device: DeviceEnvironment) { 84 | super(); 85 | } 86 | 87 | fetchData(event) { 88 | // You would usually fetch your remote data here 89 | setTimeout(() => { 90 | event.source.endRefreshing(); 91 | this.logger.debug('Ti.UI.RefreshControl finished refreshing'); 92 | }, 1000); 93 | } 94 | 95 | handleListViewClick(event) { 96 | this.logger.debug(`Ti.UI.ListView clicked cell at index ${event.sectionIndex} / ${event.itemIndex}`); 97 | if (this.device.runs('ios')) { 98 | this.listView.listView.deselectItem(event.sectionIndex, event.itemIndex); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/directives/list-view/list-section.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | ChangeDetectionStrategy, 4 | Component, 5 | ContentChildren, 6 | DoCheck, 7 | ElementRef, 8 | forwardRef, 9 | Input, 10 | IterableChanges, 11 | IterableDiffer, 12 | IterableDiffers, 13 | QueryList, 14 | TrackByFunction 15 | } from '@angular/core'; 16 | 17 | import { ListViewComponent } from './list-view'; 18 | import { ListItemDirective } from './list-item'; 19 | 20 | @Component({ 21 | changeDetection: ChangeDetectionStrategy.OnPush, 22 | selector: 'list-section,ListSection', 23 | template: `` 24 | }) 25 | export class ListSectionDirective implements AfterContentInit, DoCheck { 26 | 27 | public listSection: Titanium.UI.ListSection; 28 | 29 | @ContentChildren(forwardRef(() => ListItemDirective)) contentItems: QueryList; 30 | 31 | @Input() 32 | set items(items: ListDataItem[]) { 33 | this._items = items 34 | this._itemsDirty = true; 35 | } 36 | 37 | @Input() 38 | set itemTrackBy(fn: TrackByFunction) { 39 | this._trackByFn = fn; 40 | } 41 | 42 | get itemTrackBy() { return this._trackByFn; } 43 | 44 | private _items: Array; 45 | 46 | private _itemsDirty = true; 47 | 48 | private _trackByFn!: TrackByFunction; 49 | 50 | private owner: ListViewComponent; 51 | 52 | private _itemDiffer: IterableDiffer | null = null; 53 | 54 | constructor(el: ElementRef, owner: ListViewComponent, private _iterableDiffers: IterableDiffers) { 55 | this.listSection = el.nativeElement.titaniumView; 56 | this.owner = owner; 57 | } 58 | 59 | ngAfterContentInit() { 60 | this.updateContentListItems(); 61 | 62 | this.owner.appendSection(this.listSection); 63 | 64 | this.contentItems.changes.subscribe(changes => { 65 | for (let changedPropertyName in changes) { 66 | // @todo check for changed items? 67 | } 68 | }); 69 | } 70 | 71 | ngDoCheck(): void { 72 | if (this._itemsDirty) { 73 | this._itemsDirty = false; 74 | const value = this._items; 75 | if (!this._itemDiffer && value) { 76 | this._itemDiffer = this._iterableDiffers.find(value).create(this.itemTrackBy); 77 | } 78 | } 79 | 80 | if (this._itemDiffer) { 81 | const changes = this._itemDiffer.diff(this._items); 82 | if (changes) { 83 | this.applyItemChanges(changes); 84 | } 85 | } 86 | } 87 | 88 | updateListItem(item: ListItemDirective) { 89 | if (!this.contentItems) { 90 | return; 91 | } 92 | 93 | let itemIndex = null; 94 | this.contentItems.find((element, index, array) => { 95 | if (element.dataItem === item.dataItem) { 96 | itemIndex = index; 97 | return true; 98 | } 99 | 100 | return false; 101 | }); 102 | 103 | this.listSection.updateItemAt(itemIndex, item.dataItem); 104 | } 105 | 106 | private applyItemChanges(changes: IterableChanges) { 107 | changes.forEachOperation((item, adjustedPreviousIndex, currentIndex) => { 108 | if (item.previousIndex === null) { 109 | this.listSection.insertItemsAt(currentIndex, [item.item]); 110 | } else if (currentIndex === null) { 111 | this.listSection.deleteItemsAt(adjustedPreviousIndex, 1); 112 | } else if (adjustedPreviousIndex !== null) { 113 | this.listSection.deleteItemsAt(adjustedPreviousIndex, 1); 114 | this.listSection.insertItemsAt(currentIndex, [item.item]); 115 | } 116 | }); 117 | } 118 | 119 | private updateContentListItems() { 120 | if (this.contentItems.length > 0) { 121 | this.listSection.items = this.contentItems.map(listItem => listItem.dataItem); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/router/NavigationAwareRouteReuseStrategy.ts: -------------------------------------------------------------------------------- 1 | import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from "@angular/router"; 2 | import { NavigationManager } from "titanium-navigator"; 3 | 4 | import { Logger } from '../log'; 5 | 6 | /** 7 | * A route reuse strategy that is aware of native navigation events and 8 | */ 9 | export class NavigationAwareRouteReuseStrategy extends RouteReuseStrategy { 10 | 11 | private navigationManager: NavigationManager; 12 | 13 | private logger: Logger; 14 | 15 | private handlers: Map = new Map(); 16 | 17 | constructor(navigationManager: NavigationManager, logger: Logger) { 18 | super(); 19 | 20 | this.navigationManager = navigationManager; 21 | this.logger = logger; 22 | } 23 | 24 | /** 25 | * Determines if this route (and its subtree) should be detached to be reused later 26 | */ 27 | shouldDetach(route: ActivatedRouteSnapshot): boolean { 28 | if (this.navigationManager.isNativeBackNavigation || this.navigationManager.isLocationBackNavigation) { 29 | return false; 30 | } 31 | 32 | return true; 33 | } 34 | 35 | /** 36 | * Stores the detached route. 37 | * 38 | * Storing a `null` value should erase the previously stored value. 39 | */ 40 | store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void { 41 | this.logger.debug(this.getRouteDebugDescription(route)); 42 | 43 | const absoluteRoutePath = this.generateAbsoluteRoutePath(route); 44 | if (!handle) { 45 | this.handlers.delete(absoluteRoutePath); 46 | } else { 47 | this.handlers.set(absoluteRoutePath, handle); 48 | } 49 | } 50 | 51 | /** 52 | * Determines if this route (and its subtree) should be reattached 53 | */ 54 | shouldAttach(route: ActivatedRouteSnapshot): boolean { 55 | this.logger.trace(this.getRouteDebugDescription(route)); 56 | 57 | // check if we are coming from a natively triggered back navigation 58 | if (this.navigationManager.isNativeBackNavigation) { 59 | return true; 60 | } else if (this.navigationManager.isLocationBackNavigation) { 61 | return true; 62 | } 63 | 64 | return false; 65 | } 66 | 67 | /** 68 | * Retrieves the previously stored route 69 | */ 70 | retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { 71 | const absoluteRoutePath = this.generateAbsoluteRoutePath(route); 72 | if (this.handlers.has(absoluteRoutePath)) { 73 | return this.handlers.get(absoluteRoutePath); 74 | } 75 | 76 | return null; 77 | } 78 | 79 | /** 80 | * Determines if a route should be reused. 81 | * 82 | * Reuses routes as long as their route config is the same. 83 | */ 84 | shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { 85 | return future.routeConfig === curr.routeConfig; 86 | } 87 | 88 | clearDetachedRouteHandlers(): void { 89 | this.handlers.clear(); 90 | } 91 | 92 | snapshotDetachedRoutehandlers(): Map { 93 | return new Map(this.handlers); 94 | } 95 | 96 | restoreHandlers(handlers: Map): void { 97 | this.handlers = new Map(handlers); 98 | } 99 | 100 | private generateAbsoluteRoutePath(route: ActivatedRouteSnapshot): string { 101 | let urlSegments = []; 102 | urlSegments = urlSegments.concat(route.url); 103 | let parentRoute = route.parent; 104 | while(parentRoute) { 105 | urlSegments = urlSegments.concat(parentRoute.url); 106 | parentRoute = parentRoute.parent; 107 | } 108 | 109 | return urlSegments.reverse().join('/'); 110 | } 111 | 112 | private getRouteDebugDescription(route: ActivatedRouteSnapshot) { 113 | return route.pathFromRoot.join(' -> '); 114 | } 115 | } -------------------------------------------------------------------------------- /src/common/HistoryStack.ts: -------------------------------------------------------------------------------- 1 | import { LocationChangeListener, LocationChangeEvent } from '@angular/common'; 2 | import { Injectable } from '@angular/core'; 3 | import { Subject } from 'rxjs'; 4 | 5 | export interface LocationState { 6 | state: any; 7 | title: string, 8 | url: string, 9 | queryString: string 10 | } 11 | 12 | /** 13 | * A stack based history of location states, mimicking the HTML5 History API. 14 | */ 15 | @Injectable() 16 | export class HistoryStack { 17 | 18 | /** 19 | * List of location states. 20 | */ 21 | private states: Array = []; 22 | 23 | /** 24 | * Used to announce state changes to subscribers. 25 | */ 26 | private statesSubject = new Subject(); 27 | 28 | /** 29 | * Gets the most recent state from the top of the stack. 30 | */ 31 | get state(): LocationState { 32 | if (this.states.length === 0) { 33 | return null; 34 | } 35 | 36 | return this.states[this.states.length - 1]; 37 | } 38 | 39 | /** 40 | * Gets the number of entries currently stored in the history. 41 | */ 42 | get length(): number { 43 | return this.states.length; 44 | } 45 | 46 | /** 47 | * Pops the most recent history entry from the top of the stack and issues an 48 | * 'onpopstate' event. 49 | */ 50 | back() { 51 | const poppedState = this.popState(); 52 | this.statesSubject.next({ type: 'onpopstate', state: poppedState }); 53 | } 54 | 55 | /** 56 | * Pushes a new hitory entry to the top of the stack. 57 | * 58 | * @param state Custom state data associate with the history entry 59 | * @param title A short title for the new state 60 | * @param url The new history entry's URL 61 | * @param queryParams Any query parameters of the history entry 62 | */ 63 | pushState(state: any, title: string, url: string, queryParams: string): void { 64 | this.states.push({ 65 | state: state, 66 | title: title, 67 | url: url, 68 | queryString: queryParams 69 | }); 70 | } 71 | 72 | /** 73 | * Replaces the topmost history entry with a new one. 74 | * 75 | * @param state Custom state data associate with the history entry 76 | * @param title A short title for the new state 77 | * @param url The new history entry's URL 78 | * @param queryParams Any query parameters of the history entry 79 | */ 80 | replaceState(state: any, title: string, url: string, queryParams: string): void { 81 | if (this.states.length > 0) { 82 | this.state.state = state; 83 | this.state.title = title; 84 | this.state.url = url; 85 | this.state.queryString = queryParams; 86 | } else { 87 | this.pushState(state, title, url, queryParams); 88 | } 89 | } 90 | 91 | /** 92 | * Removes the topmost history entry from the stack. 93 | * 94 | * Note that this does not notify any onpopstate listeners. This is for 95 | * internal modification of the history stack. For backwards navigation use 96 | * the back() method. 97 | */ 98 | popState() { 99 | return this.states.pop(); 100 | } 101 | 102 | /** 103 | * Registers a new handler function for the 'popstate' event 104 | * 105 | * @param fn Handler function 106 | */ 107 | onPopState(fn: LocationChangeListener): void { 108 | this.statesSubject.subscribe((locationChangeEvent) => { 109 | fn(locationChangeEvent); 110 | }); 111 | } 112 | 113 | /** 114 | * Returns a copy of the current history stack. 115 | */ 116 | snapshotStack(): Array { 117 | return [...this.states]; 118 | } 119 | 120 | /** 121 | * Replaces all history entries with the given entries. 122 | * 123 | * @param states List of history entries 124 | */ 125 | restoreStack(states: Array): void { 126 | this.states = states; 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /src/forms/accessors/ControlValueAccessor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google Inc. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | 9 | import {InjectionToken} from '@angular/core'; 10 | 11 | /** 12 | * @description 13 | * Defines an interface that acts as a bridge between the Angular forms API and a 14 | * native element in the DOM. 15 | * 16 | * Implement this interface to create a custom form control directive 17 | * that integrates with Angular forms. 18 | * 19 | * @see DefaultValueAccessor 20 | * 21 | * @publicApi 22 | */ 23 | export interface ControlValueAccessor { 24 | /** 25 | * @description 26 | * Writes a new value to the element. 27 | * 28 | * This method is called by the forms API to write to the view when programmatic 29 | * changes from model to view are requested. 30 | * 31 | * @usageNotes 32 | * ### Write a value to the element 33 | * 34 | * The following example writes a value to the native DOM element. 35 | * 36 | * ```ts 37 | * writeValue(value: any): void { 38 | * this._renderer.setProperty(this._elementRef.nativeElement, 'value', value); 39 | * } 40 | * ``` 41 | * 42 | * @param obj The new value for the element 43 | */ 44 | writeValue(obj: any): void; 45 | 46 | /** 47 | * @description 48 | * Registers a callback function that is called when the control's value 49 | * changes in the UI. 50 | * 51 | * This method is called by the forms API on initialization to update the form 52 | * model when values propagate from the view to the model. 53 | * 54 | * When implementing the `registerOnChange` method in your own value accessor, 55 | * save the given function so your class calls it at the appropriate time. 56 | * 57 | * @usageNotes 58 | * ### Store the change function 59 | * 60 | * The following example stores the provided function as an internal method. 61 | * 62 | * ```ts 63 | * registerOnChange(fn: (_: any) => void): void { 64 | * this._onChange = fn; 65 | * } 66 | * ``` 67 | * 68 | * When the value changes in the UI, call the registered 69 | * function to allow the forms API to update itself: 70 | * 71 | * ```ts 72 | * host: { 73 | * '(change)': '_onChange($event.target.value)' 74 | * } 75 | * ``` 76 | * 77 | * @param fn The callback function to register 78 | */ 79 | registerOnChange(fn: any): void; 80 | 81 | /** 82 | * @description 83 | * Registers a callback function is called by the forms API on initialization 84 | * to update the form model on blur. 85 | * 86 | * When implementing `registerOnTouched` in your own value accessor, save the given 87 | * function so your class calls it when the control should be considered 88 | * blurred or "touched". 89 | * 90 | * @usageNotes 91 | * ### Store the callback function 92 | * 93 | * The following example stores the provided function as an internal method. 94 | * 95 | * ```ts 96 | * registerOnTouched(fn: any): void { 97 | * this._onTouched = fn; 98 | * } 99 | * ``` 100 | * 101 | * On blur (or equivalent), your class should call the registered function to allow 102 | * the forms API to update itself: 103 | * 104 | * ```ts 105 | * host: { 106 | * '(blur)': '_onTouched()' 107 | * } 108 | * ``` 109 | * 110 | * @param fn The callback function to register 111 | */ 112 | registerOnTouched(fn: any): void; 113 | 114 | /** 115 | * @description 116 | * Function that is called by the forms API when the control status changes to 117 | * or from 'DISABLED'. Depending on the status, it enables or disables the 118 | * appropriate DOM element. 119 | * 120 | * @usageNotes 121 | * The following is an example of writing the disabled property to a native DOM element: 122 | * 123 | * ```ts 124 | * setDisabledState(isDisabled: boolean): void { 125 | * this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled); 126 | * } 127 | * ``` 128 | * 129 | * @param isDisabled The disabled status to set on the element 130 | */ 131 | setDisabledState?(isDisabled: boolean): void; 132 | } 133 | 134 | /** 135 | * Used to provide a `ControlValueAccessor` for form controls. 136 | * 137 | * See `DefaultValueAccessor` for how to implement one. 138 | * 139 | * @publicApi 140 | */ 141 | export const NG_VALUE_ACCESSOR = new InjectionToken('NgValueAccessor'); --------------------------------------------------------------------------------