├── src ├── assets │ ├── .gitkeep │ └── texture.png ├── app │ ├── app.component.scss │ ├── atp-library │ │ ├── atp-time-picker │ │ │ ├── atp-time-picker.component.html │ │ │ ├── atp-time-picker.component.scss │ │ │ ├── atp-time-picker.component.spec.ts │ │ │ └── atp-time-picker.component.ts │ │ ├── atp.directive.spec.ts │ │ ├── atp-core.service.spec.ts │ │ ├── atp-time-picker.service.spec.ts │ │ ├── time-picker │ │ │ ├── time-picker.component.spec.ts │ │ │ ├── time-picker.component.html │ │ │ ├── time-picker.component.scss │ │ │ └── time-picker.component.ts │ │ ├── atp-time-picker.module.ts │ │ ├── definitions.ts │ │ ├── preferences.ts │ │ ├── atp.directive.ts │ │ ├── atp-time-picker.service.ts │ │ └── atp-core.service.ts │ ├── atp.png │ ├── snack-ad │ │ ├── snack-ad.component.html │ │ ├── snack-ad.component.spec.ts │ │ ├── snack-ad.component.scss │ │ └── snack-ad.component.ts │ ├── presentation │ │ ├── tab │ │ │ ├── tab.component.html │ │ │ ├── tab.component.scss │ │ │ ├── tab.component.ts │ │ │ └── tab.component.spec.ts │ │ ├── tabs │ │ │ ├── tabs.component.html │ │ │ ├── tabs.component.scss │ │ │ ├── tabs.component.spec.ts │ │ │ └── tabs.component.ts │ │ ├── api │ │ │ ├── api.component.ts │ │ │ ├── api.component.spec.ts │ │ │ ├── api.component.scss │ │ │ └── api.component.html │ │ ├── full-layout │ │ │ ├── full-layout.component.ts │ │ │ ├── full-layout.component.scss │ │ │ └── full-layout.component.html │ │ ├── example │ │ │ ├── example.component.spec.ts │ │ │ ├── example.component.scss │ │ │ ├── example.component.ts │ │ │ └── example.component.html │ │ ├── examples │ │ │ ├── example-arabic │ │ │ │ ├── example-arabic.component.spec.ts │ │ │ │ ├── example-arabic.component.ts │ │ │ │ ├── example-arabic.component.scss │ │ │ │ └── example-arabic.component.html │ │ │ ├── example-hour │ │ │ │ ├── example-hour.component.spec.ts │ │ │ │ ├── example-hour.component.scss │ │ │ │ ├── example-hour.component.ts │ │ │ │ └── example-hour.component.html │ │ │ ├── example-material │ │ │ │ ├── example-material.component.spec.ts │ │ │ │ ├── example-material.component.ts │ │ │ │ ├── example-material.component.scss │ │ │ │ └── example-material.component.html │ │ │ └── example-persian │ │ │ │ ├── example-persian.component.spec.ts │ │ │ │ ├── example-persian.component.scss │ │ │ │ ├── example-persian.component.ts │ │ │ │ └── example-persian.component.html │ │ ├── basic-layout │ │ │ ├── basic-layout.component.scss │ │ │ ├── basic-layout.component.ts │ │ │ └── basic-layout.component.html │ │ └── _common.scss │ ├── app.component.html │ ├── app.component.ts │ ├── app.router.ts │ ├── app.component.spec.ts │ └── app.module.ts ├── favicon.ico ├── environments │ ├── environment.hmr.ts │ ├── environment.docs.ts │ ├── environment.prod.ts │ └── environment.ts ├── typings.d.ts ├── tsconfig.app.json ├── tsconfig.spec.json ├── hmr.ts ├── styles.scss ├── main.ts ├── index-github.html ├── index-docs.html ├── test.ts ├── index.html ├── polyfills.ts └── fontello.scss ├── cypress.json ├── time-picker.jpg ├── dist ├── favicon.ico ├── assets │ └── texture.png ├── atp.600dee4fa14621183179.png ├── styles.329b5284aa1efd4ecb64.bundle.css ├── index.html ├── index-github.html ├── inline.7da7212a328c1cbd5e9f.bundle.js └── 3rdpartylicenses.txt ├── docs ├── favicon.ico ├── assets │ └── texture.png ├── atp.600dee4fa14621183179.png ├── index-docs.html ├── inline.844ab025a6723b51af71.bundle.js └── 3rdpartylicenses.txt ├── public_api.ts ├── ng-package.json ├── cypress ├── fixtures │ └── example.json ├── integration │ ├── ng2_spec.js │ ├── ng4_spec.js │ ├── ng5_spec.js │ ├── core_spec.js │ └── shared.js ├── plugins │ └── index.js └── support │ ├── index.js │ └── commands.js ├── e2e ├── app.po.ts ├── tsconfig.e2e.json └── app.e2e-spec.ts ├── .editorconfig ├── .vscode └── settings.json ├── tsconfig.json ├── deploy.sh ├── .travis.yml ├── .gitignore ├── protractor.conf.js ├── scripts └── init-test-beds.sh ├── karma.conf.js ├── .angular-cli.json ├── package.json ├── README.md └── tslint.json /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "videoRecording": false 3 | } 4 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-time-picker/atp-time-picker.component.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /src/app/atp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/src/app/atp.png -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /time-picker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/time-picker.jpg -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/dist/favicon.ico -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /dist/assets/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/dist/assets/texture.png -------------------------------------------------------------------------------- /docs/assets/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/docs/assets/texture.png -------------------------------------------------------------------------------- /src/assets/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/src/assets/texture.png -------------------------------------------------------------------------------- /src/app/snack-ad/snack-ad.component.html: -------------------------------------------------------------------------------- 1 |
2 |
-------------------------------------------------------------------------------- /src/app/presentation/tab/tab.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /dist/atp.600dee4fa14621183179.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/dist/atp.600dee4fa14621183179.png -------------------------------------------------------------------------------- /docs/atp.600dee4fa14621183179.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owsolutions/amazing-time-picker/HEAD/docs/atp.600dee4fa14621183179.png -------------------------------------------------------------------------------- /src/environments/environment.hmr.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | hmr: true, 4 | layout: 'full' 5 | }; 6 | -------------------------------------------------------------------------------- /src/environments/environment.docs.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | hmr: false, 4 | layout: 'basic' 5 | }; 6 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | hmr: false, 4 | layout: 'full' 5 | }; 6 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /public_api.ts: -------------------------------------------------------------------------------- 1 | export * from './src/app/atp-library/atp-time-picker.module'; 2 | export * from './src/app/atp-library/atp-time-picker.service'; 3 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "npm_dist", 4 | "lib": { 5 | "entryFile": "public_api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/app/presentation/tab/tab.component.scss: -------------------------------------------------------------------------------- 1 | .pane{ 2 | padding: 1em; 3 | background: #f5f2f0; 4 | border-bottom-left-radius: 5px; 5 | border-bottom-right-radius: 5px; 6 | } -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /src/app/presentation/tabs/tabs.component.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/presentation/api/api.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-api', 5 | templateUrl: './api.component.html', 6 | styleUrls: ['./api.component.scss'] 7 | }) 8 | export class ApiComponent { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/app/atp-library/atp.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { AtpDirective } from './atp.directive'; 2 | 3 | describe('AtpDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new AtpDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/presentation/tab/tab.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-tab', 5 | templateUrl: './tab.component.html', 6 | styleUrls: ['./tab.component.scss'] 7 | }) 8 | export class TabComponent { 9 | @Input('tabTitle') tabTitle: string; 10 | @Input() active = false; 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/.svn": true, 5 | "**/.hg": true, 6 | "**/CVS": true, 7 | "**/npm_dist": true, 8 | "**/.DS_Store": true 9 | }, 10 | "search.exclude": { 11 | "**/node_modules": true, 12 | "**/npm_dist": true, 13 | "**/bower_components": true 14 | } 15 | } -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { environment } from '../environments/environment'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'] 8 | }) 9 | export class AppComponent { 10 | public layout = environment.layout; 11 | } 12 | -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('amazing-time-picker App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-time-picker/atp-time-picker.component.scss: -------------------------------------------------------------------------------- 1 | .atp-time-picker{ 2 | .icon-container{ 3 | display: inline-block; 4 | margin-right: .2em; 5 | svg{ 6 | cursor: pointer; 7 | position: relative; 8 | top: .5em; 9 | } 10 | /deep/ i{ 11 | cursor: pointer; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts" 15 | ], 16 | "include": [ 17 | "**/*.spec.ts", 18 | "**/*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /docs/index-docs.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dist/styles.329b5284aa1efd4ecb64.bundle.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Dancing+Script|Roboto);@import url(https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css);@import url(https://fonts.googleapis.com/icon?family=Material+Icons);*{box-sizing:border-box}body{background:#fafafa;margin:0;padding:0;font-family:Roboto,sans-serif}.container{width:100%;margin:0 auto;padding:0 15px}.text-right{text-align:right} -------------------------------------------------------------------------------- /src/app/presentation/full-layout/full-layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | declare var require: any; 3 | @Component({ 4 | selector: 'app-full-layout', 5 | templateUrl: './full-layout.component.html', 6 | styleUrls: ['./full-layout.component.scss'] 7 | }) 8 | export class FullLayoutComponent implements OnInit { 9 | logo = require('../../atp.png'); 10 | 11 | constructor() {} 12 | 13 | ngOnInit() {} 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types" 13 | ], 14 | "lib": [ 15 | "es2017", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-core.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AtpCoreService } from './atp-core.service'; 4 | 5 | describe('AtpCoreService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AtpCoreService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([AtpCoreService], (service: AtpCoreService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | hmr: false, 9 | layout: 'basic' 10 | }; 11 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-time-picker.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { AtpTimePickerService } from './atp-time-picker.service'; 4 | 5 | describe('AtpTimePickerService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [AtpTimePickerService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([AtpTimePickerService], (service: AtpTimePickerService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/presentation/tabs/tabs.component.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | list-style: none; 3 | margin: 0; 4 | padding: 0; 5 | li { 6 | float: left; 7 | width: 20%; 8 | } 9 | a { 10 | display: block; 11 | text-align: center; 12 | text-decoration: none; 13 | text-transform: uppercase; 14 | color: #888; 15 | padding: 15px 0; 16 | border-bottom: 2px solid #888; 17 | background: #f7f7f7; 18 | 19 | &:hover, 20 | &.active { 21 | background: #ddd; 22 | } 23 | } 24 | } 25 | .clearfix:after { 26 | content: ''; 27 | display: table; 28 | clear: both; 29 | } 30 | -------------------------------------------------------------------------------- /cypress/integration/ng2_spec.js: -------------------------------------------------------------------------------- 1 | const { clockMaker } = require('./shared'); 2 | 3 | describe('Kitchen Sink', function () { 4 | 5 | it ('Project on Angular 2 must work proprely', function () { 6 | cy.visit('http://localhost:12000'); 7 | cy.title().should('include', 'MyApp') 8 | cy.get('#test-bed-directive').click(); 9 | for (const number of clockMaker('hour')) { 10 | cy.get('.time-picker-clock').click(number.top, number.left); 11 | cy.wait(50); 12 | } 13 | cy.get('.atp-ref-dialog-close').click(); 14 | cy.wait(100); 15 | cy.get('#test-bed-directive').should('have.value', '09:16'); 16 | }); 17 | }); -------------------------------------------------------------------------------- /cypress/integration/ng4_spec.js: -------------------------------------------------------------------------------- 1 | const { clockMaker } = require('./shared'); 2 | 3 | describe('Kitchen Sink', function () { 4 | 5 | it ('Project on Angular 4 must work proprely', function () { 6 | cy.visit('http://localhost:12001'); 7 | cy.title().should('include', 'Ng4TestBed') 8 | cy.get('#test-bed-directive').click(); 9 | for (const number of clockMaker('hour')) { 10 | cy.get('.time-picker-clock').click(number.top, number.left); 11 | cy.wait(50); 12 | } 13 | cy.get('.atp-ref-dialog-close').click(); 14 | cy.wait(100); 15 | cy.get('#test-bed-directive').should('have.value', '09:16'); 16 | }); 17 | }); -------------------------------------------------------------------------------- /cypress/integration/ng5_spec.js: -------------------------------------------------------------------------------- 1 | const { clockMaker } = require('./shared'); 2 | 3 | describe('Kitchen Sink', function () { 4 | 5 | it ('Project on Angular 5 must work proprely', function () { 6 | cy.visit('http://localhost:12002'); 7 | cy.title().should('include', 'Ng5TestBed') 8 | cy.get('#test-bed-directive').click(); 9 | for (const number of clockMaker('hour')) { 10 | cy.get('.time-picker-clock').click(number.top, number.left); 11 | cy.wait(50); 12 | } 13 | cy.get('.atp-ref-dialog-close').click(); 14 | cy.wait(100); 15 | cy.get('#test-bed-directive').should('have.value', '09:16'); 16 | }); 17 | }); -------------------------------------------------------------------------------- /src/hmr.ts: -------------------------------------------------------------------------------- 1 | import { NgModuleRef, ApplicationRef } from '@angular/core'; 2 | import { createNewHosts } from '@angularclass/hmr'; 3 | 4 | export const hmrBootstrap = (module: any, bootstrap: () => Promise>) => { 5 | let ngModule: NgModuleRef; 6 | module.hot.accept(); 7 | bootstrap().then(mod => ngModule = mod); 8 | module.hot.dispose(() => { 9 | const appRef: ApplicationRef = ngModule.injector.get(ApplicationRef); 10 | const elements = appRef.components.map(c => c.location.nativeElement); 11 | const makeVisible = createNewHosts(elements); 12 | ngModule.destroy(); 13 | makeVisible(); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /src/app/app.router.ts: -------------------------------------------------------------------------------- 1 | import { Route, RouterModule, Routes } from '@angular/router'; 2 | import { ExampleComponent } from './presentation/example/example.component'; 3 | import { ApiComponent } from './presentation/api/api.component'; 4 | 5 | export const appRoutes: Routes = [ 6 | { 7 | path: 'examples', 8 | component: ExampleComponent 9 | }, 10 | { 11 | path: 'api', 12 | component: ApiComponent 13 | }, 14 | { 15 | path: '', 16 | redirectTo: '/examples', 17 | pathMatch: 'full' 18 | } 19 | ]; 20 | 21 | export function appRoutersGenerator() { 22 | return RouterModule.forRoot(appRoutes, { useHash: true }); 23 | } 24 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Starting deploy..."; 4 | 5 | set -e # exit with nonzero exit code if anything fails 6 | 7 | if [[ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]]; then 8 | npm run build:github; 9 | git checkout -b gh-pages; 10 | git add -A; 11 | git commit -m "Build github pages"; 12 | 13 | git push https://torabian:${GITHUB_TOKEN}@github.com/owsolutions/amazing-time-picker :gh-pages && git subtree push --prefix dist https://torabian:${GITHUB_TOKEN}@github.com/owsolutions/amazing-time-picker gh-pages 14 | 15 | else 16 | echo "Skipped updating gh-pages, because build is not triggered from the master branch." 17 | fi; -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | cache: 10 | yarn: true 11 | directories: 12 | - $HOME/.yarn-cache 13 | - node_modules 14 | 15 | dist: trusty 16 | addons: 17 | chrome: stable 18 | 19 | script: 20 | 21 | - npm run lint 22 | # Build the project itself 23 | - npm run build 24 | - npm run build:docs 25 | # Bundle for npm package 26 | - npm run build:npm 27 | 28 | # Test within testbeds 29 | - sh scripts/init-test-beds.sh 30 | - npm run ci 31 | - npm run build:github 32 | - cp dist/index-github.html dist/index.html 33 | 34 | after_success: 35 | - bash ./deploy.sh 36 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import url('https://fonts.googleapis.com/css?family=Dancing+Script|Roboto'); 3 | @import url('https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css'); 4 | @import url('https://fonts.googleapis.com/icon?family=Material+Icons'); 5 | 6 | * { 7 | box-sizing: border-box; 8 | } 9 | body { 10 | background: #fafafa; 11 | margin: 0; 12 | padding: 0; 13 | font-family: 'Roboto', sans-serif; 14 | } 15 | .container { 16 | width: 100%; 17 | margin: 0 auto; 18 | padding: 0px 15px; 19 | //max-width: 1600px; 20 | } 21 | .text-right { 22 | text-align: right; 23 | } 24 | -------------------------------------------------------------------------------- /cypress/integration/core_spec.js: -------------------------------------------------------------------------------- 1 | const { clockMaker } = require('./shared'); 2 | 3 | describe('Kitchen Sink', function () { 4 | it('Open dialog and selecting a time must update input via directive', function () { 5 | 6 | cy.visit('http://localhost:8080'); 7 | cy.title().should('include', 'Amazing TimePicker') 8 | cy.get('button.test-open-dialog').click(); 9 | for (const number of clockMaker('hour')) { 10 | cy.get('.time-picker-clock').click(number.top, number.left); 11 | cy.wait(50); 12 | } 13 | cy.get('.atp-ref-dialog-close').click(); 14 | cy.wait(100); 15 | cy.get('#atp-ref-dialog-result').should('have.value', '09:00'); 16 | 17 | }); 18 | 19 | }); -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | } 18 | -------------------------------------------------------------------------------- /src/app/presentation/api/api.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ApiComponent } from './api.component'; 4 | 5 | describe('ApiComponent', () => { 6 | let component: ApiComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ApiComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ApiComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/presentation/tab/tab.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TabComponent } from './tab.component'; 4 | 5 | describe('TabComponent', () => { 6 | let component: TabComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TabComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TabComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | import { hmrBootstrap } from './hmr'; 8 | 9 | if (environment.production) { 10 | enableProdMode(); 11 | } 12 | 13 | const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule); 14 | 15 | if (environment.hmr) { 16 | if (module[ 'hot' ]) { 17 | hmrBootstrap(module, bootstrap); 18 | } else { 19 | console.error('HMR is not enabled for webpack-dev-server!'); 20 | console.log('Are you using the --hmr flag for ng serve?'); 21 | } 22 | } else { 23 | bootstrap(); 24 | } 25 | -------------------------------------------------------------------------------- /src/app/presentation/tabs/tabs.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TabsComponent } from './tabs.component'; 4 | 5 | describe('TabsComponent', () => { 6 | let component: TabsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TabsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TabsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /tmp 5 | /out-tsc 6 | 7 | # dependencies 8 | /node_modules 9 | /npm_dist 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | .history 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | yarn-error.log 37 | 38 | # e2e 39 | /e2e/*.js 40 | /e2e/*.map 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | cypress/screenshots 46 | -------------------------------------------------------------------------------- /src/index-github.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Amazing TimePicker | Angular 7, 2, 4, 5, 6 TimePicker | Clock TimePicker | 7 | Material time picker 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/app/snack-ad/snack-ad.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SnackAdComponent } from './snack-ad.component'; 4 | 5 | describe('SnackAdComponent', () => { 6 | let component: SnackAdComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SnackAdComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SnackAdComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/presentation/example/example.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExampleComponent } from './example.component'; 4 | 5 | describe('ExampleComponent', () => { 6 | let component: ExampleComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExampleComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExampleComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/index-docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Amazing TimePicker | Official Amazing time picker | Angular 7, 2, 4, 5, 6 7 | TimePicker | Clock TimePicker | Material time picker 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/app/atp-library/time-picker/time-picker.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TimePickerComponent } from './time-picker.component'; 4 | 5 | describe('TimePickerComponent', () => { 6 | let component: TimePickerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TimePickerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TimePickerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /cypress/integration/shared.js: -------------------------------------------------------------------------------- 1 | const clockMaker = (type = 'minute') => { 2 | 3 | const items = []; 4 | const timeVal = (type === 'minute') ? 60 : 12; 5 | const timeStep = (type === 'minute') ? 5 : 1; 6 | const timeStart = (type === 'minute') ? 0 : 1; 7 | 8 | const r = 124; 9 | const j = r - 25; 10 | 11 | for (let min = timeStart; min <= timeVal; min += timeStep) { 12 | if (min !== 60) { 13 | const str = String (min); 14 | const x = j * Math.sin(Math.PI * 2 * (min / timeVal)); 15 | const y = j * Math.cos(Math.PI * 2 * (min / timeVal)); 16 | 17 | items.push({ 18 | time: str, 19 | left: (x + r - 17), 20 | top: (-y + r - 17), 21 | type 22 | }); 23 | } 24 | } 25 | return items; 26 | } 27 | 28 | module.exports = { 29 | clockMaker 30 | }; 31 | -------------------------------------------------------------------------------- /src/app/snack-ad/snack-ad.component.scss: -------------------------------------------------------------------------------- 1 | :host.snack-ad { 2 | overflow: hidden; 3 | background-color: #323232; 4 | padding: 10px 24px; 5 | color: #fff; 6 | opacity: 0.9; 7 | min-width: 288px; 8 | font-size: 1.1em; 9 | max-width: 568px; 10 | position: fixed; 11 | bottom: 15px; 12 | left: 15px; 13 | right: 15px; 14 | display: flex; 15 | flex-wrap: nowrap; 16 | align-items: center; 17 | z-index: 999; 18 | 19 | .snackbar-message { 20 | overflow: hidden; 21 | line-height: 1.2em; 22 | min-height: 1.2em; 23 | max-height: 2.4em; 24 | flex: 1; 25 | } 26 | .snackbar-action { 27 | text-transform: uppercase; 28 | font-weight: bold; 29 | margin-left: 48px; 30 | color: #bf3727; 31 | 32 | a { 33 | text-decoration: none; 34 | color: #bf3727; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/app/atp-library/atp-time-picker/atp-time-picker.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { OwsTimePickerComponent } from './atp-time-picker.component'; 4 | 5 | describe('OwsTimePickerComponent', () => { 6 | let component: OwsTimePickerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ OwsTimePickerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(OwsTimePickerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-arabic/example-arabic.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExampleArabicComponent } from './example-arabic.component'; 4 | 5 | describe('ExampleArabicComponent', () => { 6 | let component: ExampleArabicComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExampleArabicComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExampleArabicComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-hour/example-hour.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExamplePersianComponent } from './example-persian.component'; 4 | 5 | describe('ExamplePersianComponent', () => { 6 | let component: ExamplePersianComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExamplePersianComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExamplePersianComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-material/example-material.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExamplePersianComponent } from './example-persian.component'; 4 | 5 | describe('ExamplePersianComponent', () => { 6 | let component: ExamplePersianComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExamplePersianComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExamplePersianComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-persian/example-persian.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExamplePersianComponent } from './example-persian.component'; 4 | 5 | describe('ExamplePersianComponent', () => { 6 | let component: ExamplePersianComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExamplePersianComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExamplePersianComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/presentation/basic-layout/basic-layout.component.scss: -------------------------------------------------------------------------------- 1 | 2 | .npm{ 3 | background: #717171; 4 | padding: 10px; 5 | border-radius: 5px; 6 | color: #ffffcc; 7 | } 8 | .right-acc{ 9 | position: -webkit-sticky; 10 | position: sticky; 11 | top: 48px; 12 | text-align: center; 13 | h1{ 14 | text-align: center; 15 | font-size: 1.4em; 16 | border-bottom: 1px solid #ddd; 17 | padding-bottom: 15px; 18 | margin-bottom: 15px; 19 | margin-top: 43px; 20 | } 21 | } 22 | 23 | 24 | .tabs { 25 | display: flex; 26 | flex-direction: row; 27 | list-style: none; 28 | 29 | .tab a { 30 | cursor: pointer; 31 | color: rgba(238,110,115,0.7); 32 | display: block; 33 | height: 100%; 34 | padding: 7px 24px; 35 | font-size: 14px; 36 | text-overflow: ellipsis; 37 | overflow: hidden; 38 | -webkit-transition: color .28s ease; 39 | transition: color .28s ease; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-time-picker.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { TimePickerComponent } from './time-picker/time-picker.component'; 4 | import { AtpTimePickerComponent } from './atp-time-picker/atp-time-picker.component'; 5 | import { AmazingTimePickerService } from './atp-time-picker.service'; 6 | import { AtpDirective } from './atp.directive'; 7 | import { AtpCoreService } from './atp-core.service'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | ], 13 | declarations: [ 14 | TimePickerComponent, 15 | AtpTimePickerComponent, 16 | AtpDirective 17 | ], 18 | providers: [ 19 | AmazingTimePickerService, 20 | AtpCoreService 21 | ], 22 | entryComponents: [TimePickerComponent], 23 | exports: [ 24 | TimePickerComponent, 25 | AtpTimePickerComponent, 26 | AtpDirective 27 | ] 28 | }) 29 | export class AmazingTimePickerModule { } 30 | -------------------------------------------------------------------------------- /src/app/presentation/basic-layout/basic-layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-basic-layout', 5 | templateUrl: './basic-layout.component.html', 6 | styleUrls: ['./basic-layout.component.scss'] 7 | }) 8 | export class LayoutBasicComponent implements OnInit { 9 | public Directive = ``; 10 | public appModule = `import { BrowserModule } from '@angular/platform-browser'; 11 | import { NgModule } from '@angular/core'; 12 | import { AmazingTimePickerModule } from 'amazing-time-picker'; // this line you need 13 | import { AppComponent } from './app.component'; 14 | 15 | @NgModule({ 16 | declarations: [ 17 | AppComponent 18 | ], 19 | imports: [ 20 | BrowserModule, 21 | AmazingTimePickerModule // this line you need 22 | ], 23 | providers: [], 24 | bootstrap: [AppComponent] 25 | }) 26 | export class AppModule { }`; 27 | constructor() {} 28 | 29 | ngOnInit() {} 30 | } 31 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-arabic/example-arabic.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AmazingTimePickerService } from '../../../atp-library/atp-time-picker.service'; 3 | 4 | const encode = (x) => x.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { 5 | return '&#' + i.charCodeAt(0) + ';'; 6 | }); 7 | 8 | @Component({ 9 | selector: 'app-example-arabic', 10 | templateUrl: './example-arabic.component.html', 11 | styleUrls: ['./example-arabic.component.scss'] 12 | }) 13 | export class ExampleArabicComponent implements OnInit { 14 | public selectedTime: string; 15 | public sintax = '{{selectedTime}}'; 16 | 17 | constructor( 18 | private atp: AmazingTimePickerService, 19 | ) { } 20 | 21 | ngOnInit() { 22 | } 23 | 24 | public openArabic () { 25 | const amazingTimePicker = this.atp.open({ 26 | locale: 'ar' 27 | }); 28 | amazingTimePicker.afterClose().subscribe(time => { 29 | this.selectedTime = time; 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /scripts/init-test-beds.sh: -------------------------------------------------------------------------------- 1 | # Since this project is a dependecy, we should simulate the testing inside a another angular app, and make sure 2 | # this is working so far fine. 3 | 4 | # We assume that angular-cli is globally installed. 5 | # We are not cloning existing projects, we build with latest version of angular-cli 6 | # and install this dependency 7 | set -e; 8 | 9 | cd /tmp/; 10 | git clone https://github.com/owsolutions/amazing-time-picker-test-beds --depth=1 11 | cd amazing-time-picker-test-beds; 12 | 13 | cd /tmp/amazing-time-picker-test-beds/ng2-test-bed; 14 | npm install; 15 | npm install ${TRAVIS_BUILD_DIR}/npm_dist/dist.tgz; 16 | npm run build; 17 | 18 | cd /tmp/amazing-time-picker-test-beds/ng4-test-bed; 19 | npm install; 20 | npm install ${TRAVIS_BUILD_DIR}/npm_dist/dist.tgz; 21 | npm run build; 22 | 23 | cd /tmp/amazing-time-picker-test-beds/ng5-test-bed; 24 | npm install; 25 | npm install ${TRAVIS_BUILD_DIR}/npm_dist/dist.tgz; 26 | npm run build; 27 | 28 | echo "Now we have lifted all test beds!"; 29 | cd $TRAVIS_BUILD_DIR; -------------------------------------------------------------------------------- /src/app/presentation/tabs/tabs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, AfterContentInit, ContentChildren, QueryList } from '@angular/core'; 2 | import { TabComponent } from '../tab/tab.component'; 3 | 4 | @Component({ 5 | selector: 'app-tabs', 6 | templateUrl: './tabs.component.html', 7 | styleUrls: ['./tabs.component.scss'] 8 | }) 9 | export class TabsComponent implements AfterContentInit { 10 | @ContentChildren(TabComponent) tabs: QueryList; 11 | 12 | // contentChildren are set 13 | ngAfterContentInit() { 14 | // get all active tabs 15 | const activeTabs = this.tabs.filter((tab) => tab.active); 16 | 17 | // if there is no active tab set, activate the first 18 | if (activeTabs.length === 0) { 19 | this.selectTab(this.tabs.first); 20 | } 21 | } 22 | 23 | selectTab(tab: TabComponent) { 24 | // deactivate all tabs 25 | this.tabs.toArray().forEach( tabs => tabs.active = false); 26 | 27 | // activate the tab the user has clicked on. 28 | tab.active = true; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/app/presentation/example/example.component.scss: -------------------------------------------------------------------------------- 1 | #main-container { 2 | font-size: 17px; 3 | line-height: 30px; 4 | pre { 5 | line-height: 20px; 6 | font-size: 12px !important; 7 | } 8 | } 9 | .tab-wrapper { 10 | border: 1px solid #ddd; 11 | box-shadow: 0 2px 2px rgba(0, 0, 0, 0.24), 0 0 2px rgba(0, 0, 0, 0.12); 12 | .row { 13 | margin: 0; 14 | } 15 | .tab-header { 16 | padding: 15px; 17 | background: #efefef; 18 | .title { 19 | font-size: 17px; 20 | } 21 | } 22 | pre { 23 | background: transparent; 24 | border: 0; 25 | margin: 0; 26 | } 27 | .tab-content { 28 | padding: 15px; 29 | } 30 | } 31 | 32 | .pre-title { 33 | font-size: 1.5em; 34 | border-bottom: 1px solid #ddd; 35 | padding: 15px 0; 36 | } 37 | .pre-code { 38 | &.L { 39 | background: #eee; 40 | padding: 15px; 41 | display: block; 42 | } 43 | &.S { 44 | background: #eee; 45 | padding: 3px 5px; 46 | display: inline-block; 47 | margin: 0 5px; 48 | } 49 | } 50 | @import '../common'; 51 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | Amazing TimePicker | Angular 7, 2, 4, 5, 6 TimePicker | Clock TimePicker | Material time picker -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /dist/index-github.html: -------------------------------------------------------------------------------- 1 | Amazing TimePicker | Angular 7, 2, 4, 5, 6 TimePicker | Clock TimePicker | Material time picker -------------------------------------------------------------------------------- /src/app/presentation/examples/example-hour/example-hour.component.scss: -------------------------------------------------------------------------------- 1 | @import '../../common'; 2 | 3 | #main-container{ 4 | font-size: 17px; 5 | line-height: 30px; 6 | pre{ 7 | line-height: 20px; 8 | font-size: 12px !important; 9 | } 10 | } 11 | .tab-wrapper{ 12 | border: 1px solid #ddd; 13 | box-shadow: 0 2px 2px rgba(0,0,0,.24), 0 0 2px rgba(0,0,0,.12); 14 | .row{ 15 | margin: 0; 16 | } 17 | .tab-header{ 18 | padding: 15px; 19 | background: #EFEFEF; 20 | .title{ 21 | font-size: 17px; 22 | } 23 | } 24 | pre{ 25 | background: transparent; 26 | border: 0; 27 | margin: 0; 28 | } 29 | .tab-content{ 30 | padding: 15px; 31 | } 32 | } 33 | 34 | .pre-title{ 35 | font-size: 1.5em; 36 | border-bottom: 1px solid #ddd; 37 | padding: 15px 0; 38 | } 39 | .pre-code{ 40 | &.L{ 41 | background: #eee; 42 | padding: 15px; 43 | display: block; 44 | } 45 | &.S{ 46 | background: #eee; 47 | padding: 3px 5px; 48 | display: inline-block; 49 | margin: 0 5px; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-arabic/example-arabic.component.scss: -------------------------------------------------------------------------------- 1 | @import '../../common'; 2 | 3 | #main-container{ 4 | font-size: 17px; 5 | line-height: 30px; 6 | pre{ 7 | line-height: 20px; 8 | font-size: 12px !important; 9 | } 10 | } 11 | .tab-wrapper{ 12 | border: 1px solid #ddd; 13 | box-shadow: 0 2px 2px rgba(0,0,0,.24), 0 0 2px rgba(0,0,0,.12); 14 | .row{ 15 | margin: 0; 16 | } 17 | .tab-header{ 18 | padding: 15px; 19 | background: #EFEFEF; 20 | .title{ 21 | font-size: 17px; 22 | } 23 | } 24 | pre{ 25 | background: transparent; 26 | border: 0; 27 | margin: 0; 28 | } 29 | .tab-content{ 30 | padding: 15px; 31 | } 32 | } 33 | 34 | .pre-title{ 35 | font-size: 1.5em; 36 | border-bottom: 1px solid #ddd; 37 | padding: 15px 0; 38 | } 39 | .pre-code{ 40 | &.L{ 41 | background: #eee; 42 | padding: 15px; 43 | display: block; 44 | } 45 | &.S{ 46 | background: #eee; 47 | padding: 3px 5px; 48 | display: inline-block; 49 | margin: 0 5px; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-persian/example-persian.component.scss: -------------------------------------------------------------------------------- 1 | @import '../../common'; 2 | 3 | #main-container{ 4 | font-size: 17px; 5 | line-height: 30px; 6 | pre{ 7 | line-height: 20px; 8 | font-size: 12px !important; 9 | } 10 | } 11 | .tab-wrapper{ 12 | border: 1px solid #ddd; 13 | box-shadow: 0 2px 2px rgba(0,0,0,.24), 0 0 2px rgba(0,0,0,.12); 14 | .row{ 15 | margin: 0; 16 | } 17 | .tab-header{ 18 | padding: 15px; 19 | background: #EFEFEF; 20 | .title{ 21 | font-size: 17px; 22 | } 23 | } 24 | pre{ 25 | background: transparent; 26 | border: 0; 27 | margin: 0; 28 | } 29 | .tab-content{ 30 | padding: 15px; 31 | } 32 | } 33 | 34 | .pre-title{ 35 | font-size: 1.5em; 36 | border-bottom: 1px solid #ddd; 37 | padding: 15px 0; 38 | } 39 | .pre-code{ 40 | &.L{ 41 | background: #eee; 42 | padding: 15px; 43 | display: block; 44 | } 45 | &.S{ 46 | background: #eee; 47 | padding: 3px 5px; 48 | display: inline-block; 49 | margin: 0 5px; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | }).compileComponents(); 12 | })); 13 | 14 | it('should create the app', async(() => { 15 | const fixture = TestBed.createComponent(AppComponent); 16 | const app = fixture.debugElement.componentInstance; 17 | expect(app).toBeTruthy(); 18 | })); 19 | 20 | it(`should have as title 'app'`, async(() => { 21 | const fixture = TestBed.createComponent(AppComponent); 22 | const app = fixture.debugElement.componentInstance; 23 | expect(app.title).toEqual('app'); 24 | })); 25 | 26 | it('should render title in a h1 tag', async(() => { 27 | const fixture = TestBed.createComponent(AppComponent); 28 | fixture.detectChanges(); 29 | const compiled = fixture.debugElement.nativeElement; 30 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); 31 | })); 32 | }); 33 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /src/app/presentation/example/example.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AmazingTimePickerService } from '../../atp-library/atp-time-picker.service'; 3 | 4 | @Component({ 5 | selector: 'app-example', 6 | templateUrl: './example.component.html', 7 | styleUrls: ['./example.component.scss'] 8 | }) 9 | export class ExampleComponent { 10 | public selectedTime: string; 11 | public selectedTimeDark = '18:33'; 12 | public sintax = '{{selectedTime}}'; 13 | 14 | constructor(private atp: AmazingTimePickerService) { } 15 | 16 | open() { 17 | const amazingTimePicker = this.atp.open(); 18 | amazingTimePicker.afterClose().subscribe(time => { 19 | this.selectedTime = time; 20 | }); 21 | } 22 | 23 | openDark() { 24 | const amazingTimePicker = this.atp.open({ 25 | time: this.selectedTimeDark, 26 | theme: 'dark', 27 | changeToMinutes: true, 28 | animation: 'rotate', 29 | arrowStyle: { 30 | background: 'red', 31 | color: 'white' 32 | } 33 | }); 34 | amazingTimePicker.afterClose().subscribe(time => { 35 | this.selectedTimeDark = time; 36 | }); 37 | } 38 | toggleTab() { 39 | 40 | } 41 | 42 | onCustomEvent(response: string): void { 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-persian/example-persian.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AmazingTimePickerService } from '../../../atp-library/atp-time-picker.service'; 3 | 4 | const encode = (x) => x.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { 5 | return '&#' + i.charCodeAt(0) + ';'; 6 | }); 7 | 8 | @Component({ 9 | selector: 'app-example-persian', 10 | templateUrl: './example-persian.component.html', 11 | styleUrls: ['./example-persian.component.scss'] 12 | }) 13 | export class ExamplePersianComponent implements OnInit { 14 | interface = encode(` 15 | export interface IDisplayPreference { 16 | minute?: Function; 17 | hour?: Function; 18 | onlyHour?: boolean; 19 | separator?: string; 20 | labels?: { 21 | ok?: string; 22 | cancel?: string; 23 | }; 24 | period?(period: 'AM' | 'PM'); 25 | clockMinute?(minute: any); 26 | clockHour?(hour: any); 27 | } 28 | `); 29 | public selectedTime: string; 30 | public sintax = '{{selectedTime}}'; 31 | 32 | constructor( 33 | private atp: AmazingTimePickerService, 34 | ) { } 35 | 36 | ngOnInit() { 37 | } 38 | 39 | public openPersian () { 40 | const amazingTimePicker = this.atp.open({ 41 | locale: 'fa' 42 | }); 43 | amazingTimePicker.afterClose().subscribe(time => { 44 | this.selectedTime = time; 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-hour/example-hour.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AmazingTimePickerService } from '../../../atp-library/atp-time-picker.service'; 3 | 4 | const encode = (x) => x.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { 5 | return '&#' + i.charCodeAt(0) + ';'; 6 | }); 7 | 8 | @Component({ 9 | selector: 'app-example-hour', 10 | templateUrl: './example-hour.component.html', 11 | styleUrls: ['./example-hour.component.scss'] 12 | }) 13 | export class ExampleHourComponent implements OnInit { 14 | interface = encode(` 15 | export interface IDisplayPreference { 16 | minute?: Function; 17 | hour?: Function; 18 | onlyHour?: boolean; 19 | separator?: string; 20 | labels?: { 21 | ok?: string; 22 | cancel?: string; 23 | }; 24 | period?(period: 'AM' | 'PM'); 25 | clockMinute?(minute: any); 26 | clockHour?(hour: any); 27 | } 28 | `); 29 | public selectedTime: string; 30 | public sintax = '{{selectedTime}}'; 31 | 32 | constructor( 33 | private atp: AmazingTimePickerService, 34 | ) { } 35 | 36 | ngOnInit() { 37 | } 38 | 39 | public openHour () { 40 | const amazingTimePicker = this.atp.open({ 41 | time: '3:30', 42 | onlyHour: true 43 | }); 44 | amazingTimePicker.afterClose().subscribe(time => { 45 | this.selectedTime = time; 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-material/example-material.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AmazingTimePickerService } from '../../../atp-library/atp-time-picker.service'; 3 | 4 | const encode = (x) => x.replace(/[\u00A0-\u9999<>\&]/gim, function(i) { 5 | return '&#' + i.charCodeAt(0) + ';'; 6 | }); 7 | 8 | @Component({ 9 | selector: 'app-example-material', 10 | templateUrl: './example-material.component.html', 11 | styleUrls: ['./example-material.component.scss'] 12 | }) 13 | export class ExampleMaterialComponent implements OnInit { 14 | interface = encode(` 15 | export interface IDisplayPreference { 16 | minute?: Function; 17 | hour?: Function; 18 | separator?: string; 19 | labels?: { 20 | ok?: string; 21 | cancel?: string; 22 | }; 23 | period?(period: 'AM' | 'PM'); 24 | clockMinute?(minute: any); 25 | clockHour?(hour: any); 26 | } 27 | `); 28 | public selectedTime = '18:30'; 29 | public sintax = '{{selectedTime}}'; 30 | 31 | constructor( 32 | private atp: AmazingTimePickerService, 33 | ) { } 34 | 35 | ngOnInit() { 36 | } 37 | 38 | public openByTheme (theme: any) { 39 | const amazingTimePicker = this.atp.open({ 40 | time: this.selectedTime, 41 | theme 42 | }); 43 | amazingTimePicker.afterClose().subscribe(time => { 44 | this.selectedTime = time; 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/inline.844ab025a6723b51af71.bundle.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,u){for(var a,i,f,l=0,p=[];l 2 | 3 | 4 | 5 | Amazing TimePicker | Angular 7, 2, 4, 5, 6 TimePicker | Clock TimePicker | Material time picker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 23 | 24 | 25 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/app/atp-library/definitions.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Rx'; 2 | 3 | /** 4 | * AmazingTimePicker configuration 5 | * when calling open() function from 'AmazingTimePickerService' passed as parameter 6 | */ 7 | export interface TimePickerConfig { 8 | time?: string; 9 | theme?: 10 | | 'dark' 11 | | 'light' 12 | | 'material-red' 13 | | 'material-green' 14 | | 'material-blue' 15 | | 'material-purple' 16 | | 'material-orange'; 17 | rangeTime?: RangeTime; 18 | arrowStyle?: Pallete; 19 | locale?: string; 20 | preference?: IDisplayPreference; 21 | changeToMinutes?: boolean; 22 | animation?: 'fade' | 'rotate' | false; 23 | onlyHour?: boolean; 24 | onlyMinute?: boolean; 25 | onlyAM?: boolean; 26 | onlyPM?: boolean; 27 | } 28 | 29 | export interface RangeTime { 30 | start: string; 31 | end: string; 32 | } 33 | 34 | export interface Pallete { 35 | background?: string; 36 | color?: string; 37 | } 38 | 39 | export interface IDialogResult { 40 | afterClose(): Observable; 41 | } 42 | 43 | export interface IClockNumber { 44 | time: String; 45 | left: string; 46 | top: string; 47 | type: String; 48 | } 49 | 50 | export interface IDisplayPreference { 51 | minute?: Function; 52 | hour?: Function; 53 | separator?: string; 54 | labels?: { 55 | ok?: string; 56 | cancel?: string; 57 | }; 58 | period?(period: 'AM' | 'PM'); 59 | clockMinute?(minute: any); 60 | clockHour?(hour: any); 61 | } 62 | 63 | export interface ITime { 64 | minute: number; 65 | hour: number; 66 | ampm: 'AM' | 'PM'; 67 | } 68 | -------------------------------------------------------------------------------- /src/app/presentation/full-layout/full-layout.component.scss: -------------------------------------------------------------------------------- 1 | header { 2 | width: 100%; 3 | overflow: hidden; 4 | height: 275px; 5 | text-align: center; 6 | color: #555; 7 | font-family: 'Dancing Script', cursive; 8 | background: #00c6ff; 9 | background: -webkit-linear-gradient(to bottom, #becfe4, #fafafa) url(); 10 | background: linear-gradient(to bottom, #becfe4, #fafafa); 11 | } 12 | .forker { 13 | position: absolute; 14 | top: 0; 15 | right: 0; 16 | width: 150px; 17 | height: 150px; 18 | z-index: 100; 19 | } 20 | .nav-wrapper { 21 | .container { 22 | text-align: center; 23 | height: 150px; 24 | .brand-logo { 25 | p { 26 | font-size: 20px; 27 | margin: 0px; 28 | } 29 | img { 30 | margin-top: 30px; 31 | width: 100px; 32 | } 33 | } 34 | } 35 | } 36 | .nav-content { 37 | position: sticky; 38 | top: 0; 39 | z-index: 1000; 40 | background: #ee6e72; 41 | box-shadow: 0px 6px 8px 0px #ddd; 42 | .tab { 43 | text-transform: none; 44 | a { 45 | &.active { 46 | border-bottom: 3px solid #fff; 47 | } 48 | font-size: 16px !important; 49 | } 50 | } 51 | } 52 | .npm { 53 | background: #717171; 54 | padding: 10px; 55 | border-radius: 5px; 56 | color: #ffffcc; 57 | } 58 | .right-acc { 59 | position: -webkit-sticky; 60 | position: sticky; 61 | top: 48px; 62 | text-align: center; 63 | h1 { 64 | text-align: center; 65 | font-size: 1.4em; 66 | border-bottom: 1px solid #ddd; 67 | padding-bottom: 15px; 68 | margin-bottom: 15px; 69 | margin-top: 43px; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app/presentation/api/api.component.scss: -------------------------------------------------------------------------------- 1 | #main-container{ 2 | font-size: 17px; 3 | line-height: 30px; 4 | pre{ 5 | line-height: 20px; 6 | font-size: 12px !important; 7 | } 8 | } 9 | .tab-wrapper{ 10 | border: 1px solid #ddd; 11 | box-shadow: 0 2px 2px rgba(0,0,0,.24), 0 0 2px rgba(0,0,0,.12); 12 | .row{ 13 | margin: 0; 14 | } 15 | .tab-header{ 16 | padding: 15px; 17 | background: #EFEFEF; 18 | .title{ 19 | font-size: 17px; 20 | } 21 | } 22 | pre{ 23 | background: transparent; 24 | border: 0; 25 | margin: 0; 26 | } 27 | .tab-content{ 28 | padding: 15px; 29 | } 30 | } 31 | 32 | .pre-title{ 33 | font-size: 1.5em; 34 | border-bottom: 1px solid #ddd; 35 | padding: 15px 0; 36 | } 37 | 38 | .pre-code{ 39 | &.L{ 40 | background: #eee; 41 | padding: 10px; 42 | display: inline-block; 43 | margin-top: 0; 44 | } 45 | &.S{ 46 | background: #eee; 47 | padding: 3px 5px; 48 | display: inline-block; 49 | margin: 0 5px; 50 | } 51 | } 52 | .box-title{ 53 | font-size: 1.1em; 54 | color:#555; 55 | margin: 25px 0; 56 | } 57 | .box{ 58 | border: 1px solid #ddd; 59 | box-shadow: 0 2px 2px rgba(0,0,0,.24), 0 0 2px rgba(0,0,0,.12); 60 | .box-header{ 61 | padding: 15px; 62 | background: #eee; 63 | } 64 | .box-content{ 65 | p{ 66 | padding: 15px; 67 | } 68 | table{ 69 | .header{ 70 | background: #eee; 71 | } 72 | tr{ 73 | td, th{ 74 | padding: 15px; 75 | } 76 | } 77 | } 78 | .box-header{ 79 | color: #8f95c3; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/app/atp-library/preferences.ts: -------------------------------------------------------------------------------- 1 | import { IDisplayPreference } from './definitions'; 2 | 3 | const arabic = new Intl.NumberFormat('ar-AE'); 4 | const persian = new Intl.NumberFormat('fa-IR'); 5 | 6 | export const PersianPreference: IDisplayPreference = { 7 | hour: (x) => persian.format(x), 8 | minute: (x) => { 9 | let exp = persian.format(x); 10 | if (exp.length === 1) { 11 | exp = persian.format(0) + exp; 12 | } 13 | return exp; 14 | }, 15 | separator: ':', 16 | period: (x) => x === 'AM' ? 'صبح' : 'عصر', 17 | clockHour: (x) => persian.format(x), 18 | clockMinute: (x) => persian.format(x), 19 | labels: { 20 | ok: 'تایید', 21 | cancel: 'لغو' 22 | } 23 | }; 24 | 25 | export const ArabicPreference: IDisplayPreference = { 26 | hour: (x) => arabic.format(x), 27 | minute: (x) => { 28 | let exp = arabic.format(x); 29 | if (exp.length === 1) { 30 | exp = arabic.format(0) + exp; 31 | } 32 | return exp; 33 | }, 34 | separator: ':', 35 | period: (x) => x === 'AM' ? 'صباحا' : 'مساء', 36 | clockHour: (x) => arabic.format(x), 37 | clockMinute: (x) => arabic.format(x), 38 | labels: { 39 | ok: 'حسنا', 40 | cancel: 'إلغاء' 41 | } 42 | }; 43 | 44 | export const ChinesePreference: IDisplayPreference = { 45 | hour: (x) => x, 46 | minute: (x) => { 47 | let exp = x; 48 | if (exp.length === 1) { 49 | exp = '۰' + exp; 50 | } 51 | return exp; 52 | }, 53 | separator: ':', 54 | period: (x) => x === 'AM' ? '上午' : '下午', 55 | clockHour: (x) => x, 56 | clockMinute: (x) => x, 57 | labels: { 58 | ok: '确定', 59 | cancel: '取消' 60 | } 61 | }; 62 | 63 | export const Preference = (locale: string): IDisplayPreference => { 64 | switch (locale) { 65 | case 'fa': 66 | return PersianPreference; 67 | case 'ar': 68 | return ArabicPreference; 69 | case 'zh': 70 | return ChinesePreference; 71 | default: 72 | return null; 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { appRoutersGenerator } from './app.router'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | 6 | import { AmazingTimePickerModule } from './atp-library/atp-time-picker.module'; 7 | import { AppComponent } from './app.component'; 8 | import { TabsComponent } from './presentation/tabs/tabs.component'; 9 | import { TabComponent } from './presentation/tab/tab.component'; 10 | import { ExampleComponent } from './presentation/example/example.component'; 11 | import { ApiComponent } from './presentation/api/api.component'; 12 | import { ExamplePersianComponent } from './presentation/examples/example-persian/example-persian.component'; 13 | import { ExampleArabicComponent } from './presentation/examples/example-arabic/example-arabic.component'; 14 | import { ExampleMaterialComponent } from './presentation/examples/example-material/example-material.component'; 15 | import { ExampleHourComponent } from './presentation/examples/example-hour/example-hour.component'; 16 | import { SnackAdComponent } from './snack-ad/snack-ad.component'; 17 | import { FullLayoutComponent } from './presentation/full-layout/full-layout.component'; 18 | import { LayoutBasicComponent } from './presentation/basic-layout/basic-layout.component'; 19 | 20 | @NgModule({ 21 | declarations: [ 22 | AppComponent, 23 | TabsComponent, 24 | TabComponent, 25 | ExampleComponent, 26 | ApiComponent, 27 | ExamplePersianComponent, 28 | ExampleHourComponent, 29 | ExampleArabicComponent, 30 | ExampleMaterialComponent, 31 | SnackAdComponent, 32 | FullLayoutComponent, 33 | LayoutBasicComponent 34 | ], 35 | imports: [ 36 | BrowserModule, 37 | BrowserAnimationsModule, 38 | AmazingTimePickerModule, 39 | appRoutersGenerator() 40 | ], 41 | providers: [], 42 | bootstrap: [AppComponent] 43 | }) 44 | export class AppModule {} 45 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-time-picker/atp-time-picker.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, ViewContainerRef, Output, ComponentFactoryResolver, OnInit, ApplicationRef, EventEmitter 2 | } from '@angular/core'; 3 | import { TimePickerComponent } from '../time-picker/time-picker.component'; 4 | import { TimePickerConfig } from '../definitions'; 5 | 6 | // We need to import like this, because of backward compatibility of angular 7 | /* tslint:disable */ 8 | import { Subject } from 'rxjs'; 9 | /* tslint:enable */ 10 | 11 | @Component({ 12 | selector: 'atp-time-picker', 13 | templateUrl: './atp-time-picker.component.html', 14 | styleUrls: ['./atp-time-picker.component.scss'] 15 | }) 16 | 17 | export class AtpTimePickerComponent implements OnInit { 18 | @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef; 19 | @Output() timeSelected: EventEmitter = new EventEmitter(); 20 | public config: TimePickerConfig = {}; 21 | 22 | constructor( 23 | private resolver: ComponentFactoryResolver, 24 | private appRef: ApplicationRef, 25 | ) {} 26 | 27 | ngOnInit() { 28 | let config = this.config; 29 | config = { 30 | time: config.time || '00:00', 31 | theme: ['light', 'dark', 'material'].indexOf(config.theme) > 0 ? config.theme : 'light' || config.theme || 'light', 32 | rangeTime: config.rangeTime || {start: '0:0', end: '24:0'}, 33 | arrowStyle: config.arrowStyle || {} 34 | }; 35 | config.arrowStyle = { 36 | background: (config.arrowStyle.background) ? 37 | config.arrowStyle.background : config.theme !== undefined ? 38 | config.theme === 'dark' ? 'rgb(128, 203, 196)' : 'blue' : 'blue', 39 | color: config.arrowStyle.color || '#fff' 40 | }; 41 | const cfr = this.resolver.resolveComponentFactory(TimePickerComponent); 42 | const tsc = this.container.createComponent(cfr); 43 | tsc.instance.subject = new Subject(); 44 | tsc.instance._ref = tsc; 45 | tsc.instance.appRef = this.appRef; 46 | tsc.instance.timerElement = ''; 47 | tsc.instance.config = config; 48 | tsc.instance.activeModal = true; 49 | tsc.instance.isPopup = false; 50 | tsc.instance.ParseStringToTime(config.time); 51 | tsc.instance.subject.asObservable().subscribe(time => { 52 | this.timeSelected.emit(time); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/presentation/basic-layout/basic-layout.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Amazing Time Picker (Clock)

3 | Build Status 12 | License: MIT 19 | 20 |

Retain your Angular project a time picker.

21 |

22 | A visual time picker for angular 2+ projects. You can use this timepicker 23 | with Angular 2, 4, 5, 6, 7 and Angular Material. 24 |

25 | 26 |
npm i amazing-time-picker --save
27 | 28 |

Installing

29 | 30 | You need to install this repository as dependency and import it to your 31 | `app.module.ts` in `imports` section. then, open your `app.module.ts` or 32 | other module that you want to use timepicker among, and import and add it to 33 | the `imports` section: 34 | 35 |
{{appModule}}
36 | 37 | This helps your angular project to build and compile it and let you use it. 38 | ## Using in component markup After you have installed this module, you can 39 | use it within your html templates and give the directive to the any 40 | tag. When user closes the dialog, it's gonna update the input 41 | value and will listen to input click event to open the dialog. 42 | 43 |
{{Directive}}
44 | 45 | 46 | 47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /src/app/atp-library/time-picker/time-picker.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
{{GetHour()}}
7 | {{GetSeparator()}} 8 |
{{GetMinute()}}
10 |
11 |
12 |
{{GetPeriod('AM')}}
13 |
{{GetPeriod('PM')}}
14 | 15 |
16 |
17 |
18 |
19 | 25 |
26 |
27 | 28 |
29 |
30 |
31 | 35 |
36 |
-------------------------------------------------------------------------------- /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 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | import 'core-js/es6/symbol'; 23 | import 'core-js/es6/object'; 24 | import 'core-js/es6/function'; 25 | import 'core-js/es6/parse-int'; 26 | import 'core-js/es6/parse-float'; 27 | import 'core-js/es6/number'; 28 | import 'core-js/es6/math'; 29 | import 'core-js/es6/string'; 30 | import 'core-js/es6/date'; 31 | import 'core-js/es6/array'; 32 | import 'core-js/es6/regexp'; 33 | import 'core-js/es6/map'; 34 | import 'core-js/es6/weak-map'; 35 | import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /src/app/atp-library/atp.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ViewContainerRef, Output, EventEmitter, HostListener, ElementRef } from '@angular/core'; 2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; 3 | import { AmazingTimePickerService } from './atp-time-picker.service'; 4 | 5 | @Directive({ 6 | selector: 'input[atp-time-picker]', 7 | providers: [{ 8 | provide: NG_VALUE_ACCESSOR, 9 | useExisting: AtpDirective, 10 | multi: true 11 | }] 12 | }) 13 | export class AtpDirective implements ControlValueAccessor { 14 | 15 | @Output() myClick = new EventEmitter(); 16 | 17 | private elementRef: ElementRef; 18 | private onChange = (x: any): void => {}; 19 | constructor( 20 | public viewContainerRef: ViewContainerRef, 21 | private atp: AmazingTimePickerService) { 22 | this.elementRef = this.viewContainerRef.element; 23 | } 24 | 25 | @HostListener('click', ['$event']) 26 | onClick(e) { 27 | const ele = this.viewContainerRef.element.nativeElement; 28 | const time = ele.value; 29 | const theme = ele.getAttribute('theme'); 30 | const start = ele.getAttribute('start'); 31 | const end = ele.getAttribute('end'); 32 | const locale = ele.getAttribute('locale') || 'en'; 33 | const changeToMinutes = ele.getAttribute('changeToMinutes') === 'true'; 34 | const animation = ele.getAttribute('animation'); 35 | const preference = ele.getAttribute('preference') || null; 36 | const onlyHour = ele.getAttribute('onlyHour') === 'true'; 37 | const onlyMinute = ele.getAttribute('onlyMinute') === 'true'; 38 | const onlyAM = ele.getAttribute('onlyAM') === 'true'; 39 | const onlyPM = ele.getAttribute('onlyPM') === 'true'; 40 | let arrowStyle = ele.getAttribute('arrowStyle'); 41 | arrowStyle = (arrowStyle) ? JSON.parse(arrowStyle.replace(new RegExp('\'', 'g'), '"')) : ''; 42 | const timePickerFunction = this.atp.open({ 43 | time, 44 | theme, 45 | rangeTime: { start, end}, 46 | 'arrowStyle': arrowStyle, 47 | locale, 48 | changeToMinutes, 49 | animation, 50 | onlyHour, 51 | onlyMinute, 52 | onlyAM, 53 | onlyPM, 54 | preference 55 | }); 56 | 57 | timePickerFunction.afterClose().subscribe(retTime => { 58 | this.writeValue(retTime); // update the native element 59 | this.onChange(retTime); // update the form value (if there's a form) 60 | }); 61 | } 62 | 63 | @HostListener('input', ['$event']) 64 | onInput(e: any) { 65 | this.onChange(e.srcElement.value); 66 | } 67 | 68 | writeValue(value: any) { 69 | if (this.elementRef) { 70 | this.elementRef.nativeElement.value = value; 71 | } 72 | } 73 | 74 | registerOnChange(fn: any) { 75 | this.onChange = fn; 76 | } 77 | 78 | registerOnTouched(fn) { } 79 | } 80 | -------------------------------------------------------------------------------- /src/app/presentation/api/api.component.html: -------------------------------------------------------------------------------- 1 |

API reference for Amazing Time Picker

2 |
import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
3 |
4 | 5 |

Directives

6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 |
NameDescription
atp-time-pickerYou can add this directive to any input element. It will read the value of element, if its there with 20 | 00:00 format, and it is compatible with HTML 5 input time. 21 |
25 |
26 |
27 | 28 |

Services

29 | You can use amazing time picker service in order to open dialog programmatically. 30 | You can simply import this service to any component that might need to display a dialog. 31 | 32 |

Methods

33 |
34 |
35 | open 36 |
37 |
38 |

39 | Opens the dialog, and returns a function that you can subscribe to, so when dialog is 40 | closed, it's gonna give you the result as a Observable {{'<'}}string{{'>'}} 41 |

42 |
43 | Parameters 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 70 | 71 | 72 |
NameDescription
time: string 56 | Default time when Timepicker dialog appears 57 |
theme: string 62 | Select theme to change dialog style 63 |
arrowStyle: Pallete 68 | Change arrow color of Timepicker 69 |
73 |
74 |
75 |
76 |
77 |
78 | afterClose 79 |
80 |
81 |

82 | Gets an observable that is notified when the dialog is selected. 83 |

84 |
85 | return 86 |
87 |

88 | Observabel{{'<'}}string{{'>'}} 89 |

90 |
91 |
92 | -------------------------------------------------------------------------------- /.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "amazing-time-picker" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": ["assets", "favicon.ico"], 11 | "index": "index.html", 12 | "main": "main.ts", 13 | "polyfills": "polyfills.ts", 14 | "test": "test.ts", 15 | "tsconfig": "tsconfig.app.json", 16 | "testTsconfig": "tsconfig.spec.json", 17 | "prefix": "app", 18 | "styles": ["styles.scss"], 19 | "scripts": [], 20 | "environmentSource": "environments/environment.ts", 21 | "environments": { 22 | "dev": "environments/environment.ts", 23 | "hmr": "environments/environment.hmr.ts", 24 | "prod": "environments/environment.prod.ts" 25 | } 26 | }, 27 | { 28 | "root": "src", 29 | "outDir": "docs", 30 | "assets": ["assets", "favicon.ico"], 31 | "index": "index-docs.html", 32 | "deployUrl": "/amazing-time-picker/", 33 | "baseHref": "/amazing-time-picker/", 34 | "main": "main.ts", 35 | "polyfills": "polyfills.ts", 36 | "test": "test.ts", 37 | "tsconfig": "tsconfig.app.json", 38 | "testTsconfig": "tsconfig.spec.json", 39 | "prefix": "app", 40 | "styles": [], 41 | "scripts": [], 42 | "environmentSource": "environments/environment.ts", 43 | "environments": { 44 | "dev": "environments/environment.ts", 45 | "hmr": "environments/environment.hmr.ts", 46 | "docs": "environments/environment.docs.ts" 47 | } 48 | }, 49 | { 50 | "root": "src", 51 | "outDir": "dist", 52 | "assets": ["assets", "favicon.ico"], 53 | "index": "index-github.html", 54 | "main": "main.ts", 55 | "polyfills": "polyfills.ts", 56 | "test": "test.ts", 57 | "tsconfig": "tsconfig.app.json", 58 | "testTsconfig": "tsconfig.spec.json", 59 | "prefix": "app", 60 | "styles": ["styles.scss"], 61 | "scripts": [], 62 | "environmentSource": "environments/environment.ts", 63 | "environments": { 64 | "dev": "environments/environment.ts", 65 | "hmr": "environments/environment.hmr.ts", 66 | "prod": "environments/environment.prod.ts" 67 | } 68 | } 69 | ], 70 | "e2e": { 71 | "protractor": { 72 | "config": "./protractor.conf.js" 73 | } 74 | }, 75 | "lint": [ 76 | { 77 | "project": "src/tsconfig.app.json", 78 | "exclude": "**/node_modules/**" 79 | }, 80 | { 81 | "project": "src/tsconfig.spec.json", 82 | "exclude": "**/node_modules/**" 83 | }, 84 | { 85 | "project": "e2e/tsconfig.e2e.json", 86 | "exclude": "**/node_modules/**" 87 | } 88 | ], 89 | "test": { 90 | "karma": { 91 | "config": "./karma.conf.js" 92 | } 93 | }, 94 | "defaults": { 95 | "styleExt": "scss", 96 | "component": {} 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/app/presentation/full-layout/full-layout.component.html: -------------------------------------------------------------------------------- 1 | 2 | Fork me on GitHub 8 | 9 | 10 | 19 | 31 |
32 |
33 |
34 |
35 |

Amazing Time Picker (Clock)

36 | Build Status 45 | License: MIT 52 | 53 |

Retain your Angular project a time picker.

54 |

55 | A visual time picker for angular 2+ projects. You can use this 56 | timepicker with Angular 2, 4, 5, 6, 7 and Angular Material. 57 |

58 | 59 |
npm i amazing-time-picker --save
60 |
61 |
62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-time-picker.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Injector, ApplicationRef, ComponentFactoryResolver, EmbeddedViewRef } from '@angular/core'; 2 | import { TimePickerComponent } from './time-picker/time-picker.component'; 3 | import { TimePickerConfig, IDialogResult } from './definitions'; 4 | 5 | // We need to import like this, because of backward compatibility of angular 6 | /* tslint:disable */ 7 | import { Subject } from 'rxjs'; 8 | /* tslint:enable */ 9 | import { Preference } from './preferences'; 10 | 11 | @Injectable() 12 | export class AmazingTimePickerService { 13 | 14 | constructor ( 15 | private resolver: ComponentFactoryResolver, 16 | private appRef: ApplicationRef, 17 | private injector: Injector 18 | ) {} 19 | 20 | open (config?: TimePickerConfig): IDialogResult { 21 | const thems = ['light', 'dark', 'material-red', 'material-green', 'material-blue', 'material-purple', 'material-orange']; 22 | const _self = this; 23 | config = config || {}; 24 | config = { 25 | time: config.time || '00:00', 26 | theme: thems.indexOf(config.theme) > 0 ? config.theme : 'light' || config.theme || 'light', 27 | rangeTime: config.rangeTime || {start: '0:0', end: '24:0'}, 28 | arrowStyle: config.arrowStyle || {}, 29 | locale: config.locale || 'en', 30 | changeToMinutes: config.changeToMinutes, 31 | animation: (config.animation == null || config.animation === 'fade') ? 'fade' : (config.animation === 'rotate') ? 'rotate' : false, 32 | preference: config.preference || null, 33 | onlyHour: config.onlyHour, 34 | onlyMinute: config.onlyMinute, 35 | onlyAM: config.onlyAM, 36 | onlyPM: config.onlyPM, 37 | } as TimePickerConfig; 38 | config.rangeTime = { 39 | start: config.rangeTime.start || '0:0', 40 | end: config.rangeTime.end || '24:0', 41 | }; 42 | config.arrowStyle = { 43 | background: (config.arrowStyle.background) ? 44 | config.arrowStyle.background : config.theme !== undefined ? 45 | config.theme === 'dark' ? 'rgb(128, 203, 196)' : '' : '', 46 | color: config.arrowStyle.color || '' 47 | }; 48 | const componentRef = this.resolver.resolveComponentFactory(TimePickerComponent); 49 | const tsc = componentRef.create(this.injector); 50 | this.appRef.attachView(tsc.hostView); 51 | const domElem = (tsc.hostView as EmbeddedViewRef).rootNodes[0] as HTMLElement; 52 | document.body.appendChild(domElem); 53 | tsc.instance.subject = new Subject(); 54 | tsc.instance._ref = tsc; 55 | tsc.instance.appRef = this.appRef; 56 | tsc.instance.timerElement = ''; 57 | tsc.instance.config = config; 58 | if (config.preference) { 59 | tsc.instance.preference = config.preference; 60 | } else { 61 | tsc.instance.preference = Preference(config.locale); 62 | } 63 | tsc.instance.ParseStringToTime(config.time); 64 | return { 65 | afterClose: function () { 66 | return tsc.instance.subject.asObservable(); 67 | } 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amazing-time-picker", 3 | "version": "1.8.0", 4 | "license": "MIT", 5 | "repository": "https://github.com/owsolutions/amazing-time-picker.git", 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "build:docs": "ng build -e=docs -prod --app 1 --progress false", 10 | "build:github": "ng build -e=prod --app 2 -prod --base-href=/amazing-time-picker --deploy-url=/amazing-time-picker/ --progress false && cp dist/index-github.html dist/index.html", 11 | "build": "ng build -e=prod -prod --progress false", 12 | "test": "ng test", 13 | "lint": "ng lint", 14 | "ci": "npm run serve & npm run serve:beds:5 & npm run serve:beds:4 & npm run serve:beds:2 & npm run cypress:ci", 15 | "serve:beds:2": "PORT=12000 ./node_modules/.bin/serve /tmp/amazing-time-picker-test-beds/ng2-test-bed/dist", 16 | "serve:beds:4": "PORT=12001 ./node_modules/.bin/serve /tmp/amazing-time-picker-test-beds/ng4-test-bed/dist", 17 | "serve:beds:5": "PORT=12002 ./node_modules/.bin/serve /tmp/amazing-time-picker-test-beds/ng5-test-bed/dist", 18 | "cypress:ci": "./node_modules/.bin/cypress run ", 19 | "serve": "PORT=8080 ./node_modules/.bin/serve dist", 20 | "e2e": "ng e2e", 21 | "cypress": "./node_modules/.bin/cypress open", 22 | "hmr": "ng serve --hmr -e=hmr --port=8080", 23 | "packagr": "ng-packagr -p ng-package.json", 24 | "gh-pages": "git push origin :gh-pages && git subtree push --prefix dist origin gh-pages", 25 | "build:npm": "npm run publisher", 26 | "publisher": "rm npm_dist -Rf && npm run packagr && cd npm_dist && mv $(npm pack) dist.tgz", 27 | "publisher:mac": "rm -rf npm_dist && npm run packagr && cd npm_dist && mv $(npm pack) dist.tgz" 28 | }, 29 | "private": false, 30 | "dependencies": { 31 | "@angular/animations": ">=4.2.4", 32 | "@angular/common": ">=2.4.5", 33 | "@angular/compiler": ">=2.4.5", 34 | "@angular/core": ">=2.4.5", 35 | "@angular/forms": ">=2.4.5", 36 | "@angular/http": ">=2.4.5", 37 | "@angular/platform-browser": ">=2.4.5", 38 | "@angular/platform-browser-dynamic": ">=2.4.5", 39 | "@angular/router": ">=2.4.5", 40 | "core-js": "^2.4.1", 41 | "intl": "^1.2.5", 42 | "node-sass": "^4.12.0", 43 | "rxjs": "^5.5.12", 44 | "rxjs-compat": "^6.2.2", 45 | "zone.js": "^0.8.14" 46 | }, 47 | "devDependencies": { 48 | "@angular/cli": "1.3.2", 49 | "@angular/compiler-cli": "^4.2.4", 50 | "@angular/language-service": "^4.2.4", 51 | "@angularclass/hmr": "^2.1.3", 52 | "@types/jasmine": "~2.5.53", 53 | "@types/jasminewd2": "~2.0.2", 54 | "@types/node": "~6.0.60", 55 | "codelyzer": "~3.1.1", 56 | "concurrently": "^3.5.1", 57 | "cypress": "^1.4.1", 58 | "jasmine-core": "~2.6.2", 59 | "jasmine-spec-reporter": "~4.1.0", 60 | "karma": "~1.7.0", 61 | "karma-chrome-launcher": "~2.1.1", 62 | "karma-cli": "~1.0.1", 63 | "karma-coverage-istanbul-reporter": "^1.2.1", 64 | "karma-jasmine": "~1.1.0", 65 | "karma-jasmine-html-reporter": "^0.2.2", 66 | "ng-packagr": "1.6.0", 67 | "protractor": "~5.1.2", 68 | "serve": "^6.4.8", 69 | "ts-node": "~3.2.0", 70 | "tslint": "~5.3.2", 71 | "typescript": "~2.3.3" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/app/snack-ad/snack-ad.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, HostBinding } from '@angular/core'; 2 | import { Observable } from 'rxjs/Rx'; 3 | import { 4 | state, 5 | style, 6 | animate, 7 | transition, 8 | trigger 9 | } from '@angular/animations'; 10 | 11 | interface RenderState { 12 | duration: number; 13 | content: any; 14 | } 15 | @Component({ 16 | selector: 'app-snack-ad', 17 | templateUrl: './snack-ad.component.html', 18 | styleUrls: ['./snack-ad.component.scss'], 19 | animations: [ 20 | trigger('showHide', [ 21 | state( 22 | 'hide', 23 | style({ 24 | opacity: 0 25 | }) 26 | ), 27 | state( 28 | 'show', 29 | style({ 30 | opacity: 1 31 | }) 32 | ), 33 | transition('hide => show', [animate('0.3s')]), 34 | transition('show => hide', [animate('0.3s')]) 35 | ]) 36 | ] 37 | }) 38 | export class SnackAdComponent implements OnInit { 39 | private stateIndex = 0; 40 | 41 | states: RenderState[] = [ 42 | { 43 | duration: 3500, 44 | content: ` 45 | 46 | Build microservice from 40€ each service by us!` 47 | }, 48 | { 49 | duration: 2500, 50 | content: ` 51 | 52 | Web design with cutting edge tech only from 350€!` 53 | }, 54 | { 55 | duration: 5000, 56 | content: ` 57 | 58 | Amazing time picker loves you!` 59 | }, 60 | { 61 | duration: 2000, 62 | content: ` 63 | 64 | We support Angular 2, 4, 5, 6, 7` 65 | }, 66 | { 67 | duration: 5000, 68 | content: ` 69 | 70 | You can chat with us, click on the green box on the right side 71 | ` 72 | }, 73 | { 74 | duration: 5000, 75 | content: ` 76 | 77 | Star us on github, and get your free request from us Star now` 78 | } 79 | ]; 80 | public content: any = null; 81 | public visible = false; 82 | @HostBinding('class') public klass = 'snack-ad'; 83 | constructor() {} 84 | 85 | ngOnInit() { 86 | this.viewSlide(); 87 | } 88 | 89 | private viewSlide() { 90 | if (this.stateIndex === this.states.length - 1) { 91 | this.stateIndex = 0; 92 | } else { 93 | this.stateIndex++; 94 | } 95 | const state = this.states[this.stateIndex]; 96 | this.renderState(state); 97 | 98 | setTimeout(() => { 99 | this.viewSlide(); 100 | }, state.duration + 2000); 101 | } 102 | 103 | private renderState(state) { 104 | this.content = state.content; 105 | 106 | Observable.of(state).subscribe(data => { 107 | this.visible = false; 108 | this.content = data.content; 109 | }); 110 | 111 | Observable.of(state) 112 | .delay(300) 113 | .subscribe(() => { 114 | this.visible = true; 115 | }); 116 | 117 | Observable.of(state) 118 | .delay(state.duration + 600) 119 | .subscribe(data => { 120 | this.visible = false; 121 | }); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/app/atp-library/atp-core.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ITime } from './definitions'; 3 | 4 | @Injectable() 5 | export class AtpCoreService { 6 | 7 | constructor() { } 8 | 9 | public allowedTimes (min, max) { 10 | const allTimes = []; 11 | const nowMinHour = +min.split(':')[0]; 12 | const nowMaxHour = +max.split(':')[0]; 13 | const nowMinMin = +min.split(':')[1]; 14 | const nowMaxMin = +max.split(':')[1]; 15 | for (let i = nowMinHour; i <= nowMaxHour; i++) { 16 | let j = 0, 17 | jDest = 59; 18 | if (i === nowMinHour) { 19 | j = nowMinMin; 20 | }else if (i === nowMaxHour) { 21 | jDest = nowMaxMin; 22 | } 23 | for (j; j <= jDest; j++) { 24 | const hour = i <= 12 ? i : i - 12; 25 | const minute = j; 26 | const ampm = i < 12 ? 'AM' : 'PM'; 27 | allTimes.push(hour + ':' + minute + ' ' + ampm); 28 | } 29 | } 30 | return allTimes; 31 | } 32 | 33 | public ClockMaker (type: 'minute' | 'hour'): Array { 34 | const items = []; 35 | const timeVal = (type === 'minute') ? 60 : 12; 36 | const timeStep = (type === 'minute') ? 5 : 1; 37 | const timeStart = (type === 'minute') ? 0 : 1; 38 | const r = 124; 39 | const j = r - 25; 40 | 41 | for (let min = timeStart; min <= timeVal; min += timeStep) { 42 | if (min !== 60) { 43 | const str = String(min); 44 | const x = j * Math.sin(Math.PI * 2 * (min / timeVal)); 45 | const y = j * Math.cos(Math.PI * 2 * (min / timeVal)); 46 | 47 | items.push({ 48 | time: str, 49 | left: (x + r - 17) + 'px', 50 | top: (-y + r - 17) + 'px', 51 | type 52 | }); 53 | } 54 | } 55 | return items; 56 | } 57 | 58 | public TimeToString(time: ITime): string { 59 | const { ampm, minute, hour } = time; 60 | let hh = ampm === 'PM' ? +hour + 12 : +hour; 61 | if (ampm === 'AM' && hh === 12) { 62 | hh = 0; 63 | } 64 | if ( hh === 24) { 65 | hh = 12; 66 | } 67 | hh = hh < 10 ? '0' + hh : '' + hh as any; 68 | const mm = minute < 10 ? '0' + minute : minute; 69 | return `${hh}:${mm}`; 70 | } 71 | 72 | /** 73 | * Converts 00:00 format to ITime object 74 | */ 75 | public StringToTime (time: string): ITime { 76 | const [h, m] = time.split(':'); 77 | let hour = +h > 12 ? +h - 12 : +h; 78 | hour = hour === 0 ? 12 : hour; 79 | const ampm = +h >= 12 ? 'PM' : 'AM'; 80 | return { 81 | ampm, minute: +m, hour 82 | }; 83 | } 84 | 85 | /** 86 | * @experimental 87 | */ 88 | public CalcDegrees (ele: any, parrentPos: any, step: number): number { 89 | const clock = { 90 | width: ele.currentTarget.offsetWidth, 91 | height: ele.currentTarget.offsetHeight 92 | }; 93 | const targetX = clock.width / 2; 94 | const targetY = clock.height / 2; 95 | const Vx = Math.round((ele.clientX - parrentPos.left) - targetX); 96 | const Vy = Math.round(targetY - (ele.clientY - parrentPos.top)); 97 | let radians = -Math.atan2(Vy, Vx); 98 | radians += 2.5 * Math.PI; 99 | 100 | let degrees = Math.round(radians * 180 / Math.PI); 101 | const degMod = degrees % step; 102 | if (degMod === 0) { 103 | return degrees; 104 | } else if (degMod >= step / 2) { 105 | degrees = degrees + (step - degMod); 106 | } else if (degMod < step / 2) { 107 | degrees = degrees - degMod; 108 | } 109 | return degrees; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazing Time Picker (Clock) ![Build Status](https://travis-ci.org/owsolutions/amazing-time-picker.svg?branch=master) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 2 | 3 | ## Docs and demo 4 | 5 | You can read and learn amazing time picker and online demo here: 6 | 7 | [https://owsolutions.github.io/amazing-time-picker 8 | ](https://owsolutions.github.io/amazing-time-picker) 9 | 10 | A visual time picker for angular 2+ projects. You can use this timepicker with Angular 2, 4, 5, 6, 7 and Angular Material. **This project doesn't require angular material or any other dependencies** 11 | 12 | ## Angular 6, 7 support since version 1.8.0 13 | After some delay we have now support for Angular 6+. Also you can install latest version on Angular 2 project as well and it's fully backward compatible. 14 | 15 | In case required ( which shouldn't! ), install version 1.6.* for Angular 2, 4, 5 but they won't receive update. 16 | 17 | 18 | ## Live demo 19 | [https://pixelplux.com/product/amazing-time-picker](https://owsolutions.github.io/amazing-time-picker) 20 | 21 | ## Install 22 | You need to install this repository as dependency and import it to your `app.module.ts` in `imports` section. 23 | 24 | ``` 25 | npm install amazing-time-picker --save 26 | ``` 27 | 28 | then, open your `app.module.ts` or other module that you want to use timepicker among, and import and add it to the `imports` section: 29 | 30 | ``` 31 | import { BrowserModule } from '@angular/platform-browser'; 32 | import { NgModule } from '@angular/core'; 33 | import { AmazingTimePickerModule } from 'amazing-time-picker'; // this line you need 34 | import { AppComponent } from './app.component'; 35 | 36 | @NgModule({ 37 | declarations: [ 38 | AppComponent 39 | ], 40 | imports: [ 41 | BrowserModule, 42 | AmazingTimePickerModule // this line you need 43 | ], 44 | providers: [], 45 | bootstrap: [AppComponent] 46 | }) 47 | export class AppModule { } 48 | ``` 49 | 50 | This helps your angular project to build and compile it and let you use it. 51 | 52 | ## Using in component markup 53 | After you have installed this module, you can use it within your html templates and give the directive to the any tag. When user closes the dialog, it's gonna update the input value and will listen to input click event to open the dialog. 54 | 55 | ```html 56 | 57 | ``` 58 | 59 | ## Opening component programmatically 60 | You can also open a timepicker dialog programmatically. In order to open that, you need to import the service in your component: 61 | 62 | ``` 63 | import { AmazingTimePickerService } from 'amazing-time-picker'; 64 | ``` 65 | 66 | Then add it inside your `app.component.ts` or any other component that you want to use timepicker inside of that. 67 | 68 | ``` 69 | import { Component } from '@angular/core'; 70 | import { AmazingTimePickerService } from 'amazing-time-picker'; // this line you need 71 | 72 | @Component({ 73 | selector: 'app-root', 74 | templateUrl: './app.component.html', 75 | styleUrls: ['./app.component.scss'] 76 | }) 77 | export class AppComponent { 78 | 79 | constructor( private atp: AmazingTimePickerService, // this line you need 80 | ) { } 81 | 82 | open() { 83 | const amazingTimePicker = this.atp.open(); 84 | amazingTimePicker.afterClose().subscribe(time => { 85 | console.log(time); 86 | }); 87 | } 88 | } 89 | ``` 90 | 91 | 92 | Online demo 93 | 94 | ![Amazing Time Picker | TimePicker | Materialize time picker | AmazingTimepicker | ClockPicker](time-picker.jpg) 95 | 96 | [https://owsolutions.github.io/amazing-time-picker 97 | ](https://owsolutions.github.io/amazing-time-picker) 98 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "eofline": true, 15 | "forin": true, 16 | "import-blacklist": [ 17 | true, 18 | "rxjs" 19 | ], 20 | "import-spacing": true, 21 | "indent": [ 22 | true, 23 | "spaces" 24 | ], 25 | "interface-over-type-literal": true, 26 | "label-position": true, 27 | "max-line-length": [ 28 | true, 29 | 140 30 | ], 31 | "member-access": false, 32 | "member-ordering": [ 33 | true, 34 | { 35 | "order": [ 36 | "static-field", 37 | "instance-field", 38 | "static-method", 39 | "instance-method" 40 | ] 41 | } 42 | ], 43 | "no-arg": true, 44 | "no-bitwise": true, 45 | "no-console": [ 46 | true, 47 | "debug", 48 | "info", 49 | "time", 50 | "timeEnd", 51 | "trace" 52 | ], 53 | "no-construct": true, 54 | "no-debugger": true, 55 | "no-duplicate-super": true, 56 | "no-empty": false, 57 | "no-empty-interface": true, 58 | "no-eval": true, 59 | "no-inferrable-types": [ 60 | true, 61 | "ignore-params" 62 | ], 63 | "no-misused-new": true, 64 | "no-non-null-assertion": true, 65 | "no-shadowed-variable": true, 66 | "no-string-literal": false, 67 | "no-string-throw": true, 68 | "no-switch-case-fall-through": true, 69 | "no-trailing-whitespace": true, 70 | "no-unnecessary-initializer": true, 71 | "no-unused-expression": true, 72 | "no-use-before-declare": true, 73 | "no-var-keyword": true, 74 | "object-literal-sort-keys": false, 75 | "one-line": [ 76 | true, 77 | "check-open-brace", 78 | "check-catch", 79 | "check-else", 80 | "check-whitespace" 81 | ], 82 | "prefer-const": true, 83 | "quotemark": [ 84 | true, 85 | "single" 86 | ], 87 | "radix": true, 88 | "semicolon": [ 89 | true, 90 | "always" 91 | ], 92 | "triple-equals": [ 93 | true, 94 | "allow-null-check" 95 | ], 96 | "typedef-whitespace": [ 97 | true, 98 | { 99 | "call-signature": "nospace", 100 | "index-signature": "nospace", 101 | "parameter": "nospace", 102 | "property-declaration": "nospace", 103 | "variable-declaration": "nospace" 104 | } 105 | ], 106 | "typeof-compare": true, 107 | "unified-signatures": true, 108 | "variable-name": false, 109 | "whitespace": [ 110 | true, 111 | "check-branch", 112 | "check-decl", 113 | "check-operator", 114 | "check-separator", 115 | "check-type" 116 | ], 117 | "directive-selector": [ 118 | false, 119 | "attribute", 120 | "app", 121 | "camelCase" 122 | ], 123 | "component-selector": [ 124 | false, 125 | "element", 126 | "app", 127 | "kebab-case" 128 | ], 129 | "use-input-property-decorator": true, 130 | "use-output-property-decorator": true, 131 | "use-host-property-decorator": true, 132 | "no-input-rename": true, 133 | "no-output-rename": true, 134 | "use-life-cycle-interface": true, 135 | "use-pipe-transform-interface": true, 136 | "component-class-suffix": true, 137 | "directive-class-suffix": true, 138 | "no-access-missing-member": true, 139 | "templates-use-public": true, 140 | "invoke-injectable": true 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-hour/example-hour.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 |
7 |
8 | Restrict user to select hour only, not the minute. 9 |
10 |
11 |
12 | 13 | 14 |
15 | 
16 |     <button (click)="open()">Time</button>
17 |     <input type="time" value="{{sintax}}">
18 |         
19 |
20 | 21 |
22 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
23 |     import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
24 | 
25 |     @Component({{ '{' }}
26 |         selector: 'app-test',
27 |         templateUrl: './test.component.html',
28 |         styleUrls: ['./test.component.scss']
29 |     {{ '}' }})
30 |     export class TestComponent {{ '{' }}
31 |         public selectedTime = '18:33';
32 | 
33 |         constructor(private atp: AmazingTimePickerService) {{ '{' }} {{ '}' }}
34 | 
35 |         open() {{ '{' }}
36 |             const amazingTimePicker = this.atp.open({{ '{' }}
37 |                 onlyHour: true,
38 |             {{ '}' }});
39 |             amazingTimePicker.afterClose().subscribe(time => {{ '{' }}
40 |                 this.selectedTime = time;
41 |             {{ '}' }});
42 |         {{ '}' }}
43 |     {{ '}' }}
44 |     
45 |
46 |
47 |
48 |
49 |
50 | 51 | 52 |
53 |
54 |
55 |
56 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-arabic/example-arabic.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | This can be used to override the modal behavior. We have added a builtin display preference for Arabic Language. 4 |

5 |
6 |
7 |
8 |
Programmatically with Arabic Language
9 |
10 |
11 | 12 | 13 |
14 | 
15 |     <button (click)="open()">Time</button>
16 |     <input type="time" value="{{sintax}}">
17 |         
18 |
19 | 20 |
21 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
22 |     import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
23 | 
24 |     @Component({{ '{' }}
25 |         selector: 'app-test',
26 |         templateUrl: './test.component.html',
27 |         styleUrls: ['./test.component.scss']
28 |     {{ '}' }})
29 |     export class TestComponent {{ '{' }}
30 |         public selectedTime = '18:33';
31 | 
32 |         constructor(private atp: AmazingTimePickerService) {{ '{' }} {{ '}' }}
33 | 
34 |         open() {{ '{' }}
35 |             const amazingTimePicker = this.atp.open({{ '{' }}
36 |                 locale: 'ar',
37 |             {{ '}' }});
38 |             amazingTimePicker.afterClose().subscribe(time => {{ '{' }}
39 |                 this.selectedTime = time;
40 |             {{ '}' }});
41 |         {{ '}' }}
42 |     {{ '}' }}
43 |     
44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 |
55 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-persian/example-persian.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

We do accept a parameter called preferrence, which can help you to modify the appreance of the dialog

4 | Please take a review of the interface here: 5 |
6 |
7 |

8 | This can be used to override the modal behavior. We have added a builtin display preference for Persian Language. 9 |

10 |
11 |
12 |
13 |
Programmatically with Persian Language
14 |
15 |
16 | 17 | 18 |
19 | 
20 |     <button (click)="open()">Time</button>
21 |     <input type="time" value="{{sintax}}">
22 |         
23 |
24 | 25 |
26 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
27 |     import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
28 | 
29 |     @Component({{ '{' }}
30 |         selector: 'app-test',
31 |         templateUrl: './test.component.html',
32 |         styleUrls: ['./test.component.scss']
33 |     {{ '}' }})
34 |     export class TestComponent {{ '{' }}
35 |         public selectedTime = '18:33';
36 | 
37 |         constructor(private atp: AmazingTimePickerService) {{ '{' }} {{ '}' }}
38 | 
39 |         open() {{ '{' }}
40 |             const amazingTimePicker = this.atp.open({{ '{' }}
41 |                 locale: 'fa',
42 |             {{ '}' }});
43 |             amazingTimePicker.afterClose().subscribe(time => {{ '{' }}
44 |                 this.selectedTime = time;
45 |             {{ '}' }});
46 |         {{ '}' }}
47 |     {{ '}' }}
48 |     
49 |
50 |
51 |
52 |
53 |
54 | 55 | 56 |
57 |
58 |
59 |
60 | -------------------------------------------------------------------------------- /src/app/presentation/examples/example-material/example-material.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | There is an special theme for material design green, which you can use by setting the theme to material. 4 |

5 |
There are few material colours available:
 6 |   material-green
 7 |   material-blue
 8 |   material-red
 9 |   material-orange
10 |   meterial-purple
11 | 
12 |
13 |
14 |
15 |
Programmatically with Material theme
16 |
17 | 18 | 19 |
20 | 21 | 22 |
23 | 
24 |     <button (click)="open()">Time</button>
25 |     <input type="time" value="{{sintax}}">
26 |         
27 |
28 | 29 |
30 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
31 |     import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
32 | 
33 |     @Component({{ '{' }}
34 |         selector: 'app-test',
35 |         templateUrl: './test.component.html',
36 |         styleUrls: ['./test.component.scss']
37 |     {{ '}' }})
38 |     export class TestComponent {{ '{' }}
39 |         public selectedTime = '18:33';
40 | 
41 |         constructor(private atp: AmazingTimePickerService) {{ '{' }} {{ '}' }}
42 | 
43 |         open() {{ '{' }}
44 |             const amazingTimePicker = this.atp.open({{ '{' }}
45 |                 theme: 'material-blue',
46 |             {{ '}' }});
47 |             amazingTimePicker.afterClose().subscribe(time => {{ '{' }}
48 |                 this.selectedTime = time;
49 |             {{ '}' }});
50 |         {{ '}' }}
51 |     {{ '}' }}
52 |     
53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 |
65 | 66 |
67 |
68 |
69 |
70 | -------------------------------------------------------------------------------- /docs/3rdpartylicenses.txt: -------------------------------------------------------------------------------- 1 | core-js@2.5.3 MIT 2 | Copyright (c) 2014-2017 Denis Pushkarev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | 23 | @angular/core@4.4.6 MIT 24 | 25 | @angular/router@4.4.6 MIT 26 | 27 | @angular/animations@4.4.6 MIT 28 | 29 | @angular/forms@4.4.6 MIT 30 | 31 | webpack@3.4.1 MIT 32 | Copyright JS Foundation and other contributors 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining 35 | a copy of this software and associated documentation files (the 36 | 'Software'), to deal in the Software without restriction, including 37 | without limitation the rights to use, copy, modify, merge, publish, 38 | distribute, sublicense, and/or sell copies of the Software, and to 39 | permit persons to whom the Software is furnished to do so, subject to 40 | the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be 43 | included in all copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 46 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 47 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 48 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 49 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 50 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 51 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 | 53 | 54 | @angular/platform-browser@4.4.6 MIT 55 | 56 | @angular/common@4.4.6 MIT 57 | 58 | intl@1.2.5 MIT 59 | The MIT License (MIT) 60 | 61 | Copyright (c) 2013 Andy Earnshaw 62 | 63 | Permission is hereby granted, free of charge, to any person obtaining a copy 64 | of this software and associated documentation files (the "Software"), to deal 65 | in the Software without restriction, including without limitation the rights 66 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 67 | copies of the Software, and to permit persons to whom the Software is 68 | furnished to do so, subject to the following conditions: 69 | 70 | The above copyright notice and this permission notice shall be included in 71 | all copies or substantial portions of the Software. 72 | 73 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 74 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 75 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 76 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 77 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 78 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 79 | THE SOFTWARE. 80 | 81 | 82 | ------------------------------------------------------------------------------ 83 | Contents of the `locale-data` directory are a modified form of the Unicode CLDR 84 | data found at http://www.unicode.org/cldr/data/. It comes with the following 85 | license. 86 | 87 | COPYRIGHT AND PERMISSION NOTICE 88 | 89 | Copyright (c) 1991-2013 Unicode, Inc. All rights reserved. Distributed under 90 | the Terms of Use in http://www.unicode.org/copyright.html. 91 | 92 | Permission is hereby granted, free of charge, to any person obtaining a copy of 93 | the Unicode data files and any associated documentation (the "Data Files") or 94 | Unicode software and any associated documentation (the "Software") to deal in 95 | the Data Files or Software without restriction, including without limitation 96 | the rights to use, copy, modify, merge, publish, distribute, and/or sell copies 97 | of the Data Files or Software, and to permit persons to whom the Data Files or 98 | Software are furnished to do so, provided that (a) the above copyright 99 | notice(s) and this permission notice appear with all copies of the Data Files 100 | or Software, (b) both the above copyright notice(s) and this permission notice 101 | appear in associated documentation, and (c) there is clear notice in each 102 | modified Data File or in the Software as well as in the documentation 103 | associated with the Data File(s) or Software that the data or software has been 104 | modified. 105 | 106 | THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 107 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 108 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD 109 | PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN 110 | THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL 111 | DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 112 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 113 | OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR 114 | SOFTWARE. 115 | 116 | Except as contained in this notice, the name of a copyright holder shall not be 117 | used in advertising or otherwise to promote the sale, use or other dealings in 118 | these Data Files or Software without prior written authorization of the 119 | copyright holder. 120 | 121 | Unicode and the Unicode logo are trademarks of Unicode, Inc. in the United 122 | States and other countries. All third party trademarks referenced herein are 123 | the property of their respective owners. 124 | 125 | 126 | 127 | zone.js@0.8.18 MIT 128 | The MIT License 129 | 130 | Copyright (c) 2016 Google, Inc. 131 | 132 | Permission is hereby granted, free of charge, to any person obtaining a copy 133 | of this software and associated documentation files (the "Software"), to deal 134 | in the Software without restriction, including without limitation the rights 135 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 136 | copies of the Software, and to permit persons to whom the Software is 137 | furnished to do so, subject to the following conditions: 138 | 139 | The above copyright notice and this permission notice shall be included in 140 | all copies or substantial portions of the Software. 141 | 142 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 143 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 144 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 145 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 146 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 147 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 148 | THE SOFTWARE. 149 | -------------------------------------------------------------------------------- /dist/3rdpartylicenses.txt: -------------------------------------------------------------------------------- 1 | core-js@2.5.3 MIT 2 | Copyright (c) 2014-2017 Denis Pushkarev 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | 23 | @angular/core@4.4.6 MIT 24 | 25 | @angular/router@4.4.6 MIT 26 | 27 | @angular/animations@4.4.6 MIT 28 | 29 | @angular/forms@4.4.6 MIT 30 | 31 | webpack@3.4.1 MIT 32 | Copyright JS Foundation and other contributors 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining 35 | a copy of this software and associated documentation files (the 36 | 'Software'), to deal in the Software without restriction, including 37 | without limitation the rights to use, copy, modify, merge, publish, 38 | distribute, sublicense, and/or sell copies of the Software, and to 39 | permit persons to whom the Software is furnished to do so, subject to 40 | the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be 43 | included in all copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 46 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 47 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 48 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 49 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 50 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 51 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 | 53 | 54 | @angular/platform-browser@4.4.6 MIT 55 | 56 | @angular/common@4.4.6 MIT 57 | 58 | intl@1.2.5 MIT 59 | The MIT License (MIT) 60 | 61 | Copyright (c) 2013 Andy Earnshaw 62 | 63 | Permission is hereby granted, free of charge, to any person obtaining a copy 64 | of this software and associated documentation files (the "Software"), to deal 65 | in the Software without restriction, including without limitation the rights 66 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 67 | copies of the Software, and to permit persons to whom the Software is 68 | furnished to do so, subject to the following conditions: 69 | 70 | The above copyright notice and this permission notice shall be included in 71 | all copies or substantial portions of the Software. 72 | 73 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 74 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 75 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 76 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 77 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 78 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 79 | THE SOFTWARE. 80 | 81 | 82 | ------------------------------------------------------------------------------ 83 | Contents of the `locale-data` directory are a modified form of the Unicode CLDR 84 | data found at http://www.unicode.org/cldr/data/. It comes with the following 85 | license. 86 | 87 | COPYRIGHT AND PERMISSION NOTICE 88 | 89 | Copyright (c) 1991-2013 Unicode, Inc. All rights reserved. Distributed under 90 | the Terms of Use in http://www.unicode.org/copyright.html. 91 | 92 | Permission is hereby granted, free of charge, to any person obtaining a copy of 93 | the Unicode data files and any associated documentation (the "Data Files") or 94 | Unicode software and any associated documentation (the "Software") to deal in 95 | the Data Files or Software without restriction, including without limitation 96 | the rights to use, copy, modify, merge, publish, distribute, and/or sell copies 97 | of the Data Files or Software, and to permit persons to whom the Data Files or 98 | Software are furnished to do so, provided that (a) the above copyright 99 | notice(s) and this permission notice appear with all copies of the Data Files 100 | or Software, (b) both the above copyright notice(s) and this permission notice 101 | appear in associated documentation, and (c) there is clear notice in each 102 | modified Data File or in the Software as well as in the documentation 103 | associated with the Data File(s) or Software that the data or software has been 104 | modified. 105 | 106 | THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 107 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 108 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD 109 | PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN 110 | THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL 111 | DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 112 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 113 | OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR 114 | SOFTWARE. 115 | 116 | Except as contained in this notice, the name of a copyright holder shall not be 117 | used in advertising or otherwise to promote the sale, use or other dealings in 118 | these Data Files or Software without prior written authorization of the 119 | copyright holder. 120 | 121 | Unicode and the Unicode logo are trademarks of Unicode, Inc. in the United 122 | States and other countries. All third party trademarks referenced herein are 123 | the property of their respective owners. 124 | 125 | 126 | 127 | zone.js@0.8.18 MIT 128 | The MIT License 129 | 130 | Copyright (c) 2016 Google, Inc. 131 | 132 | Permission is hereby granted, free of charge, to any person obtaining a copy 133 | of this software and associated documentation files (the "Software"), to deal 134 | in the Software without restriction, including without limitation the rights 135 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 136 | copies of the Software, and to permit persons to whom the Software is 137 | furnished to do so, subject to the following conditions: 138 | 139 | The above copyright notice and this permission notice shall be included in 140 | all copies or substantial portions of the Software. 141 | 142 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 143 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 144 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 145 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 146 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 147 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 148 | THE SOFTWARE. 149 | 150 | 151 | css-loader@0.28.7 MIT 152 | Copyright JS Foundation and other contributors 153 | 154 | Permission is hereby granted, free of charge, to any person obtaining 155 | a copy of this software and associated documentation files (the 156 | 'Software'), to deal in the Software without restriction, including 157 | without limitation the rights to use, copy, modify, merge, publish, 158 | distribute, sublicense, and/or sell copies of the Software, and to 159 | permit persons to whom the Software is furnished to do so, subject to 160 | the following conditions: 161 | 162 | The above copyright notice and this permission notice shall be 163 | included in all copies or substantial portions of the Software. 164 | 165 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 166 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 167 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 168 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 169 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 170 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 171 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 172 | -------------------------------------------------------------------------------- /src/app/atp-library/time-picker/time-picker.component.scss: -------------------------------------------------------------------------------- 1 | // Color pallete 2 | $lightThemeBG: blue; 3 | $lightThemeColor: #fff; 4 | 5 | $darkThemeBG: #84cbc5; 6 | $darkThemeColor: #000; 7 | 8 | $themeName: 'green', 'blue', 'red', 'purple', 'orange'; 9 | $materialThemeBG: #00897b, #3F51B5, #F44336, #9C27B0, #FF9800; 10 | $materialThemeColor: #fff, #fff, #fff, #fff, #fff; 11 | 12 | #time-picker-wrapper{ 13 | position: fixed; 14 | top:0; 15 | bottom:0; 16 | left:0; 17 | right:0; 18 | background: transparent; 19 | transition: background 0.4s; 20 | font-family: 'Roboto', sans-serif; 21 | z-index: 1000; 22 | &.static{ 23 | position: relative !important; 24 | background: transparent !important; 25 | display: inline-block; 26 | z-index: 0; 27 | } 28 | &.active{ 29 | background: rgba(0,0,0,0.3); 30 | } 31 | &.dark{ 32 | #time-picker{ 33 | background: #424242; 34 | .time-picker-header{ 35 | border-bottom: none; 36 | background: #555555; 37 | .time-picker-selected-time{ 38 | color: #999; 39 | div.selected{ 40 | color: #fff; 41 | } 42 | } 43 | .time-picker-selected-ampm{ 44 | color: #999; 45 | div.selected{ 46 | color: #fff; 47 | } 48 | } 49 | } 50 | .time-picker-clock{ 51 | background: #555555; 52 | > button{ 53 | color: #fff; 54 | } 55 | } 56 | .time-picker-footer{ 57 | border-top: none; 58 | button{ 59 | background: #555555; 60 | color: #fff; 61 | &:hover{ 62 | background: #777; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | &.light{ 69 | #time-picker{ 70 | background: #fff; 71 | .time-picker-header{ 72 | border-bottom: 1px solid #e1e1e1; 73 | .time-picker-selected-time{ 74 | color: #aaa; 75 | div.selected{ 76 | color: #000; 77 | } 78 | } 79 | .time-picker-selected-ampm{ 80 | color: #aaa; 81 | div.selected{ 82 | color: #000; 83 | } 84 | } 85 | } 86 | .time-picker-clock{ 87 | background: #ededed; 88 | > button{ 89 | color: #000; 90 | &.active{ 91 | background: $lightThemeBG; 92 | color: $lightThemeColor; 93 | } 94 | } 95 | .time-picker-clock-origin { 96 | background: $lightThemeBG; 97 | } 98 | .time-picker-clock-arrow { 99 | background: $lightThemeBG; 100 | span { 101 | background: $lightThemeBG; 102 | } 103 | } 104 | } 105 | .time-picker-footer{ 106 | border-top: 1px solid #e1e1e1; 107 | button{ 108 | background: #f1f1f1; 109 | &:hover{ 110 | background: #ddd; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | @each $theme in $themeName { 117 | $i:index($themeName, $theme); 118 | &.material-#{$theme}{ 119 | #time-picker{ 120 | background: #fff; 121 | .time-picker-header{ 122 | background-color: nth($materialThemeBG, $i); 123 | border-bottom: 1px solid #e1e1e1; 124 | .time-picker-selected-time{ 125 | color: rgba(nth($materialThemeColor, $i), 0.4); 126 | div.selected{ 127 | color: nth($materialThemeColor, $i); 128 | } 129 | } 130 | .time-picker-selected-ampm{ 131 | color: rgba(nth($materialThemeColor, $i), 0.4); 132 | div.selected{ 133 | color: nth($materialThemeColor, $i); 134 | } 135 | } 136 | } 137 | .time-picker-clock{ 138 | background: #ededed; 139 | > button{ 140 | color: #000; 141 | &.active{ 142 | background: nth($materialThemeBG, $i); 143 | color: nth($materialThemeColor, $i); 144 | } 145 | } 146 | .time-picker-clock-origin { 147 | background: nth($materialThemeBG, $i); 148 | } 149 | .time-picker-clock-arrow { 150 | background: nth($materialThemeBG, $i); 151 | span { 152 | background: nth($materialThemeBG, $i); 153 | } 154 | } 155 | } 156 | .time-picker-footer{ 157 | border-top: 1px solid #e1e1e1; 158 | button{ 159 | font-weight: bold; 160 | text-transform: uppercase; 161 | background: transparent; 162 | color: nth($materialThemeBG, $i); 163 | &:hover{ 164 | background: #ddd; 165 | } 166 | } 167 | } 168 | } 169 | } 170 | } 171 | #time-picker{ 172 | width: 325px; 173 | height: auto; 174 | box-shadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.14), 0 9px 46px 8px rgba(0,0,0,.12); 175 | border-radius: 2px; 176 | margin:15vh auto !important; 177 | transform: scale(0.5) !important; 178 | transition: transform 0.3s, opacity 0.3s; 179 | opacity: 0; 180 | &.static{ 181 | margin: 0px !important; 182 | box-shadow: 0 11px 15px -7px rgba(0, 0, 0, 0.2), 0 0 7px 3px rgba(0, 0, 0, 0.14), 0 9px 46px 8px rgba(0, 0, 0, 0.12); 183 | } 184 | &.active{ 185 | transform: scale(1) !important; 186 | opacity: 1; 187 | } 188 | .time-picker-header{ 189 | text-align: center; 190 | padding: 15px 0px; 191 | .time-picker-selected-time{ 192 | font-size: 35px; 193 | padding: 5px 0px; 194 | div{ 195 | display: inline-block; 196 | cursor: pointer; 197 | &[disabled=true] { 198 | cursor: default; 199 | } 200 | } 201 | } 202 | .time-picker-selected-ampm{ 203 | font-size: 18px; 204 | div{ 205 | display: inline-block; 206 | padding: 0 5px; 207 | cursor: pointer; 208 | &[disabled=true] { 209 | cursor: default; 210 | } 211 | } 212 | } 213 | } 214 | .time-picker-content{ 215 | .time-picker-clock{ 216 | width: 200px; 217 | height: 200px; 218 | padding: 24px; 219 | border-radius: 50%; 220 | cursor: pointer; 221 | margin: 25px auto; 222 | position: relative; 223 | user-select: none; 224 | -moz-user-select: none; 225 | -ms-user-select: none; 226 | -webkit-user-select: none; 227 | opacity: 1; 228 | transform: scale(1); 229 | -webkit-transition: opacity 0.3s, transform 0.3s; 230 | -moz-transition: opacity 0.3s, transform 0.3s; 231 | -o-transition: opacity 0.3s, transform 0.3s; 232 | transition: opacity 0.3s, transform 0.3s; 233 | button{ 234 | border: none; 235 | position: absolute; 236 | width: 35px; 237 | height: 35px; 238 | border-radius: 50%; 239 | cursor: pointer; 240 | font-size: 17px; 241 | text-align: center; 242 | padding: 0; 243 | transition: all .2s; 244 | z-index: 3; 245 | } 246 | .time-picker-clock-origin{ 247 | width: 6px; 248 | height: 6px; 249 | border-radius: 50%; 250 | position: absolute; 251 | left: 50%; 252 | top: 50%; 253 | margin-left: -3px; 254 | margin-top: -3px; 255 | } 256 | .time-picker-clock-arrow{ 257 | width: 2px; 258 | height: 41%; 259 | position: absolute; 260 | left: 0; 261 | top: 22px; 262 | right: 0; 263 | margin: auto; 264 | -webkit-transform-origin: top left; 265 | transform-origin: bottom; 266 | span{ 267 | width: 8px; 268 | height: 8px; 269 | border-radius: 50%; 270 | position: absolute; 271 | top: 0; 272 | right: -3px; 273 | } 274 | } 275 | &.hide-time-picker-clock{ 276 | opacity: 0; 277 | transform: scale(0.8); 278 | } 279 | } 280 | } 281 | 282 | .time-picker-footer{ 283 | padding: 15px; 284 | text-align: right; 285 | button{ 286 | border: transparent; 287 | margin-left: 10px; 288 | padding: 10px; 289 | font-size: 14px; 290 | border-radius: 2px; 291 | cursor: pointer; 292 | } 293 | } 294 | } 295 | } 296 | *{ 297 | outline: none; 298 | box-sizing: content-box; 299 | } 300 | -------------------------------------------------------------------------------- /src/app/atp-library/time-picker/time-picker.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { IClockNumber, IDisplayPreference, TimePickerConfig } from '../definitions'; 3 | import { AtpCoreService } from '../atp-core.service'; 4 | import { ITime } from '../definitions'; 5 | 6 | @Component({ 7 | selector: 'time-picker', 8 | templateUrl: './time-picker.component.html', 9 | styleUrls: ['./time-picker.component.scss'] 10 | }) 11 | export class TimePickerComponent implements OnInit { 12 | 13 | _ref: any; 14 | public subject: any = null; 15 | public activeModal = false; 16 | public timerElement: any; 17 | public clockObject: Array; 18 | public isClicked: boolean; 19 | public clockType: 'minute' | 'hour' = 'hour'; 20 | public time: ITime = { 21 | ampm: 'AM', 22 | minute: 0, 23 | hour: 12 24 | }; 25 | public nowTime: any = this.time.hour; 26 | public degree: any; 27 | public config: TimePickerConfig; 28 | public appRef: any; 29 | public isPopup = true; 30 | public allowed: any; 31 | public preference: IDisplayPreference; 32 | public changeToMin: boolean; 33 | 34 | private animationTime = 0; 35 | 36 | constructor( 37 | private core: AtpCoreService 38 | ) { } 39 | 40 | public ParseStringToTime (time: string): void { 41 | time = (time === '' || time === undefined || time === null) ? this.time.hour + ':' + this.time.minute : time; 42 | this.time = this.core.StringToTime(time); 43 | } 44 | 45 | public GetTime () { 46 | const time = this.core.TimeToString(this.time); 47 | this.subject.next(time); 48 | } 49 | 50 | clockMaker = () => { 51 | const type = this.clockType; 52 | this.clockObject = this.core.ClockMaker(type); 53 | this.setArrow(null); 54 | } 55 | 56 | setActiveTime = () => { 57 | this.nowTime = (this.clockType === 'minute' ? this.time.minute : this.time.hour); 58 | } 59 | 60 | setArrow = (obj: any) => { 61 | if (obj) { 62 | this.clockType = obj.type; 63 | if (this.clockType === 'minute') { 64 | this.time.minute = obj.time; 65 | } else { 66 | this.time.hour = obj.time; 67 | } 68 | } 69 | const step = (this.clockType === 'minute') ? 6 : 30; 70 | const time = (this.clockType === 'minute') ? this.time.minute : this.time.hour; 71 | const degrees = time * step; 72 | this.rotationClass(degrees); 73 | this.setActiveTime(); 74 | } 75 | 76 | rotationClass = (degrees: any) => { 77 | this.degree = degrees; 78 | } 79 | 80 | setTime() { 81 | this.isClicked = false; 82 | if (this.config.changeToMinutes && !this.config.onlyHour && this.clockType === 'hour') { 83 | this.ChangeAnimational('minute'); 84 | } 85 | } 86 | 87 | getDegree = (ele: any) => { 88 | const step = this.clockType === 'minute' ? 6 : 30; 89 | const parrentPos = ele.currentTarget.getBoundingClientRect(); 90 | if (this.isClicked && (ele.currentTarget === ele.target || ele.target.nodeName === 'BUTTON')) { 91 | const clock = { 92 | width: ele.currentTarget.offsetWidth, 93 | height: ele.currentTarget.offsetHeight 94 | }; 95 | const degrees = this.core.CalcDegrees(ele, parrentPos, step); 96 | let hour = this.time.hour, 97 | minute = this.time.minute; 98 | 99 | if (this.clockType === 'hour') { 100 | hour = (degrees / step); 101 | hour = (hour > 12) ? hour - 12 : hour; 102 | } else if (this.clockType === 'minute') { 103 | minute = (degrees / step); 104 | minute = (minute > 59) ? minute - 60 : minute; 105 | } 106 | 107 | const min = this.config.rangeTime.start, 108 | max = this.config.rangeTime.end; 109 | 110 | const nowMinHour = +min.split(':')[0] < 12 ? +min.split(':')[0] : +min.split(':')[0] - 12; 111 | const nowMaxHour = +max.split(':')[0] < 12 ? +max.split(':')[0] : +max.split(':')[0] - 12; 112 | const nowMinMin = +min.split(':')[1]; 113 | const nowMaxMin = +max.split(':')[1]; 114 | 115 | const nowTime = this.GetNowTime(hour, this.time.ampm, minute); 116 | if (this.allowed.indexOf(nowTime) > -1) { 117 | this.time.hour = hour; 118 | this.time.minute = minute; 119 | this.rotationClass(degrees); 120 | this.setActiveTime(); 121 | }else if (this.clockType === 'hour' && (hour === nowMinHour && minute <= nowMinMin)) { 122 | this.time.hour = nowMinHour; 123 | this.time.minute = nowMinMin; 124 | }else if (this.clockType === 'hour' && (hour === nowMaxHour && minute >= nowMaxMin)) { 125 | this.time.hour = nowMaxHour; 126 | this.time.minute = nowMaxMin; 127 | } 128 | } 129 | } 130 | 131 | private GetNowTime (hour: number, ampm: 'AM' | 'PM', minute: number): string { 132 | const Hour = (hour === 12 && ampm === 'AM') ? '0' : hour; 133 | const nowTime = Hour + ':' + minute + ' ' + ampm; 134 | return nowTime; 135 | } 136 | 137 | checkBet() { 138 | const nowTime = this.GetNowTime(this.time.hour, this.time.ampm, this.time.minute); 139 | if (this.allowed.indexOf(nowTime) === -1) { 140 | this.ParseStringToTime(this.config.rangeTime.start); 141 | this.setArrow(null); 142 | this.setActiveTime(); 143 | } 144 | } 145 | 146 | /** 147 | * Check if clock button time is not in allowed times and disabled 148 | * @param t Button Time Value 149 | */ 150 | checkDisabled(t) { 151 | const m = (this.clockType === 'minute') ? t : this.time.minute; 152 | const h = (this.clockType === 'hour') ? t : this.time.hour; 153 | const nowTime = this.GetNowTime(h, this.time.ampm, m); 154 | return (this.allowed.indexOf(nowTime) === -1) ? true : false; 155 | } 156 | 157 | modalAnimation() { 158 | setTimeout(() => { 159 | this.activeModal = true; 160 | }, 1); 161 | } 162 | 163 | ngOnInit() { 164 | this.allowed = this.core.allowedTimes (this.config.rangeTime.start, this.config.rangeTime.end); 165 | if (this.config && this.config.onlyMinute) { 166 | this.clockType = 'minute'; 167 | } 168 | if (this.config) { 169 | if (this.config.onlyPM) { 170 | this.time.ampm = 'PM'; 171 | } else if (this.config.onlyAM) { 172 | this.time.ampm = 'AM'; 173 | } 174 | } 175 | this.clockMaker(); 176 | this.modalAnimation(); 177 | } 178 | 179 | public MinuteClick () { 180 | /** 181 | * We are not permitting user to select the minute. 182 | * but anyway, it will return the standard time, if provided the default time. 183 | */ 184 | if (this.config && this.config.onlyHour) { 185 | return false; 186 | } 187 | 188 | this.ChangeAnimational('minute'); 189 | } 190 | 191 | public HourClick () { 192 | /** 193 | * We are not permitting user to select the minute. 194 | * but anyway, it will return the standard time, if provided the default time. 195 | */ 196 | if (this.config && this.config.onlyMinute) { 197 | return false; 198 | } 199 | this.ChangeAnimational('hour'); 200 | } 201 | 202 | ChangeAnimational(type: 'minute' | 'hour') { 203 | if (this.clockType !== type) { 204 | if (this.config.animation === 'fade') { 205 | this.changeToMin = true; 206 | setTimeout(() => { 207 | this.changeToMin = false; 208 | this.clockType = type; 209 | this.clockMaker(); 210 | }, 200); 211 | } else if (this.config.animation === 'rotate') { 212 | this.animationTime = 0.4; 213 | this.clockType = type; 214 | this.clockMaker(); 215 | } else { 216 | this.clockType = type; 217 | this.clockMaker(); 218 | } 219 | } 220 | } 221 | 222 | SetAM () { 223 | if (this.config && this.config.onlyPM) { 224 | return false; 225 | } 226 | this.time.ampm = 'AM'; 227 | this.checkBet(); 228 | } 229 | 230 | SetPM () { 231 | if (this.config && this.config.onlyAM) { 232 | return false; 233 | } 234 | this.time.ampm = 'PM'; 235 | this.checkBet(); 236 | } 237 | 238 | Close(e: any) { 239 | if (e.target === e.currentTarget) { 240 | if (this.isPopup === true) { 241 | this.activeModal = false; 242 | setTimeout(() => { 243 | this.appRef.detachView(this._ref.hostView); 244 | this._ref.destroy(); 245 | }, 400); 246 | } 247 | } 248 | } 249 | 250 | getClockArrowStyle() { 251 | let arrowStyle = {}; 252 | if (this.config.animation === 'rotate') { 253 | arrowStyle = { 254 | transform: 'rotate(' + this.degree + 'deg)', 255 | '-webkit-transform': 'rotate(' + this.degree + 'deg)', 256 | background: this.config.arrowStyle.background, 257 | '-webkit-transition': 'transform ' + this.getAnimationTime(), 258 | transition: 'transform ' + + this.getAnimationTime() 259 | }; 260 | }else { 261 | arrowStyle = { 262 | transform: 'rotate(' + this.degree + 'deg)', 263 | '-webkit-transform': 'rotate(' + this.degree + 'deg)', 264 | background: this.config.arrowStyle.background, 265 | }; 266 | } 267 | return arrowStyle; 268 | } 269 | 270 | getAnimationTime() { 271 | return this.animationTime + 's'; 272 | } 273 | 274 | /** 275 | * Event on clock mouse click down 276 | * @param event - captured event 277 | */ 278 | updateClockDown(event) { 279 | this.isClicked = true; 280 | this.animationTime = 0; 281 | this.getDegree(event); 282 | } 283 | 284 | 285 | setNewRotation() { 286 | const targetDegree = ((this.time.minute / 60) * 360) + 360; 287 | const targetDegree2 = targetDegree * 2; 288 | 289 | const diff1 = Math.abs(this.degree - targetDegree); 290 | const diff2 = Math.abs(this.degree - targetDegree2); 291 | 292 | if (diff1 < diff2) { 293 | this.rotationClass(targetDegree); 294 | } else { 295 | this.rotationClass(targetDegree2); 296 | } 297 | } 298 | 299 | public GetSeparator () { 300 | if (this.preference && this.preference.separator) { 301 | return this.preference.separator; 302 | } 303 | return ':'; 304 | } 305 | public GetPeriod (period: 'AM' | 'PM') { 306 | if (this.preference && this.preference.period) { 307 | return this.preference.period(period); 308 | } 309 | return period; 310 | } 311 | public GetMinute () { 312 | if (this.preference && this.preference.minute) { 313 | return this.preference.minute(this.time.minute); 314 | } 315 | let min: string = this.time.minute.toString(); 316 | if (+min < 10) { 317 | min = '0' + min; 318 | } 319 | return min; 320 | } 321 | public GetHour () { 322 | if (this.preference && this.preference.hour) { 323 | return this.preference.hour(this.time.hour); 324 | } 325 | return this.time.hour; 326 | } 327 | public GetClockTime(clock: IClockNumber) { 328 | if ( ! this.preference) { 329 | return clock.time; 330 | } 331 | if ( this.clockType === 'hour' && this.preference.clockHour) { 332 | return this.preference.clockHour(clock.time); 333 | } 334 | if ( this.clockType === 'minute' && this.preference.clockMinute) { 335 | return this.preference.clockMinute(clock.time); 336 | } 337 | return clock.time; 338 | } 339 | 340 | public GetLabel (key: string) { 341 | const defaults = { 342 | 'ok': 'Ok', 343 | 'cancel': 'Cancel' 344 | }; 345 | if ((this.preference && this.preference.labels && this.preference.labels.ok)) { 346 | defaults.ok = this.preference.labels.ok; 347 | } 348 | if ((this.preference && this.preference.labels && this.preference.labels.cancel)) { 349 | defaults.cancel = this.preference.labels.cancel; 350 | } 351 | return defaults[key]; 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /src/fontello.scss: -------------------------------------------------------------------------------- 1 | 2 | @font-face { 3 | font-family: 'fontello'; 4 | src: url('data:application/octet-stream;base64,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') format('woff'), 5 | url('data:application/octet-stream;base64,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') format('truetype'); 6 | } 7 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 8 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 9 | /* 10 | @media screen and (-webkit-min-device-pixel-ratio:0) { 11 | @font-face { 12 | font-family: 'fontello'; 13 | src: url('../font/fontello.svg?5731835#fontello') format('svg'); 14 | } 15 | } 16 | */ 17 | 18 | [class^="icon-"]:before, [class*=" icon-"]:before { 19 | font-family: "fontello"; 20 | font-style: normal; 21 | font-weight: normal; 22 | speak: none; 23 | 24 | display: inline-block; 25 | text-decoration: inherit; 26 | width: 1em; 27 | margin-right: .2em; 28 | text-align: center; 29 | /* opacity: .8; */ 30 | 31 | /* For safety - reset parent styles, that can break glyph codes*/ 32 | font-variant: normal; 33 | text-transform: none; 34 | 35 | /* fix buttons height, for twitter bootstrap */ 36 | line-height: 1em; 37 | 38 | /* Animation center compensation - margins should be symmetric */ 39 | /* remove if not needed */ 40 | margin-left: .2em; 41 | 42 | /* you can be more comfortable with increased icons size */ 43 | /* font-size: 120%; */ 44 | 45 | /* Uncomment for 3D effect */ 46 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 47 | } 48 | .icon-heart:before { content: '\e800'; } /* '' */ 49 | .icon-clouds-flash-alt:before { content: '\e801'; } /* '' */ 50 | .icon-undo:before { content: '\e802'; } /* '' */ 51 | .icon-chat:before { content: '\e803'; } /* '' */ 52 | .icon-star:before { content: '\e804'; } /* '' */ 53 | .icon-rss:before { content: '\f09e'; } /* '' */ 54 | .icon-angle-double-right:before { content: '\f101'; } /* '' */ 55 | .icon-lifebuoy:before { content: '\f1cd'; } /* '' */ 56 | .icon-amazon:before { content: '\f270'; } /* '' */ -------------------------------------------------------------------------------- /src/app/presentation/example/example.component.html: -------------------------------------------------------------------------------- 1 |

Using in component markup

2 |

3 | After you have installed this module, you can use it within your html 4 | templates and give the directive to the any tag. When user closes the dialog, 5 | it's gonna update the input value and will listen to input click event to open 6 | the dialog. 7 |

8 |
9 |
10 |
11 |
Directive
12 |
13 | 14 | 15 |
 16 |     <input type="time" atp-time-picker value="20:55"/>
 17 |             
18 |
19 | 20 |
21 |
 22 | 
 23 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
 24 | 
 25 |     @Component({{ '{' }}
 26 |         selector: 'app-test',
 27 |         templateUrl: './api.component.html',
 28 |         styleUrls: ['./api.component.scss']
 29 |     {{ '}' }})
 30 | 
 31 |     export class TestComponent {{ '{' }}
 32 | 
 33 |     {{ '}' }}
 34 | 
35 |
36 |
37 |
38 |
39 |
40 |
41 | Time: 42 | 43 | 49 |
50 |
51 |
52 |
53 |
54 |

55 | When using in component markup we can set Timepicker configuration with using 56 | attributes. 57 |

58 |
59 |
60 |
61 |
62 |
Directive with configuration
63 |
64 |
65 | 66 | 67 |
 68 | 
 69 |     <input type="time" atp-time-picker value="20:55" theme="dark" locale='fa' start='18:30' end="23:00" arrowStyle="{{'{'}}'background': 'red', 'color': 'white'{{'}'}}"/>
 70 |             
71 |
72 | 73 |
74 |
 75 | 
 76 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
 77 | 
 78 |     @Component({{ '{' }}
 79 |         selector: 'app-test',
 80 |         templateUrl: './api.component.html',
 81 |         styleUrls: ['./api.component.scss']
 82 |     {{ '}' }})
 83 | 
 84 |     export class TestComponent {{ '{' }}
 85 | 
 86 |     {{ '}' }}
 87 | 
88 |
89 |
90 |
91 |
92 |
93 |
94 | Time: 95 | 105 |
106 |
107 |
108 |
109 | 110 |
111 |
112 |
113 |
114 |
115 | Restrict user to select hour and AM only with Directive 116 |
117 |
118 |
119 | 120 | 121 |
122 | 
123 |     <input type="time" atp-time-picker value="8:55" onlyHour="true" onlyAM='true' />
124 |             
125 |
126 | 127 |
128 |
129 | 
130 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
131 | 
132 |     @Component({{ '{' }}
133 |         selector: 'app-test',
134 |         templateUrl: './api.component.html',
135 |         styleUrls: ['./api.component.scss']
136 |     {{ '}' }})
137 | 
138 |     export class TestComponent {{ '{' }}
139 | 
140 |     {{ '}' }}
141 | 
142 |
143 |
144 |
145 |
146 |
147 |
148 | Time: 149 | 156 |
157 |
158 |
159 |
160 | 161 |
162 |

Opening component programmatically

163 |

164 | You can also open a timepicker dialog programmatically. In order to open that, 165 | you need to import the service in your component 166 |

167 |
import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
170 |

171 | Then add it inside your test.component.ts or 172 | any other component that you want to use timepicker inside of that. 173 |

174 |
175 |
176 |
177 |
Programmatically
178 |
179 | 180 | 181 |
182 | 
183 | <button (click)="open()">Time</button>
184 | <input type="time" value="{{sintax}}">
185 | 
186 |
187 | 188 |
189 |
190 | import {{ '{' }} Component {{ '}' }} from '@angular/core';
191 | import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
192 | 
193 | @Component({{ '{' }}
194 |     selector: 'app-test',
195 |     templateUrl: './test.component.html',
196 |     styleUrls: ['./test.component.scss']
197 | {{ '}' }})
198 | export class TestComponent {{ '{' }}
199 |     public selectedTime: string;
200 | 
201 |     constructor( private atp: AmazingTimePickerService ) {{ '{' }} {{ '}' }}
202 | 
203 |     open() {{ '{' }}
204 |         const amazingTimePicker = this.atp.open();
205 |         amazingTimePicker.afterClose().subscribe(time => {{ '{' }}
206 |             this.selectedTime = time;
207 |         {{ '}' }});
208 |     {{ '}' }}
209 | {{ '}' }}
210 |                         
211 |
212 |
213 |
214 |
215 |
216 |
217 | 220 | 227 |
228 |
229 |
230 |
231 | 232 |
233 |

234 | Opening component programmatically with custom configuration 235 |

236 |

237 | You can also open a timepicker dialog programmatically with parameter to 238 | config Timepicker such as set time as default and change style and other 239 | things. 240 |

241 |

The config structure like below code:

242 |
{{ '{' }}
243 |     time: '18:30',
244 |     theme: 'dark',  // Default: 'light'
245 |     locale: 'fa',   // Default: 'en'
246 |     rangeTime: {{ '{' }}
247 |         start: '15:30',
248 |         end: '18:45'
249 |     {{ '}' }},
250 |     arrowStyle: {{ '{' }}
251 |         background: 'red',
252 |         color: 'white'
253 |     {{ '}' }}
254 | {{ '}' }}
255 |
256 |
257 |
258 |
259 |
Programmatically with parameter
260 |
261 |
262 | 263 | 264 |
265 | 
266 |     <button (click)="open()">Time</button>
267 |     <input type="time" value="{{sintax}}">
268 |     
269 |
270 | 271 |
272 |
273 |     import {{ '{' }} Component {{ '}' }} from '@angular/core';
274 |     import {{ '{' }} AmazingTimePickerService {{ '}' }} from 'amazing-time-picker';
275 | 
276 |     @Component({{ '{' }}
277 |         selector: 'app-test',
278 |         templateUrl: './test.component.html',
279 |         styleUrls: ['./test.component.scss']
280 |     {{ '}' }})
281 |     export class TestComponent {{ '{' }}
282 |         public selectedTime = '18:33';
283 | 
284 |         constructor(private atp: AmazingTimePickerService) {{ '{' }} {{ '}' }}
285 | 
286 |         open() {{ '{' }}
287 |             const amazingTimePicker = this.atp.open({{ '{' }}
288 |                 time:  this.selectedTime,
289 |                 theme: 'dark',
290 |                 arrowStyle: {{ '{' }}
291 |                     background: 'red',
292 |                     color: 'white'
293 |                 {{ '}' }}
294 |             {{ '}' }});
295 |             amazingTimePicker.afterClose().subscribe(time => {{ '{' }}
296 |                 this.selectedTime = time;
297 |             {{ '}' }});
298 |         {{ '}' }}
299 |     {{ '}' }}
300 |     
301 |
302 |
303 |
304 |
305 |
306 |
307 | 310 | 311 |
312 |
313 |
314 |
315 | 316 | 317 | 318 | 319 | 320 | --------------------------------------------------------------------------------