├── 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');
--------------------------------------------------------------------------------