├── projects ├── io │ ├── src │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ └── code-snippets │ │ │ │ └── core │ │ │ │ ├── overview │ │ │ │ ├── installation.sh │ │ │ │ └── angular-cdk-installation.sh │ │ │ │ ├── in-viewport │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ │ │ ├── clipboard │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ │ │ ├── autocomplete │ │ │ │ ├── styles.scss │ │ │ │ ├── custom.scss │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ │ │ ├── nested-nav │ │ │ │ ├── custom.scss │ │ │ │ ├── app.component.ts │ │ │ │ └── app.component.html │ │ │ │ ├── code-prettify │ │ │ │ ├── app.js │ │ │ │ ├── app.component.html │ │ │ │ ├── app.component.ts │ │ │ │ └── custom.scss │ │ │ │ ├── outside │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ │ │ ├── file-input │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ │ │ ├── file-dropzone │ │ │ │ ├── app.component.scss │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ │ │ ├── lazy-renderer │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ │ │ └── forms │ │ │ │ ├── app.component.html │ │ │ │ └── app.component.ts │ │ ├── app │ │ │ ├── app.component.scss │ │ │ ├── core │ │ │ │ ├── pages │ │ │ │ │ ├── file-dropzone │ │ │ │ │ │ ├── file-dropzone.page.scss │ │ │ │ │ │ ├── file-dropzone.module.ts │ │ │ │ │ │ ├── file-dropzone.page.ts │ │ │ │ │ │ └── file-dropzone.page.html │ │ │ │ │ ├── lazy-renderer │ │ │ │ │ │ ├── test.component.ts │ │ │ │ │ │ ├── lazy-renderer.page.ts │ │ │ │ │ │ ├── lazy-renderer.module.ts │ │ │ │ │ │ └── lazy-renderer.page.html │ │ │ │ │ ├── forms │ │ │ │ │ │ ├── forms.module.ts │ │ │ │ │ │ ├── forms.page.ts │ │ │ │ │ │ └── forms.page.html │ │ │ │ │ ├── overview │ │ │ │ │ │ ├── core-overview.page.ts │ │ │ │ │ │ ├── core-overview.module.ts │ │ │ │ │ │ └── core-overview.page.html │ │ │ │ │ ├── code-prettify │ │ │ │ │ │ ├── code-prettify.module.ts │ │ │ │ │ │ ├── code-prettify.page.ts │ │ │ │ │ │ └── code-prettify.page.html │ │ │ │ │ ├── in-viewport │ │ │ │ │ │ ├── in-viewport.module.ts │ │ │ │ │ │ ├── in-viewport.page.ts │ │ │ │ │ │ └── in-viewport.page.html │ │ │ │ │ ├── outside-click │ │ │ │ │ │ ├── outside-click.module.ts │ │ │ │ │ │ ├── outside-click.page.ts │ │ │ │ │ │ └── outside-click.page.html │ │ │ │ │ ├── nested-nav │ │ │ │ │ │ ├── nested-nav.module.ts │ │ │ │ │ │ ├── nested-nav.page.ts │ │ │ │ │ │ └── nested-nav.page.html │ │ │ │ │ ├── file-input │ │ │ │ │ │ ├── file-input.module.ts │ │ │ │ │ │ ├── file-input.page.ts │ │ │ │ │ │ └── file-input.page.html │ │ │ │ │ ├── autocomplete │ │ │ │ │ │ ├── autocomplete.module.ts │ │ │ │ │ │ ├── autocomplete.page.ts │ │ │ │ │ │ └── autocomplete.page.html │ │ │ │ │ └── clipboard │ │ │ │ │ │ ├── clipboard.module.ts │ │ │ │ │ │ ├── clipboard.page.ts │ │ │ │ │ │ └── clipboard.page.html │ │ │ │ └── core.routing.ts │ │ │ ├── app.routing.ts │ │ │ ├── app.component.spec.ts │ │ │ ├── app.module.ts │ │ │ ├── app.component.ts │ │ │ └── app.component.html │ │ ├── favicon.ico │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── main.ts │ │ ├── test.ts │ │ ├── index.html │ │ ├── styles.scss │ │ ├── polyfills.ts │ │ └── theme.scss │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ ├── tslint.json │ ├── browserslist │ └── karma.conf.js ├── core │ ├── src │ │ ├── pipes │ │ │ ├── index.ts │ │ │ └── bytes │ │ │ │ ├── index.ts │ │ │ │ ├── bytes.module.ts │ │ │ │ └── bytes.pipe.ts │ │ ├── nested-nav │ │ │ ├── nested-nav.component.html │ │ │ ├── nested-nav.component.scss │ │ │ ├── index.ts │ │ │ ├── nested-nav.component.ts │ │ │ ├── nav-list.component.html │ │ │ ├── nested-nav.module.ts │ │ │ ├── nav-list.component.scss │ │ │ └── nav-list.component.ts │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── models.ts │ │ │ ├── byte.ts │ │ │ └── byte.spec.ts │ │ ├── forms │ │ │ ├── index.ts │ │ │ ├── url.validator.ts │ │ │ ├── forms.module.ts │ │ │ ├── file-type.validator.ts │ │ │ ├── max-size.validator.ts │ │ │ ├── total-size.validator.ts │ │ │ ├── equals.validator.spec.ts │ │ │ ├── equals.validator.ts │ │ │ ├── url.validator.spec.ts │ │ │ ├── file-type.validator.spec.ts │ │ │ ├── max-size.validator.spec.ts │ │ │ └── total-size.validator.spec.ts │ │ ├── window │ │ │ ├── index.ts │ │ │ ├── window.module.ts │ │ │ └── window.service.ts │ │ ├── lazy-renderer │ │ │ ├── lazy-renderer.component.scss │ │ │ ├── index.ts │ │ │ ├── models.ts │ │ │ ├── lazy-renderer.component.html │ │ │ ├── lazy-renderer.module.ts │ │ │ └── lazy-renderer.component.ts │ │ ├── outside-click │ │ │ ├── index.ts │ │ │ ├── outside-click.module.ts │ │ │ └── outside-click.directive.ts │ │ ├── code-prettify │ │ │ ├── code-loading.component.html │ │ │ ├── code-loading-error.component.html │ │ │ ├── index.ts │ │ │ ├── models.ts │ │ │ ├── code-loading.component.ts │ │ │ ├── reload.directive.ts │ │ │ ├── code.service.ts │ │ │ ├── code-loading-error.component.ts │ │ │ ├── prettify.service.ts │ │ │ ├── code-prettify.module.ts │ │ │ ├── code-prettify.component.html │ │ │ ├── code-prettify.component.ts │ │ │ └── code-prettify.component.scss │ │ ├── clipboard │ │ │ ├── index.ts │ │ │ ├── clipboard.module.ts │ │ │ ├── clipboard.directive.ts │ │ │ └── clipboard.service.ts │ │ ├── file │ │ │ ├── index.ts │ │ │ ├── models.ts │ │ │ ├── file.module.ts │ │ │ ├── file.ts │ │ │ ├── file-dropzone.directive.spec.ts │ │ │ └── file-input.directive.ts │ │ ├── autocomplete │ │ │ ├── autocomplete-option.component.html │ │ │ ├── models.ts │ │ │ ├── index.ts │ │ │ ├── autocomplete.component.html │ │ │ ├── autocomplete.component.scss │ │ │ ├── autocomplete-option.component.scss │ │ │ ├── autocomplete.module.ts │ │ │ ├── autocomplete-option.component.ts │ │ │ └── autocomplete.component.ts │ │ ├── in-viewport │ │ │ ├── index.ts │ │ │ ├── in-viewport.module.ts │ │ │ ├── models.ts │ │ │ └── in-viewport.directive.ts │ │ ├── public_api.ts │ │ └── test.ts │ ├── _variables.scss │ ├── .babelrc │ ├── ng-package.json │ ├── tslint.json │ ├── tsconfig.spec.json │ ├── tsconfig.lib.json │ ├── karma.conf.js │ ├── CHANGELOG.md │ └── package.json └── io-e2e │ ├── src │ ├── app.po.ts │ └── app.e2e-spec.ts │ ├── tsconfig.e2e.json │ └── protractor.conf.js ├── .firebaserc ├── .editorconfig ├── firebase.json ├── README.md ├── tsconfig.json ├── .gitignore ├── LICENSE ├── package.json ├── tslint.json ├── CODE_OF_CONDUCT.md └── angular.json /projects/io/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/io/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/core/src/pipes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bytes/index'; -------------------------------------------------------------------------------- /projects/core/src/nested-nav/nested-nav.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "ngez-platform" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /projects/core/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './byte'; 2 | export * from './models'; -------------------------------------------------------------------------------- /projects/core/src/nested-nav/nested-nav.component.scss: -------------------------------------------------------------------------------- 1 | :host{ 2 | display: block; 3 | } -------------------------------------------------------------------------------- /projects/core/src/forms/index.ts: -------------------------------------------------------------------------------- 1 | export * from './forms.module'; 2 | export * from './validators'; -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/overview/installation.sh: -------------------------------------------------------------------------------- 1 | npm install @ngez/core --save -------------------------------------------------------------------------------- /projects/core/src/pipes/bytes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bytes.pipe'; 2 | export * from './bytes.module'; -------------------------------------------------------------------------------- /projects/core/src/window/index.ts: -------------------------------------------------------------------------------- 1 | export * from './window.service'; 2 | export * from './window.module'; -------------------------------------------------------------------------------- /projects/io/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngez/platform/HEAD/projects/io/src/favicon.ico -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/overview/angular-cdk-installation.sh: -------------------------------------------------------------------------------- 1 | npm install --save @angular/cdk -------------------------------------------------------------------------------- /projects/io/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/core/src/lazy-renderer/lazy-renderer.component.scss: -------------------------------------------------------------------------------- 1 | .ngez-lazy-renderer-content{ 2 | display: inline; 3 | } -------------------------------------------------------------------------------- /projects/core/src/outside-click/index.ts: -------------------------------------------------------------------------------- 1 | export * from './outside-click.directive'; 2 | export * from './outside-click.module'; -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-loading.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-loading-error.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/in-viewport/app.component.html: -------------------------------------------------------------------------------- 1 |
Some content
-------------------------------------------------------------------------------- /projects/core/src/clipboard/index.ts: -------------------------------------------------------------------------------- 1 | export * from './clipboard.directive'; 2 | export * from './clipboard.service'; 3 | export * from './clipboard.module'; -------------------------------------------------------------------------------- /projects/core/src/lazy-renderer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lazy-renderer.component'; 2 | export * from './lazy-renderer.module'; 3 | export * from './models'; -------------------------------------------------------------------------------- /projects/core/src/nested-nav/index.ts: -------------------------------------------------------------------------------- 1 | export * from './nav-list.component'; 2 | export * from './nested-nav.component'; 3 | export * from './nested-nav.module'; -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/clipboard/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /projects/core/_variables.scss: -------------------------------------------------------------------------------- 1 | // $primary: #09203f; 2 | // $accent: #7f152e; 3 | 4 | $primary: #000b29; 5 | $accent: #D70026; 6 | $secondary: #EDB83D; 7 | $background: #F8F5F2; -------------------------------------------------------------------------------- /projects/core/src/file/index.ts: -------------------------------------------------------------------------------- 1 | export * from './file-dropzone.directive'; 2 | export * from './file-input.directive'; 3 | export * from './file.module'; 4 | export * from './models'; -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/autocomplete/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import '~@angular/cdk/overlay-prebuilt.css'; -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/nested-nav/custom.scss: -------------------------------------------------------------------------------- 1 | .ngez-nav-list-header, .ngez-nav-list-content a { 2 | &:hover { 3 | background-color: #1e1e1e !important; 4 | } 5 | } -------------------------------------------------------------------------------- /projects/core/src/autocomplete/autocomplete-option.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /projects/core/src/lazy-renderer/models.ts: -------------------------------------------------------------------------------- 1 | import { NgEzViewportOffsetConfig } from "../in-viewport"; 2 | 3 | export interface NgEzLazyRendererConfig { 4 | offset?: NgEzViewportOffsetConfig; 5 | } -------------------------------------------------------------------------------- /projects/core/src/in-viewport/index.ts: -------------------------------------------------------------------------------- 1 | export * from './in-viewport.directive'; 2 | export * from './in-viewport.module'; 3 | export { NgEzInViewportConfig, NgEzInViewportEvent, NgEzViewportOffsetConfig } from './models'; -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/code-prettify/app.js: -------------------------------------------------------------------------------- 1 | //some comment 2 | 3 | const x = 100; 4 | const y = 200; 5 | 6 | function sum(x, y){ 7 | return x + y; 8 | } 9 | 10 | console.log('x + y: ', sum(x, y)); -------------------------------------------------------------------------------- /projects/core/src/file/models.ts: -------------------------------------------------------------------------------- 1 | export class NgEzFileDropzoneEvent{ 2 | constructor(public event:NgEzFileDropzoneEventTypes, public value: File | File[]){} 3 | } 4 | 5 | export type NgEzFileDropzoneEventTypes = 'drop' | 'select'; -------------------------------------------------------------------------------- /projects/core/src/utils/models.ts: -------------------------------------------------------------------------------- 1 | export type NgEzByteUnit = 'byte' 2 | | 'kilobyte' 3 | | 'kibibyte' 4 | | 'megabyte' 5 | | 'mebibyte' 6 | | 'gigabyte' 7 | | 'gibibyte' 8 | | 'terabyte' 9 | | 'tebibyte'; -------------------------------------------------------------------------------- /projects/core/src/window/window.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { WINDOW_PROVIDERS } from "./window.service"; 3 | 4 | @NgModule({ 5 | providers: [WINDOW_PROVIDERS] 6 | }) 7 | export class WindowModule{} -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/file-dropzone/file-dropzone.page.scss: -------------------------------------------------------------------------------- 1 | .ngez-file-dropzone{ 2 | height: 300px; 3 | width: 300px; 4 | border: 1px dashed gray; 5 | &.active{ 6 | border: 1px solid blue; 7 | } 8 | } -------------------------------------------------------------------------------- /projects/core/src/autocomplete/models.ts: -------------------------------------------------------------------------------- 1 | export const defaultConfig: NgEzAutocompleteConfig = { 2 | maxHeight: 256 3 | } 4 | 5 | export interface NgEzAutocompleteConfig { 6 | labelExtractor?: (selectedOption: any) => any; 7 | maxHeight?: number; 8 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/outside/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | Click outside 3 | Clicks: {{clicks}} 4 |
-------------------------------------------------------------------------------- /projects/core/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["prismjs", { 4 | "languages": ["javascript", "css", "markup"], 5 | "plugins": ["line-numbers"], 6 | "theme": "twilight", 7 | "css": true 8 | }] 9 | ] 10 | } -------------------------------------------------------------------------------- /projects/core/src/pipes/bytes/bytes.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzBytesPipe } from "./bytes.pipe"; 3 | 4 | @NgModule({ 5 | declarations: [NgEzBytesPipe], 6 | exports: [NgEzBytesPipe] 7 | }) 8 | export class NgEzBytesModule{} -------------------------------------------------------------------------------- /projects/core/src/autocomplete/index.ts: -------------------------------------------------------------------------------- 1 | export * from './autocomplete.component'; 2 | export * from './autocomplete.directive'; 3 | export * from './autocomplete.module'; 4 | export * from './autocomplete-option.component' 5 | export { NgEzAutocompleteConfig } from './models' -------------------------------------------------------------------------------- /projects/io/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "src/assets/*", 10 | "**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /projects/io-e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/core/src/code-prettify/index.ts: -------------------------------------------------------------------------------- 1 | export * from './reload.directive'; 2 | export * from './code-prettify.component'; 3 | export * from './code-loading.component'; 4 | export * from './code-loading-error.component'; 5 | export * from './code-prettify.module'; 6 | export * from './models'; -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/file-input/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
-------------------------------------------------------------------------------- /projects/core/src/nested-nav/nested-nav.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: 'ngez-nested-nav', 5 | templateUrl: './nested-nav.component.html', 6 | styleUrls: ['./nested-nav.component.scss'] 7 | }) 8 | export class NgEzNestedNavComponent{} -------------------------------------------------------------------------------- /projects/core/src/code-prettify/models.ts: -------------------------------------------------------------------------------- 1 | export interface NgEzCodePrettifyConfig{ 2 | header?: string; 3 | code?: string; 4 | path?: string; 5 | language?: string 6 | theme?: string; 7 | linenums?: boolean | number; 8 | canCopy?: boolean; 9 | maxHeight?: number; 10 | } -------------------------------------------------------------------------------- /projects/io-e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /projects/core/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/core", 4 | "lib": { 5 | "entryFile": "src/public_api.ts" 6 | }, 7 | "whitelistedNonPeerDependencies": [ 8 | "fortawesome", 9 | "code-prettify" 10 | ] 11 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/file-dropzone/app.component.scss: -------------------------------------------------------------------------------- 1 | .ngez-file-dropzone{ 2 | height: 300px; 3 | width: 300px; 4 | border: 1px dashed gray; 5 | &.active{ 6 | border: 1px solid blue; 7 | } 8 | &.disabled{ 9 | border: 1px solid red; 10 | } 11 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /projects/core/src/lazy-renderer/lazy-renderer.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/nested-nav/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: 'app', 5 | templateUrl: './app.component.html' 6 | }) 7 | export class AppComponent{ 8 | 9 | routerLinkOptions = { 10 | exact: true 11 | }; 12 | } -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist/io", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /projects/core/src/outside-click/outside-click.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzOutsideClickDirective } from "./outside-click.directive"; 3 | 4 | @NgModule({ 5 | declarations: [NgEzOutsideClickDirective], 6 | exports: [NgEzOutsideClickDirective] 7 | }) 8 | export class NgEzOutsideClickModule{} -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/outside/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: 'app', 5 | templateUrl: './app.component.html' 6 | }) 7 | export class AppComponent { 8 | 9 | clicks = 0; 10 | 11 | onOutsideClick(){ 12 | this.clicks++; 13 | } 14 | } -------------------------------------------------------------------------------- /projects/core/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/lazy-renderer/test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: 'test', 5 | template: 'test component' 6 | }) 7 | export class TestComponent implements OnInit{ 8 | 9 | ngOnInit() { 10 | console.log('running ngOnInit()') 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NgEz 2 | 3 | A collection of minimalistic, easy-to-use and fully customizable Angular components, directives and services 4 | 5 | ## Documentation 6 | 7 | Checkout the [docs](https://ngez-platform.firebaseapp.com/#/core) 8 | 9 | ## Changelog 10 | 11 | [core](https://github.com/ngez/platform/blob/master/projects/core/CHANGELOG.md) 12 | -------------------------------------------------------------------------------- /projects/core/src/autocomplete/autocomplete.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 |
7 |
-------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-loading.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, TemplateRef } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: 'ngez-code-loading', 5 | templateUrl: './code-loading.component.html' 6 | }) 7 | export class NgEzCodeLoadingComponent{ 8 | 9 | @ViewChild('template') template: TemplateRef; 10 | } -------------------------------------------------------------------------------- /projects/core/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/core/src/autocomplete/autocomplete.component.scss: -------------------------------------------------------------------------------- 1 | .ngez-autocomplete{ 2 | width: 100%; 3 | background-color: white; 4 | overflow-x: hidden; 5 | box-shadow: 0 2px 4px -1px rgba(0,0,0,.2), 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12); 6 | } 7 | 8 | .ngez-autocomplete-content{ 9 | display: flex; 10 | flex-direction: column; 11 | } -------------------------------------------------------------------------------- /projects/io/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /projects/io-e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project 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.getTitleText()).toEqual('Welcome to io!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /projects/core/src/code-prettify/reload.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener, Output, EventEmitter } from "@angular/core"; 2 | 3 | @Directive({ 4 | selector: '[ngezReload]' 5 | }) 6 | export class NgEzReloadDirective{ 7 | 8 | @Output() reload = new EventEmitter(); 9 | 10 | @HostListener('click') 11 | private onClick(){ 12 | this.reload.emit(); 13 | } 14 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/autocomplete/custom.scss: -------------------------------------------------------------------------------- 1 | .ngez-autocomplete-option{ 2 | 3 | //By default a disabled option only will change the cursor on hover. 4 | //You can easily add your own styles like this: 5 | &.disabled{ 6 | color: red; 7 | } 8 | 9 | &.active, &:hover:not(.disabled){ 10 | background-color: #1e1e1e !important; 11 | } 12 | } -------------------------------------------------------------------------------- /projects/core/src/nested-nav/nav-list.component.html: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 | 8 |
9 |
-------------------------------------------------------------------------------- /projects/io/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { HttpClient } from "@angular/common/http"; 3 | import { Observable } from "rxjs"; 4 | 5 | @Injectable() 6 | export class CodeService{ 7 | 8 | constructor(private http: HttpClient){} 9 | 10 | get(path: string): Observable{ 11 | return this.http.get(path, {responseType: 'text'}) 12 | } 13 | } -------------------------------------------------------------------------------- /projects/core/src/in-viewport/in-viewport.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzInViewportDirective } from "./in-viewport.directive"; 3 | import { WINDOW_PROVIDERS } from "../window/window.service"; 4 | 5 | @NgModule({ 6 | declarations: [NgEzInViewportDirective], 7 | providers: [WINDOW_PROVIDERS], 8 | exports: [NgEzInViewportDirective] 9 | }) 10 | export class NgEzInViewportModule{} -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/lazy-renderer/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 |
9 |
-------------------------------------------------------------------------------- /projects/io/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 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /projects/io/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /projects/core/src/pipes/bytes/bytes.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from "@angular/core"; 2 | import { NgEzByteUnit, NgEzByteUtils } from "../../utils"; 3 | 4 | @Pipe({ 5 | name: 'ngezBytes' 6 | }) 7 | export class NgEzBytesPipe implements PipeTransform{ 8 | 9 | transform(value: number, from: NgEzByteUnit, to?: NgEzByteUnit){ 10 | if(!value) return value; 11 | 12 | return NgEzByteUtils.convert(value, from, to); 13 | } 14 | } -------------------------------------------------------------------------------- /projects/core/src/clipboard/clipboard.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { WINDOW_PROVIDERS } from "../window/window.service"; 3 | import { NgEzClipboardService } from "./clipboard.service"; 4 | import { NgEzClipboardDirective } from "./clipboard.directive"; 5 | 6 | @NgModule({ 7 | declarations: [NgEzClipboardDirective], 8 | providers: [NgEzClipboardService, WINDOW_PROVIDERS], 9 | exports: [NgEzClipboardDirective] 10 | }) 11 | export class NgEzClipboardModule{} -------------------------------------------------------------------------------- /projects/core/src/file/file.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzFileInputDirective } from "./file-input.directive"; 3 | import { NgEzFileDropzoneDirective } from "./file-dropzone.directive"; 4 | import { NgEzBytesModule } from "../pipes/bytes/bytes.module"; 5 | 6 | @NgModule({ 7 | declarations: [NgEzFileInputDirective, NgEzFileDropzoneDirective], 8 | exports: [NgEzFileInputDirective, NgEzFileDropzoneDirective, NgEzBytesModule] 9 | }) 10 | export class NgEzFileModule{} -------------------------------------------------------------------------------- /projects/io/src/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | 4 | const routes: Routes = [{ 5 | path: '', 6 | pathMatch: 'full', 7 | redirectTo: 'core' 8 | }, { 9 | path: 'core', 10 | loadChildren: './core/core.routing#CoreRoutingModule' 11 | }]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forRoot(routes, { useHash: true })], 15 | exports: [RouterModule] 16 | }) 17 | export class AppRoutingModule{} -------------------------------------------------------------------------------- /projects/core/src/autocomplete/autocomplete-option.component.scss: -------------------------------------------------------------------------------- 1 | @import "../../variables"; 2 | 3 | .ngez-autocomplete-option{ 4 | display:block; 5 | padding: 8px; 6 | width: auto; 7 | 8 | &.disabled{ 9 | cursor: not-allowed; 10 | } 11 | 12 | &.active{ 13 | background-color: $primary; 14 | color: white; 15 | } 16 | 17 | &:hover:not(.disabled){ 18 | background-color: $primary; 19 | color: white; 20 | cursor: pointer; 21 | } 22 | } -------------------------------------------------------------------------------- /projects/core/src/lazy-renderer/lazy-renderer.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzLazyRendererComponent } from "./lazy-renderer.component"; 3 | import { NgEzInViewportModule } from "../in-viewport/in-viewport.module"; 4 | import { CommonModule } from "@angular/common"; 5 | 6 | @NgModule({ 7 | imports: [NgEzInViewportModule, CommonModule], 8 | declarations: [NgEzLazyRendererComponent], 9 | exports: [NgEzLazyRendererComponent] 10 | }) 11 | export class NgEzLazyRendererModule{} -------------------------------------------------------------------------------- /projects/core/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of core 3 | */ 4 | 5 | export * from './autocomplete/index'; 6 | export * from './nested-nav/index'; 7 | export * from './outside-click/index'; 8 | export * from './code-prettify/index'; 9 | export * from './clipboard/index'; 10 | export * from './in-viewport/index'; 11 | export * from './lazy-renderer/index'; 12 | export * from './file/index'; 13 | export * from './forms/index'; 14 | export * from './utils/index'; 15 | export * from './pipes/index'; 16 | -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/clipboard/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzClipboardService } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'app', 6 | templateUrl: './app.component.html' 7 | }) 8 | export class AppComponent { 9 | 10 | constructor(private clipboard: NgEzClipboardService) {} 11 | 12 | onCopy() { 13 | const didCopy = this.clipboard.copy('This will be copied to clipboard'); 14 | console.log(didCopy); 15 | } 16 | } -------------------------------------------------------------------------------- /projects/core/src/file/file.ts: -------------------------------------------------------------------------------- 1 | import { Input } from '@angular/core'; 2 | 3 | export abstract class NgEzFileBase{ 4 | 5 | @Input() accept: string; 6 | 7 | @Input() set multiple(multiple){ 8 | this._multiple = multiple; 9 | }; 10 | 11 | get multiple(){ 12 | const multiple = this._multiple; 13 | return multiple || multiple === '' ? true : false; 14 | } 15 | 16 | private _multiple: any; 17 | 18 | protected fileInput: HTMLInputElement; 19 | 20 | protected listener: Function; 21 | } -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-loading-error.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, TemplateRef, ViewChild, ContentChild } from "@angular/core"; 2 | import { NgEzReloadDirective } from "./reload.directive"; 3 | 4 | @Component({ 5 | selector: 'ngez-code-loading-error', 6 | templateUrl: './code-loading-error.component.html' 7 | }) 8 | export class NgEzCodeLoadingErrorComponent{ 9 | 10 | @ViewChild('template') template: TemplateRef; 11 | 12 | @ContentChild(NgEzReloadDirective) reloadDirective: NgEzReloadDirective; 13 | 14 | } -------------------------------------------------------------------------------- /projects/core/src/nested-nav/nested-nav.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzNestedNavComponent } from "./nested-nav.component"; 3 | import { NgEzNavListComponent } from "./nav-list.component"; 4 | import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; 5 | import { CommonModule } from "@angular/common"; 6 | 7 | @NgModule({ 8 | imports: [CommonModule, FontAwesomeModule], 9 | declarations: [NgEzNestedNavComponent, NgEzNavListComponent], 10 | exports: [NgEzNestedNavComponent, NgEzNavListComponent] 11 | }) 12 | export class NgEzNestedNavModule{} -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/autocomplete/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 |
10 | {{user.username}} 11 |
12 |
13 |
-------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/file-dropzone/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |

7 | Files: 8 |

9 |
    10 |
  • 11 | {{file.name}} 12 | 13 |
  • 14 |
15 |
-------------------------------------------------------------------------------- /projects/core/src/code-prettify/prettify.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { $prettyPrintOne } from './prettify'; 3 | 4 | @Injectable() 5 | export class PrettifyService { 6 | 7 | formatCode(code: string, language?: string, linenums?: number | boolean) { 8 | return $prettyPrintOne(this.encode(code), language, linenums); 9 | } 10 | 11 | private encode(value: string){ 12 | return value 13 | .replace(/&/g, '&') 14 | .replace(/"/g, '"') 15 | .replace(/'/g, ''') 16 | .replace(//g, '>'); 18 | } 19 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "importHelpers": true, 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ], 20 | "paths": { 21 | "@ngez/core": [ 22 | "dist/core" 23 | ], 24 | "@ngez/core/*": [ 25 | "dist/core/*" 26 | ] 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /projects/core/src/in-viewport/models.ts: -------------------------------------------------------------------------------- 1 | export interface NgEzInViewportConfig{ 2 | offset?: NgEzViewportOffsetConfig 3 | } 4 | 5 | export interface NgEzViewportOffsetConfig{ 6 | top?: number; 7 | bottom?: number; 8 | left?: number; 9 | right?: number; 10 | } 11 | 12 | export interface NgEzInViewportEvent{ 13 | top: boolean; 14 | bottom: boolean; 15 | left: boolean; 16 | right: boolean; 17 | any: boolean; 18 | all: boolean; 19 | } 20 | 21 | export const defaultConfig: NgEzInViewportConfig = {} 22 | 23 | export const defaultOffsetConfig: NgEzViewportOffsetConfig = { 24 | top: 0, 25 | bottom: 0, 26 | left: 0, 27 | right: 0 28 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/code-prettify/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Loading code... 6 | 7 | 8 | 9 |
10 | Something went wrong while loading the code. 11 | 12 |
13 |
14 |
-------------------------------------------------------------------------------- /projects/core/src/forms/url.validator.ts: -------------------------------------------------------------------------------- 1 | import { Directive, forwardRef } from "@angular/core"; 2 | import { Validator, AbstractControl, NG_VALIDATORS, ValidationErrors } from "@angular/forms"; 3 | import { NgEzValidators } from "./validators"; 4 | 5 | @Directive({ 6 | selector: '[ngezUrl][formControlName],[ngezUrl][formControl],[ngezUrl][ngModel]', 7 | providers: [{ 8 | provide: NG_VALIDATORS, 9 | useExisting: forwardRef(() => NgEzUrlValidator), 10 | multi: true 11 | }] 12 | }) 13 | export class NgEzUrlValidator implements Validator{ 14 | 15 | validate(control: AbstractControl): ValidationErrors{ 16 | return NgEzValidators.url(control); 17 | } 18 | } -------------------------------------------------------------------------------- /projects/core/src/clipboard/clipboard.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, Output, EventEmitter, HostListener } from "@angular/core"; 2 | import { NgEzClipboardService } from "./clipboard.service"; 3 | 4 | @Directive({ selector: '[ngezClipboard]' }) 5 | export class NgEzClipboardDirective { 6 | 7 | @Input("ngezClipboard") payload: string; 8 | 9 | @Output() copy = new EventEmitter(); 10 | 11 | constructor(private clipboard: NgEzClipboardService){} 12 | 13 | @HostListener("click", ["$event"]) 14 | public onClick(event: MouseEvent): void { 15 | if (!this.payload) 16 | return; 17 | 18 | this.copy.emit(this.clipboard.copy(this.payload)); 19 | } 20 | } -------------------------------------------------------------------------------- /projects/core/src/forms/forms.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzUrlValidator } from "./url.validator"; 3 | import { NgEzEqualsValidator } from "./equals.validator"; 4 | import { NgEzFileTypeValidator } from "./file-type.validator"; 5 | import { NgEzMaxSizeValidator } from "./max-size.validator"; 6 | import { NgEzTotalSizeValidator } from "./total-size.validator"; 7 | 8 | const COMMON_EXPORTS = [ 9 | NgEzUrlValidator, 10 | NgEzEqualsValidator, 11 | NgEzFileTypeValidator, 12 | NgEzMaxSizeValidator, 13 | NgEzTotalSizeValidator 14 | ]; 15 | 16 | @NgModule({ 17 | declarations: COMMON_EXPORTS, 18 | exports: COMMON_EXPORTS 19 | }) 20 | export class NgEzFormsModule{} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # firebase 4 | /.firebase 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /out-tsc 10 | 11 | # dependencies 12 | /node_modules 13 | 14 | # IDEs and editors 15 | /.idea 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # IDE - VSCode 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | 30 | # misc 31 | /.sass-cache 32 | /connect.lock 33 | /coverage 34 | /libpeerconnection.log 35 | npm-debug.log 36 | yarn-error.log 37 | testem.log 38 | /typings 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /projects/io/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/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /projects/io/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/core/src/autocomplete/autocomplete.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { NgEzAutocompleteComponent } from './autocomplete.component'; 3 | import { NgEzAutocompleteDirective } from './autocomplete.directive'; 4 | import { Overlay } from '@angular/cdk/overlay'; 5 | import { NgEzAutocompleteOptionComponent } from './autocomplete-option.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | NgEzAutocompleteComponent, 10 | NgEzAutocompleteDirective, 11 | NgEzAutocompleteOptionComponent 12 | ], 13 | exports: [NgEzAutocompleteComponent, NgEzAutocompleteDirective, NgEzAutocompleteOptionComponent], 14 | entryComponents: [NgEzAutocompleteComponent], 15 | providers: [Overlay] 16 | }) 17 | export class NgEzAutocompleteModule { } 18 | -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/in-viewport/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from "@angular/core"; 2 | import { NgEzInViewportEvent, NgEzInViewportDirective } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'app', 6 | templateUrl: './app.component.html' 7 | }) 8 | export class AppComponent{ 9 | 10 | @ViewChild(NgEzInViewportDirective) inViewport: NgEzInViewportDirective; 11 | 12 | onChange(event: NgEzInViewportEvent){ 13 | console.log(event) 14 | } 15 | 16 | someMethod(){ 17 | //NgEzInViewportDirective will check if the element is in the viewport during scroll, window resize and on init. 18 | //You can always get reference to the directive and check manually. 19 | this.inViewport.check(); 20 | } 21 | } -------------------------------------------------------------------------------- /projects/core/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed().initTestEnvironment( 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting() 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/forms/forms.module.ts: -------------------------------------------------------------------------------- 1 | 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | import { FormsPage } from "./forms.page"; 5 | import { FlexLayoutModule } from "@angular/flex-layout"; 6 | import { CommonModule } from "@angular/common"; 7 | import { NgEzCodePrettifyModule } from '@ngez/core'; 8 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 9 | 10 | const routes : Routes = [{ 11 | path: '', 12 | component: FormsPage 13 | }]; 14 | 15 | @NgModule({ 16 | imports: [ 17 | NgEzCodePrettifyModule, 18 | MatProgressSpinnerModule, 19 | FlexLayoutModule, 20 | RouterModule.forChild(routes), 21 | CommonModule], 22 | declarations: [FormsPage] 23 | }) 24 | export class FormsModule{} -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/overview/core-overview.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import { Observable } from "rxjs"; 3 | import { map, tap } from "rxjs/operators"; 4 | import { NgEzCodePrettifyConfig } from "@ngez/core"; 5 | 6 | @Component({ 7 | selector: 'core-overview', 8 | templateUrl: './core-overview.page.html' 9 | }) 10 | export class CoreOverviewPage { 11 | 12 | isLoading = false; 13 | hasError = false; 14 | 15 | options: NgEzCodePrettifyConfig = { 16 | language: 'sh', 17 | path: 'assets/code-snippets/core/overview/installation.sh', 18 | theme: 'dark' 19 | }; 20 | 21 | options2: NgEzCodePrettifyConfig = { 22 | language: 'sh', 23 | path: 'assets/code-snippets/core/overview/angular-cdk-installation.sh', 24 | theme: 'dark' 25 | }; 26 | 27 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/code-prettify/code-prettify.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | import { CodePrettifyPage } from "./code-prettify.page"; 4 | import { FlexLayoutModule } from "@angular/flex-layout"; 5 | import { CommonModule } from "@angular/common"; 6 | import { NgEzCodePrettifyModule } from '@ngez/core'; 7 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 8 | 9 | const routes : Routes = [{ 10 | path: '', 11 | component: CodePrettifyPage 12 | }]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | NgEzCodePrettifyModule, 17 | MatProgressSpinnerModule, 18 | FlexLayoutModule, 19 | RouterModule.forChild(routes), 20 | CommonModule], 21 | declarations: [CodePrettifyPage] 22 | }) 23 | export class CodePrettifyModule{} -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/overview/core-overview.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CoreOverviewPage } from "./core-overview.page"; 3 | import { Routes, RouterModule } from '@angular/router'; 4 | import { NgEzCodePrettifyModule } from '@ngez/core'; 5 | import { CommonModule } from "@angular/common"; 6 | import { FlexLayoutModule } from "@angular/flex-layout"; 7 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 8 | 9 | const routes: Routes = [{ 10 | path: '', 11 | component: CoreOverviewPage 12 | }]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | FlexLayoutModule, 17 | MatProgressSpinnerModule, 18 | RouterModule.forChild(routes), 19 | NgEzCodePrettifyModule, 20 | CommonModule 21 | ], 22 | declarations: [CoreOverviewPage] 23 | }) 24 | export class CoreOverviewModule{} -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/nested-nav/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Overview 4 | 5 | Autocomplete 6 | Nested Nav 7 | 8 | 9 | Outside Click 10 | 11 | 12 | -------------------------------------------------------------------------------- /projects/core/src/lazy-renderer/lazy-renderer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, ContentChild, TemplateRef, Input } from "@angular/core"; 2 | import { NgEzInViewportEvent } from "../in-viewport/models"; 3 | import { NgEzLazyRendererConfig } from "./models"; 4 | 5 | @Component({ 6 | selector: 'ngez-lazy-renderer', 7 | templateUrl: './lazy-renderer.component.html', 8 | styleUrls: ['./lazy-renderer.component.scss'] 9 | }) 10 | export class NgEzLazyRendererComponent { 11 | 12 | @Input() config: NgEzLazyRendererConfig; 13 | 14 | @ContentChild(TemplateRef) template: TemplateRef; 15 | 16 | shouldRender = false; 17 | 18 | onChange(change: NgEzInViewportEvent) { 19 | if (change.any && !this.shouldRender) 20 | this.render(); 21 | } 22 | 23 | render() { 24 | setTimeout(() => this.shouldRender = true, 0); 25 | } 26 | } -------------------------------------------------------------------------------- /projects/core/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "annotateForClosureCompiler": true, 22 | "skipTemplateCodegen": true, 23 | "strictMetadataEmit": true, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true, 26 | "enableResourceInlining": true 27 | }, 28 | "exclude": [ 29 | "src/test.ts", 30 | "**/*.spec.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/io-e2e/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 | './src/**/*.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: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/in-viewport/in-viewport.module.ts: -------------------------------------------------------------------------------- 1 | 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | import { InViewportPage } from "./in-viewport.page"; 5 | import { FlexLayoutModule } from "@angular/flex-layout"; 6 | import { CommonModule } from "@angular/common"; 7 | import { NgEzCodePrettifyModule, NgEzInViewportModule } from '@ngez/core'; 8 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 9 | 10 | const routes : Routes = [{ 11 | path: '', 12 | component: InViewportPage 13 | }]; 14 | 15 | @NgModule({ 16 | imports: [ 17 | NgEzCodePrettifyModule, 18 | MatProgressSpinnerModule, 19 | FlexLayoutModule, 20 | RouterModule.forChild(routes), 21 | CommonModule, 22 | NgEzInViewportModule], 23 | declarations: [InViewportPage] 24 | }) 25 | export class InViewportModule{} -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/lazy-renderer/lazy-renderer.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import * as faker from 'faker'; 3 | import { NgEzCodePrettifyConfig } from "@ngez/core"; 4 | 5 | @Component({ 6 | selector: 'lazy-renderer', 7 | templateUrl: './lazy-renderer.page.html' 8 | }) 9 | export class LazyRendererPage{ 10 | 11 | image = faker.image.people(); 12 | 13 | code1: NgEzCodePrettifyConfig = { 14 | language: 'html', 15 | path: 'assets/code-snippets/core/lazy-renderer/app.component.html', 16 | theme: 'dark', 17 | canCopy: true, 18 | header: 'app.component.html' 19 | }; 20 | 21 | code2: NgEzCodePrettifyConfig = { 22 | language: 'typescript', 23 | path: 'assets/code-snippets/core/lazy-renderer/app.component.ts', 24 | theme: 'dark', 25 | canCopy: true, 26 | header: 'app.component.ts' 27 | }; 28 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/lazy-renderer/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from "@angular/core"; 2 | import { NgEzLazyRendererConfig, NgEzLazyRendererComponent } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'app', 6 | templateUrl: './app.component.html' 7 | }) 8 | export class AppComponent { 9 | 10 | //By default the content will be rendered exactly when entering the viewport. 11 | //You can add an offset so that it's rendered for example 100px before actually visible. 12 | config: NgEzLazyRendererConfig = { 13 | offset: { 14 | top: 100 15 | } 16 | } 17 | 18 | @ViewChild(NgEzLazyRendererComponent) lazyRenderer: NgEzLazyRendererComponent; 19 | 20 | someMethod() { 21 | //You can always get a reference to the NgEzLazyRendererComponent and manually render the content. 22 | this.lazyRenderer.render(); 23 | } 24 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/in-viewport/in-viewport.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig, NgEzInViewportEvent } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'in-viewport', 6 | templateUrl: './in-viewport.page.html' 7 | }) 8 | export class InViewportPage{ 9 | 10 | code1: NgEzCodePrettifyConfig = { 11 | language: 'html', 12 | path: 'assets/code-snippets/core/in-viewport/app.component.html', 13 | theme: 'dark', 14 | canCopy: true, 15 | header: 'app.component.html' 16 | }; 17 | 18 | code2: NgEzCodePrettifyConfig = { 19 | language: 'typescript', 20 | path: 'assets/code-snippets/core/in-viewport/app.component.ts', 21 | theme: 'dark', 22 | canCopy: true, 23 | header: 'app.component.ts' 24 | }; 25 | 26 | onChange(e: NgEzInViewportEvent){ 27 | console.log(e) 28 | } 29 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/outside-click/outside-click.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | import { OutsideClickPage } from "./outside-click.page"; 4 | import { FlexLayoutModule } from "@angular/flex-layout"; 5 | import { CommonModule } from "@angular/common"; 6 | import { NgEzCodePrettifyModule, NgEzOutsideClickModule } from '@ngez/core'; 7 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 8 | 9 | const routes : Routes = [{ 10 | path: '', 11 | component: OutsideClickPage 12 | }]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | NgEzCodePrettifyModule, 17 | MatProgressSpinnerModule, 18 | FlexLayoutModule, 19 | RouterModule.forChild(routes), 20 | CommonModule, 21 | NgEzOutsideClickModule], 22 | declarations: [OutsideClickPage] 23 | }) 24 | export class OutsideClickModule{} -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/outside-click/outside-click.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'outside-click', 6 | templateUrl: './outside-click.page.html' 7 | }) 8 | export class OutsideClickPage { 9 | 10 | config1: NgEzCodePrettifyConfig = { 11 | language: 'html', 12 | path: 'assets/code-snippets/core/outside/app.component.html', 13 | theme: 'dark', 14 | canCopy: true, 15 | header: 'app.component.html' 16 | }; 17 | 18 | config2: NgEzCodePrettifyConfig = { 19 | language: 'typescript', 20 | path: 'assets/code-snippets/core/outside/app.component.ts', 21 | theme: 'dark', 22 | canCopy: true, 23 | header: 'app.component.ts' 24 | }; 25 | 26 | clicks = 0; 27 | 28 | onOutsideClick(){ 29 | this.clicks++; 30 | } 31 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/forms/forms.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig, NgEzByteUtils } from "@ngez/core"; 3 | import { FormBuilder, FormGroup } from "@angular/forms"; 4 | 5 | @Component({ 6 | selector: 'forms', 7 | templateUrl: './forms.page.html' 8 | }) 9 | export class FormsPage{ 10 | 11 | form: FormGroup; 12 | 13 | code1: NgEzCodePrettifyConfig = { 14 | language: 'html', 15 | path: 'assets/code-snippets/core/forms/app.component.html', 16 | theme: 'dark', 17 | canCopy: true, 18 | header: 'Template-driven forms (FormsModule)' 19 | }; 20 | 21 | code2: NgEzCodePrettifyConfig = { 22 | language: 'typescript', 23 | path: 'assets/code-snippets/core/forms/app.component.ts', 24 | theme: 'dark', 25 | canCopy: true, 26 | header: 'Model-driven forms (ReactiveFormsModule)' 27 | }; 28 | 29 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/nested-nav/nested-nav.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | import { NestedNavPage } from "./nested-nav.page"; 4 | import { FlexLayoutModule } from "@angular/flex-layout"; 5 | import { CommonModule } from "@angular/common"; 6 | import { NgEzCodePrettifyModule } from '@ngez/core'; 7 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 8 | import { NgEzNestedNavModule } from '@ngez/core'; 9 | 10 | const routes : Routes = [{ 11 | path: '', 12 | component: NestedNavPage 13 | }]; 14 | 15 | @NgModule({ 16 | imports: [ 17 | NgEzCodePrettifyModule, 18 | MatProgressSpinnerModule, 19 | FlexLayoutModule, 20 | RouterModule.forChild(routes), 21 | CommonModule, 22 | NgEzNestedNavModule], 23 | declarations: [NestedNavPage] 24 | }) 25 | export class NestedNavModule{} -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/forms/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
-------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/file-dropzone/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from "@angular/core"; 2 | import { NgEzByteUtils, NgEzValidators, NgEzFileDropzoneDirective } from "@ngez/core"; 3 | import { FormBuilder, FormGroup } from "@angular/forms"; 4 | 5 | @Component({ 6 | selector: 'app', 7 | templateUrl: './app.component.html' 8 | }) 9 | export class AppComponent { 10 | 11 | @ViewChild(NgEzFileDropzoneDirective) dropzone: NgEzFileDropzoneDirective; 12 | 13 | form: FormGroup; 14 | 15 | constructor(fb: FormBuilder){ 16 | this.form = fb.group({ 17 | files: [[]] 18 | }); 19 | } 20 | 21 | onRemove(file: File){ 22 | const control = this.form.get('files'); 23 | control.setValue((control.value as File[]).filter(f => file != f)) 24 | } 25 | 26 | //Programtically open file browser 27 | onBrowse(){ 28 | this.dropzone.browse(); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/file-input/file-input.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | import { FileInputPage } from "./file-input.page"; 4 | import { FlexLayoutModule } from "@angular/flex-layout"; 5 | import { CommonModule } from "@angular/common"; 6 | import { NgEzCodePrettifyModule, NgEzFileModule } from '@ngez/core'; 7 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 8 | import { ReactiveFormsModule } from "@angular/forms"; 9 | 10 | const routes : Routes = [{ 11 | path: '', 12 | component: FileInputPage 13 | }]; 14 | 15 | @NgModule({ 16 | imports: [ 17 | NgEzCodePrettifyModule, 18 | MatProgressSpinnerModule, 19 | FlexLayoutModule, 20 | RouterModule.forChild(routes), 21 | CommonModule, 22 | NgEzFileModule, 23 | ReactiveFormsModule], 24 | declarations: [FileInputPage] 25 | }) 26 | export class FileInputModule{} -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/code-prettify/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'app', 6 | templateUrl: './app.component.html' 7 | }) 8 | export class AppComponent { 9 | 10 | config: NgEzCodePrettifyConfig = { 11 | language: 'typescript', 12 | //If you already have the code, ignore this and pass it in the "code" property. 13 | //Assuming you have a file in this path 14 | path: 'assets/code-snippets/app.js', 15 | //By default the component supports light and dark themes, but you can implement your own 16 | theme: 'dark', 17 | //Whether it should show the copy to clipboard button. 18 | canCopy: true, 19 | //File name or any text you want in the header. 20 | header: 'app.js', 21 | //Enable line numbers. It can also be a number if you'd like to start at a different number. 22 | linenums: true 23 | }; 24 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/file-input/file-input.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig } from "@ngez/core"; 3 | import { FormBuilder, FormGroup } from "@angular/forms"; 4 | 5 | @Component({ 6 | selector: 'file-input', 7 | templateUrl: './file-input.page.html' 8 | }) 9 | export class FileInputPage{ 10 | 11 | form: FormGroup; 12 | 13 | code1: NgEzCodePrettifyConfig = { 14 | language: 'html', 15 | path: 'assets/code-snippets/core/file-input/app.component.html', 16 | theme: 'dark', 17 | canCopy: true, 18 | header: 'app.component.html' 19 | }; 20 | 21 | code2: NgEzCodePrettifyConfig = { 22 | language: 'typescript', 23 | path: 'assets/code-snippets/core/file-input/app.component.ts', 24 | theme: 'dark', 25 | canCopy: true, 26 | header: 'app.component.ts' 27 | }; 28 | 29 | constructor(fb: FormBuilder){ 30 | this.form = fb.group({ 31 | file: null 32 | }); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/autocomplete/autocomplete.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | import { AutocompletePage } from "./autocomplete.page"; 4 | import { FlexLayoutModule } from "@angular/flex-layout"; 5 | import { NgEzAutocompleteModule } from "@ngez/core"; 6 | import { CommonModule } from "@angular/common"; 7 | import { ReactiveFormsModule } from "@angular/forms"; 8 | import { NgEzCodePrettifyModule } from '@ngez/core'; 9 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 10 | 11 | const routes : Routes = [{ 12 | path: '', 13 | component: AutocompletePage 14 | }]; 15 | 16 | @NgModule({ 17 | imports: [ 18 | NgEzCodePrettifyModule, 19 | MatProgressSpinnerModule, 20 | FlexLayoutModule, 21 | ReactiveFormsModule, 22 | RouterModule.forChild(routes), 23 | NgEzAutocompleteModule, 24 | CommonModule], 25 | declarations: [AutocompletePage] 26 | }) 27 | export class AutocompleteModule{} -------------------------------------------------------------------------------- /projects/io/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-devkit/build-angular'], 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-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /projects/core/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-devkit/build-angular'], 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-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/lazy-renderer/lazy-renderer.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | import { LazyRendererPage } from "./lazy-renderer.page"; 4 | import { FlexLayoutModule } from "@angular/flex-layout"; 5 | import { CommonModule } from "@angular/common"; 6 | import { NgEzCodePrettifyModule, NgEzLazyRendererModule } from '@ngez/core'; 7 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 8 | import { NgEzInViewportModule } from "../../../../../../core/src/in-viewport"; 9 | import { TestComponent } from "./test.component"; 10 | 11 | const routes : Routes = [{ 12 | path: '', 13 | component: LazyRendererPage 14 | }]; 15 | 16 | @NgModule({ 17 | imports: [ 18 | NgEzCodePrettifyModule, 19 | MatProgressSpinnerModule, 20 | FlexLayoutModule, 21 | RouterModule.forChild(routes), 22 | NgEzLazyRendererModule, 23 | NgEzInViewportModule, 24 | CommonModule], 25 | declarations: [LazyRendererPage, TestComponent] 26 | }) 27 | export class LazyRendererModule{} -------------------------------------------------------------------------------- /projects/io/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', async(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | it(`should have as title 'app'`, async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app.title).toEqual('app'); 20 | })); 21 | it('should render title in a h1 tag', async(() => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | fixture.detectChanges(); 24 | const compiled = fixture.debugElement.nativeElement; 25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to ngez!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-prettify.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { NgEzCodePrettifyComponent } from "./code-prettify.component"; 3 | import { CodeService } from "./code.service"; 4 | import { PrettifyService } from "./prettify.service"; 5 | import { NgEzCodeLoadingComponent } from "./code-loading.component"; 6 | import { NgEzCodeLoadingErrorComponent } from "./code-loading-error.component"; 7 | import { CommonModule } from "@angular/common"; 8 | import { NgEzReloadDirective } from "./reload.directive"; 9 | import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; 10 | import { NgEzClipboardModule } from "../clipboard/index"; 11 | 12 | const COMMON_DECLARATIONS = [ 13 | NgEzCodePrettifyComponent, 14 | NgEzCodeLoadingComponent, 15 | NgEzCodeLoadingErrorComponent, 16 | NgEzReloadDirective 17 | ]; 18 | 19 | @NgModule({ 20 | imports: [CommonModule, FontAwesomeModule, NgEzClipboardModule], 21 | declarations: COMMON_DECLARATIONS, 22 | exports: COMMON_DECLARATIONS, 23 | providers: [CodeService, PrettifyService] 24 | }) 25 | export class NgEzCodePrettifyModule{} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ngez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/nested-nav/nested-nav.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'nested-nav', 6 | templateUrl: './nested-nav.page.html' 7 | }) 8 | export class NestedNavPage { 9 | 10 | routerLinkOptions = { 11 | exact: true 12 | }; 13 | 14 | code1: NgEzCodePrettifyConfig = { 15 | language: 'html', 16 | path: 'assets/code-snippets/core/nested-nav/app.component.html', 17 | theme: 'dark', 18 | canCopy: true, 19 | header: 'app.component.html' 20 | }; 21 | 22 | code2: NgEzCodePrettifyConfig = { 23 | language: 'typescript', 24 | path: 'assets/code-snippets/core/nested-nav/app.component.ts', 25 | theme: 'dark', 26 | canCopy: true, 27 | header: 'app.component.ts' 28 | }; 29 | 30 | code3: NgEzCodePrettifyConfig = { 31 | language: 'css', 32 | path: 'assets/code-snippets/core/nested-nav/custom.scss', 33 | theme: 'dark', 34 | canCopy: true, 35 | header: 'styles.scss' 36 | }; 37 | } -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-prettify.component.html: -------------------------------------------------------------------------------- 1 |
2 | {{config.header}} 3 |
4 |
5 |
6 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

19 |         
20 |
21 |
-------------------------------------------------------------------------------- /projects/io/src/app/core/pages/file-dropzone/file-dropzone.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FlexLayoutModule } from '@angular/flex-layout'; 4 | import { ReactiveFormsModule } from '@angular/forms'; 5 | import { MatButtonModule } from '@angular/material'; 6 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 7 | import { RouterModule, Routes } from '@angular/router'; 8 | import { NgEzFileModule } from 'projects/core/src/file'; 9 | import { NgEzCodePrettifyModule } from 'projects/core/src/public_api'; 10 | 11 | import { FileDropzonePage } from './file-dropzone.page'; 12 | 13 | // import { NgEzCodePrettifyModule, NgEzFileModule } from '@ngez/core'; 14 | const routes: Routes = [ 15 | { 16 | path: "", 17 | component: FileDropzonePage 18 | } 19 | ]; 20 | 21 | @NgModule({ 22 | imports: [ 23 | NgEzCodePrettifyModule, 24 | MatProgressSpinnerModule, 25 | FlexLayoutModule, 26 | RouterModule.forChild(routes), 27 | CommonModule, 28 | NgEzFileModule, 29 | MatButtonModule, 30 | ReactiveFormsModule 31 | ], 32 | declarations: [FileDropzonePage] 33 | }) 34 | export class FileDropzoneModule {} 35 | -------------------------------------------------------------------------------- /projects/core/src/nested-nav/nav-list.component.scss: -------------------------------------------------------------------------------- 1 | @import "../../variables"; 2 | 3 | .ngez-nav-list{ 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | 8 | .ngez-nav-list-header { 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: space-between; 12 | border: none; 13 | background-color: transparent; 14 | margin-right: 0; 15 | padding-left: 6px; 16 | padding-top: 8px; 17 | padding-bottom: 10px; 18 | cursor: pointer; 19 | outline:none; 20 | 21 | &.active { 22 | color: $accent; 23 | } 24 | 25 | &:hover { 26 | color: $accent; 27 | background-color: #f2f2f2; 28 | } 29 | } 30 | 31 | .ngez-nav-list-content{ 32 | padding-left: 18px; 33 | 34 | & a{ 35 | display: flex; 36 | flex-direction: row; 37 | justify-content: space-between; 38 | border: none; 39 | background-color: transparent; 40 | padding: 6px; 41 | text-decoration: none; 42 | cursor: pointer; 43 | 44 | &.active { 45 | color: $accent; 46 | } 47 | 48 | &:hover { 49 | background-color: #f2f2f2; 50 | color: $accent; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /projects/io/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 18 | NgEz - Docs 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /projects/core/src/autocomplete/autocomplete-option.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | Output, 5 | EventEmitter, 6 | HostListener, 7 | HostBinding, 8 | ElementRef } from "@angular/core"; 9 | import { Highlightable } from "@angular/cdk/a11y"; 10 | 11 | @Component({ 12 | selector: 'ngez-autocomplete-option', 13 | templateUrl: './autocomplete-option.component.html', 14 | styleUrls: ['./autocomplete-option.component.scss'] 15 | }) 16 | export class NgEzAutocompleteOptionComponent implements Highlightable { 17 | 18 | @Input() value: any; 19 | 20 | @Input() disabled = false; 21 | 22 | @Output() selected = new EventEmitter(); 23 | 24 | active = false; 25 | 26 | constructor(private element: ElementRef){} 27 | 28 | setActiveStyles() { 29 | this.active = true; 30 | }; 31 | 32 | setInactiveStyles() { 33 | this.active = false; 34 | } 35 | 36 | @HostListener('click') 37 | onSelect() { 38 | if(!this.disabled) 39 | this.selected.emit(this); 40 | } 41 | 42 | getOffsetHeight(){ 43 | const element = this.element.nativeElement as HTMLElement; 44 | return element.getBoundingClientRect().height; 45 | } 46 | } -------------------------------------------------------------------------------- /projects/core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 4.0.0 (2019-01-29) 2 | ### Breaking Changes 3 | * **file-dropzone:** added "multiple" @input which determines whether the control's value will be File[] or simply File. 4 | # 3.0.0 (2019-01-29) 5 | ### Bug Fixes 6 | * **file-dropzone:** events fired on child elements no longer remove "active" class. 7 | 8 | ### Breaking Changes 9 | * **forms:** maxSize and fileType validators now return ValidationError if the value is a File, and ValidationError[] if the value is a FileList or File[]. 10 | 11 | # 2.0.0 (2019-01-26) 12 | ### Features 13 | * Added **file-dropzone** directive. 14 | ### Breaking Changes 15 | * **NgEzOutsideDirective** renamed to **NgEzOutsideClickDirective**. 16 | * **NgEzOutsideModule** renamed to **NgEzOutsideClickModule**. 17 | 18 | # 1.2.0 (2019-01-11) 19 | ### Features 20 | * Added **file-input** directive. 21 | * **forms**: added equals, url, fileType, maxSize and totalSize validators. 22 | 23 | # 1.1.0 (2019-01-02) 24 | ### Features 25 | * Added **lazy-renderer** component. 26 | * Added **in-viewport** directive. 27 | 28 | # 1.0.2 (2018-12-28) 29 | ### Bug Fixes 30 | * **code-prettify:** should not throw warning on init. 31 | ### Features 32 | * **autocomplete:** cursor changes to "not-allowed" on hover if option is disabled. -------------------------------------------------------------------------------- /projects/io/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import '~@angular/cdk/overlay-prebuilt.css'; 3 | @import '~bootstrap/scss/mixins'; 4 | @import '~bootstrap/scss/functions'; 5 | @import '~bootstrap/scss/variables'; 6 | @import '~bootstrap/scss/forms'; 7 | 8 | html, body{ 9 | margin: 0; 10 | height: 100%; 11 | font-family: sans-serif; 12 | font-size: 14px; 13 | color: #333; 14 | } 15 | 16 | p { 17 | text-align: justify; 18 | text-justify: inter-word; 19 | } 20 | 21 | a { 22 | color: #333; 23 | } 24 | 25 | .app-container{ 26 | height: 100%; 27 | } 28 | 29 | mat-toolbar.header { 30 | z-index: 10; 31 | box-shadow: 0 2px 5px 0 rgba(0,0,0,.3); 32 | } 33 | 34 | mat-toolbar.footer { 35 | background-color: red; 36 | } 37 | 38 | 39 | mat-sidenav.mat-sidenav { 40 | min-width: 260px; 41 | background-color: #fafafa; 42 | box-shadow: 6px 0 6px rgba(0,0,0,.1); 43 | } 44 | 45 | .sidenav-container{ 46 | min-height: 100%; 47 | // background-color: #F8F5F2; 48 | box-shadow: 6px 0 6px rgba(0,0,0,.1); 49 | // background-color: #09203f !important 50 | } 51 | 52 | .content-container{ 53 | padding: 24px; 54 | 55 | // overflow-y: auto; 56 | } 57 | -------------------------------------------------------------------------------- /projects/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngez/core", 3 | "version": "5.0.1", 4 | "description": "A collection of minimalistic, easy-to-use and fully customizable Angular components, directives and services", 5 | "keywords": [ 6 | "ngez", 7 | "angular", 8 | "core", 9 | "autocomplete", 10 | "clipboard", 11 | "code", 12 | "prettify", 13 | "code-prettify", 14 | "nested", 15 | "nav", 16 | "nested-nav", 17 | "outside", 18 | "click", 19 | "minimalistic", 20 | "in-viewport", 21 | "viewport", 22 | "lazy", 23 | "load", 24 | "lazy-renderer", 25 | "forms", 26 | "validators", 27 | "file", 28 | "file-input", 29 | "file-dropzone", 30 | "dropzone" 31 | ], 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/ngez/platform" 35 | }, 36 | "license": "MIT", 37 | "dependencies": { 38 | "@fortawesome/angular-fontawesome": "^0.3.0", 39 | "@fortawesome/fontawesome-svg-core": "^1.2.12", 40 | "@fortawesome/free-solid-svg-icons": "^5.6.3", 41 | "@fortawesome/free-regular-svg-icons": "^5.6.3" 42 | }, 43 | "peerDependencies": { 44 | "@angular/common": "^7.2.2", 45 | "@angular/core": "^7.2.2", 46 | "@angular/cdk": "^7.2.2" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /projects/core/src/utils/byte.ts: -------------------------------------------------------------------------------- 1 | import { NgEzByteUnit } from "./models"; 2 | 3 | export class NgEzByteUtils{ 4 | 5 | private static decimalUnits = [ 6 | 'kilobyte', 7 | 'megabyte', 8 | 'gigabyte', 9 | 'terabyte']; 10 | 11 | private static binaryUnits = [ 12 | 'kibibyte', 13 | 'mebibyte', 14 | 'gibibyte', 15 | 'tebibyte']; 16 | 17 | static convert(input: number, from: NgEzByteUnit, to?: NgEzByteUnit){ 18 | 19 | if(!input || (!from && !to)) 20 | return input; 21 | 22 | const fromBytes = from && from != 'byte' ? this.toBytes(from) : 1; 23 | 24 | const toBytes = to && to != 'byte' ? this.toBytes(to) : 1; 25 | 26 | return input * (fromBytes / toBytes); 27 | } 28 | 29 | private static toBytes(unit: NgEzByteUnit){ 30 | 31 | { 32 | const power = this.decimalUnits.findIndex(decimalUnit => decimalUnit == unit) + 1; 33 | if(power) 34 | return Math.pow(1000, power); 35 | } 36 | 37 | { 38 | const power = this.binaryUnits.findIndex(binaryUnit => binaryUnit == unit) + 1; 39 | if(power) 40 | return Math.pow(1024, power); 41 | } 42 | 43 | return 1; 44 | } 45 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/clipboard/clipboard.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | import { ClipboardPage } from "./clipboard.page"; 4 | import { FlexLayoutModule } from "@angular/flex-layout"; 5 | import { CommonModule } from "@angular/common"; 6 | import { NgEzCodePrettifyModule } from '@ngez/core'; 7 | import { MatInputModule } from '@angular/material/input'; 8 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 9 | import { NgEzClipboardModule } from '@ngez/core'; 10 | import { ReactiveFormsModule } from "@angular/forms"; 11 | import { MatButtonModule } from '@angular/material/button'; 12 | import { MatSnackBarModule } from '@angular/material/snack-bar'; 13 | 14 | const routes : Routes = [{ 15 | path: '', 16 | component: ClipboardPage 17 | }]; 18 | 19 | @NgModule({ 20 | imports: [ 21 | ReactiveFormsModule, 22 | MatSnackBarModule, 23 | MatButtonModule, 24 | MatInputModule, 25 | NgEzCodePrettifyModule, 26 | MatProgressSpinnerModule, 27 | FlexLayoutModule, 28 | RouterModule.forChild(routes), 29 | CommonModule, 30 | NgEzClipboardModule], 31 | declarations: [ClipboardPage] 32 | }) 33 | export class ClipboardModule{} -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/code-prettify/code-prettify.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig } from "@ngez/core"; 3 | 4 | @Component({ 5 | selector: 'code-prettify', 6 | templateUrl: './code-prettify.page.html' 7 | }) 8 | export class CodePrettifyPage { 9 | 10 | code1: NgEzCodePrettifyConfig = { 11 | language: 'javascript', 12 | path: 'assets/code-snippets/core/code-prettify/app.js', 13 | theme: 'dark', 14 | canCopy: true, 15 | header: 'app.js' 16 | }; 17 | 18 | code2: NgEzCodePrettifyConfig = { 19 | language: 'html', 20 | path: 'assets/code-snippets/core/code-prettify/app.component.html', 21 | theme: 'dark', 22 | canCopy: true, 23 | header: 'app.component.html' 24 | }; 25 | 26 | code3: NgEzCodePrettifyConfig = { 27 | language: 'typescript', 28 | path: 'assets/code-snippets/core/code-prettify/app.component.ts', 29 | theme: 'dark', 30 | canCopy: true, 31 | header: 'app.component.ts' 32 | }; 33 | 34 | code4: NgEzCodePrettifyConfig = { 35 | language: 'css', 36 | path: 'assets/code-snippets/core/code-prettify/custom.scss', 37 | theme: 'dark', 38 | canCopy: true, 39 | header: 'styles.scss' 40 | }; 41 | } -------------------------------------------------------------------------------- /projects/core/src/forms/file-type.validator.ts: -------------------------------------------------------------------------------- 1 | import { Directive, forwardRef, Input, SimpleChanges, OnInit, OnChanges } from "@angular/core"; 2 | import { NG_VALIDATORS, Validator, AbstractControl, ValidatorFn, ValidationErrors } from "@angular/forms"; 3 | import { NgEzValidators } from "./validators"; 4 | 5 | @Directive({ 6 | selector: '[ngezFileType][formControlName],[ngezFileType][formControl],[ngezFileType][ngModel]', 7 | providers: [{ 8 | provide: NG_VALIDATORS, 9 | useExisting: forwardRef(() => NgEzFileTypeValidator), 10 | multi: true 11 | }] 12 | }) 13 | export class NgEzFileTypeValidator implements Validator, OnInit, OnChanges{ 14 | 15 | @Input() accept: string; 16 | 17 | private validator: ValidatorFn; 18 | 19 | private onChange: Function; 20 | 21 | ngOnChanges(changes: SimpleChanges){ 22 | this.createValidator(); 23 | if(this.onChange) 24 | this.onChange(); 25 | } 26 | 27 | ngOnInit() { 28 | if(!this.validator) 29 | this.createValidator(); 30 | } 31 | 32 | validate(control: AbstractControl): ValidationErrors{ 33 | return this.validator ? this.validator(control) : null; 34 | } 35 | 36 | registerOnValidatorChange(fn){ 37 | this.onChange = fn; 38 | } 39 | 40 | private createValidator(){ 41 | this.validator = NgEzValidators.fileType(this.accept); 42 | } 43 | } -------------------------------------------------------------------------------- /projects/core/src/forms/max-size.validator.ts: -------------------------------------------------------------------------------- 1 | import { Directive, forwardRef, Input, SimpleChanges, OnInit, OnChanges } from "@angular/core"; 2 | import { NG_VALIDATORS, Validator, AbstractControl, ValidatorFn, ValidationErrors } from "@angular/forms"; 3 | import { NgEzValidators } from "./validators"; 4 | 5 | @Directive({ 6 | selector: '[ngezMaxSize][formControlName],[ngezMaxSize][formControl],[ngezMaxSize][ngModel]', 7 | providers: [{ 8 | provide: NG_VALIDATORS, 9 | useExisting: forwardRef(() => NgEzMaxSizeValidator), 10 | multi: true 11 | }] 12 | }) 13 | export class NgEzMaxSizeValidator implements Validator, OnInit, OnChanges{ 14 | 15 | @Input('ngezMaxSize') maxSize: number; 16 | 17 | private validator: ValidatorFn; 18 | 19 | private onChange: Function; 20 | 21 | ngOnChanges(changes: SimpleChanges){ 22 | this.createValidator(); 23 | if(this.onChange) 24 | this.onChange(); 25 | } 26 | 27 | ngOnInit() { 28 | if(!this.validator) 29 | this.createValidator(); 30 | } 31 | 32 | validate(control: AbstractControl): ValidationErrors{ 33 | return this.validator ? this.validator(control) : null; 34 | } 35 | 36 | registerOnValidatorChange(fn){ 37 | this.onChange = fn; 38 | } 39 | 40 | private createValidator(){ 41 | this.validator = NgEzValidators.maxSize(+this.maxSize); 42 | } 43 | } -------------------------------------------------------------------------------- /projects/core/src/forms/total-size.validator.ts: -------------------------------------------------------------------------------- 1 | import { Directive, forwardRef, Input, SimpleChanges, OnInit, OnChanges } from "@angular/core"; 2 | import { NG_VALIDATORS, Validator, AbstractControl, ValidatorFn, ValidationErrors } from "@angular/forms"; 3 | import { NgEzValidators } from "./validators"; 4 | 5 | @Directive({ 6 | selector: '[ngezTotalSize][formControlName],[ngezTotalSize][formControl],[ngezTotalSize][ngModel]', 7 | providers: [{ 8 | provide: NG_VALIDATORS, 9 | useExisting: forwardRef(() => NgEzTotalSizeValidator), 10 | multi: true 11 | }] 12 | }) 13 | export class NgEzTotalSizeValidator implements Validator, OnInit, OnChanges{ 14 | 15 | @Input('ngezTotalSize') totalSize: number; 16 | 17 | private validator: ValidatorFn; 18 | 19 | private onChange: Function; 20 | 21 | ngOnChanges(changes: SimpleChanges){ 22 | this.createValidator(); 23 | if(this.onChange) 24 | this.onChange(); 25 | } 26 | 27 | ngOnInit() { 28 | if(!this.validator) 29 | this.createValidator(); 30 | } 31 | 32 | validate(control: AbstractControl): ValidationErrors{ 33 | return this.validator ? this.validator(control) : null; 34 | } 35 | 36 | registerOnValidatorChange(fn){ 37 | this.onChange = fn; 38 | } 39 | 40 | private createValidator(){ 41 | this.validator = NgEzValidators.totalSize(+this.totalSize); 42 | } 43 | } -------------------------------------------------------------------------------- /projects/io/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { AppComponent } from './app.component'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { MatToolbarModule } from '@angular/material/toolbar'; 6 | import { MatSidenavModule } from '@angular/material/sidenav'; 7 | import { FlexLayoutModule } from '@angular/flex-layout'; 8 | import { MatButtonModule } from '@angular/material/button'; 9 | import { MatIconModule } from '@angular/material/icon'; 10 | import { AppRoutingModule } from './app.routing'; 11 | import { MatListModule } from '@angular/material/list'; 12 | import { NgEzNestedNavModule } from '@ngez/core'; 13 | import { HttpClientModule } from '@angular/common/http'; 14 | import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; 15 | import { MatDividerModule } from '@angular/material'; 16 | 17 | @NgModule({ 18 | declarations: [ 19 | AppComponent 20 | ], 21 | imports: [ 22 | BrowserModule, 23 | BrowserAnimationsModule, 24 | HttpClientModule, 25 | MatToolbarModule, 26 | MatSidenavModule, 27 | FlexLayoutModule, 28 | MatButtonModule, 29 | MatIconModule, 30 | MatListModule, 31 | AppRoutingModule, 32 | NgEzNestedNavModule, 33 | MatDividerModule, 34 | FontAwesomeModule 35 | ], 36 | providers: [], 37 | bootstrap: [AppComponent] 38 | }) 39 | export class AppModule { } 40 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/clipboard/clipboard.page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzCodePrettifyConfig, NgEzClipboardService } from "@ngez/core"; 3 | import { FormBuilder, FormGroup } from "@angular/forms"; 4 | import { MatSnackBar } from '@angular/material'; 5 | 6 | @Component({ 7 | selector: 'clipboard', 8 | templateUrl: './clipboard.page.html' 9 | }) 10 | export class ClipboardPage { 11 | 12 | config1: NgEzCodePrettifyConfig = { 13 | language: 'html', 14 | path: 'assets/code-snippets/core/clipboard/app.component.html', 15 | theme: 'dark', 16 | canCopy: true, 17 | header: 'app.component.html' 18 | }; 19 | 20 | config2: NgEzCodePrettifyConfig = { 21 | language: 'typescript', 22 | path: 'assets/code-snippets/core/clipboard/app.component.ts', 23 | theme: 'dark', 24 | canCopy: true, 25 | header: 'app.component.ts' 26 | }; 27 | 28 | form: FormGroup; 29 | 30 | constructor(private clipboard: NgEzClipboardService, private snackbar: MatSnackBar, formBuilder: FormBuilder) { 31 | this.form = formBuilder.group({ 32 | text: '' 33 | }); 34 | } 35 | 36 | onCopy() { 37 | const text = this.form.value.text; 38 | if (this.clipboard.copy(text)) 39 | this.snackbar.open(`Copied: ${text}`, null, { duration: 2000 }); 40 | else 41 | this.snackbar.open('Something went wrong while copying', null, { duration: 2000 }); 42 | } 43 | } -------------------------------------------------------------------------------- /projects/core/src/forms/equals.validator.spec.ts: -------------------------------------------------------------------------------- 1 | import { NgEzEqualsValidator } from "./equals.validator"; 2 | import { ComponentFixture, TestBed } from "@angular/core/testing"; 3 | import { FormGroup, FormBuilder } from "@angular/forms"; 4 | 5 | describe('EqualsValidator', () => { 6 | 7 | const formBuilder = new FormBuilder(); 8 | let directive: NgEzEqualsValidator; 9 | 10 | beforeEach(() => { 11 | directive = new NgEzEqualsValidator(); 12 | }); 13 | 14 | it('should return null if not passed the controls or if both controls are equal', () => { 15 | const form = formBuilder.group({ 16 | password: 'passw0rd', 17 | confirmPassword: 'passw0rd' 18 | }); 19 | 20 | expect(directive.validate(form)).toBeNull(); 21 | 22 | directive.c1 = 'password'; 23 | 24 | directive.ngOnChanges(null); 25 | 26 | expect(directive.validate(form)).toBeNull(); 27 | 28 | directive.c2 = 'confirmPassword'; 29 | 30 | directive.ngOnChanges(null); 31 | 32 | expect(directive.validate(form)).toBeNull(); 33 | }); 34 | 35 | it('should return an error object if the controls are not equal', () => { 36 | const form = formBuilder.group({ 37 | password: 'passw0rd', 38 | confirmPassword: 'password' 39 | }); 40 | 41 | directive.c1 = 'password'; 42 | 43 | directive.c2 = 'confirmPassword'; 44 | 45 | directive.ngOnChanges(null); 46 | 47 | expect(directive.validate(form)).toBeTruthy(); 48 | }); 49 | }); -------------------------------------------------------------------------------- /projects/io/src/app/core/core.routing.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | 4 | const routes: Routes = [{ 5 | path: '', 6 | loadChildren: './pages/overview/core-overview.module#CoreOverviewModule' 7 | }, { 8 | path: 'autocomplete', 9 | loadChildren: './pages/autocomplete/autocomplete.module#AutocompleteModule' 10 | }, { 11 | path: 'nested-nav', 12 | loadChildren: './pages/nested-nav/nested-nav.module#NestedNavModule' 13 | }, { 14 | path: 'code-prettify', 15 | loadChildren: './pages/code-prettify/code-prettify.module#CodePrettifyModule' 16 | }, { 17 | path: 'outside-click', 18 | loadChildren: './pages/outside-click/outside-click.module#OutsideClickModule' 19 | }, { 20 | path: 'clipboard', 21 | loadChildren: './pages/clipboard/clipboard.module#ClipboardModule' 22 | }, { 23 | path: 'lazy-renderer', 24 | loadChildren: './pages/lazy-renderer/lazy-renderer.module#LazyRendererModule' 25 | }, { 26 | path: 'in-viewport', 27 | loadChildren: './pages/in-viewport/in-viewport.module#InViewportModule' 28 | }, { 29 | path: 'file-input', 30 | loadChildren: './pages/file-input/file-input.module#FileInputModule' 31 | }, { 32 | path: 'forms', 33 | loadChildren: './pages/forms/forms.module#FormsModule' 34 | }, { 35 | path: 'file-dropzone', 36 | loadChildren: './pages/file-dropzone/file-dropzone.module#FileDropzoneModule' 37 | }]; 38 | 39 | @NgModule({ 40 | imports: [RouterModule.forChild(routes)] 41 | }) 42 | export class CoreRoutingModule{} -------------------------------------------------------------------------------- /projects/core/src/forms/equals.validator.ts: -------------------------------------------------------------------------------- 1 | import { Directive, forwardRef, Input, OnChanges, SimpleChanges, OnInit } from "@angular/core"; 2 | import { Validator, AbstractControl, NG_VALIDATORS, ValidatorFn, Validators, ValidationErrors } from "@angular/forms"; 3 | import { NgEzValidators } from "./validators"; 4 | 5 | @Directive({ 6 | selector: '[ngezEquals][formGroupName], [ngezEquals][formGroup], form[ngezEquals], [ngezEquals][ngModelGroup]', 7 | providers: [{ 8 | provide: NG_VALIDATORS, 9 | useExisting: forwardRef(() => NgEzEqualsValidator), 10 | multi: true 11 | }], 12 | host: { 13 | '[attr.c1]': 'c1 ? c1 : null', 14 | '[attr.c2]': 'c2 ? c2 : null' 15 | } 16 | }) 17 | export class NgEzEqualsValidator implements Validator, OnChanges, OnInit{ 18 | 19 | @Input() c1: string; 20 | 21 | @Input() c2: string; 22 | 23 | private validator: ValidatorFn; 24 | 25 | private onChange: Function; 26 | 27 | ngOnChanges(changes: SimpleChanges){ 28 | this.createValidator(); 29 | if(this.onChange) 30 | this.onChange(); 31 | } 32 | 33 | ngOnInit() { 34 | if(!this.validator) 35 | this.createValidator(); 36 | } 37 | 38 | validate(control: AbstractControl): ValidationErrors{ 39 | return this.validator ? this.validator(control) : null; 40 | } 41 | 42 | registerOnValidatorChange(fn){ 43 | this.onChange = fn; 44 | } 45 | 46 | private createValidator(){ 47 | this.validator = NgEzValidators.equals(this.c1, this.c2); 48 | } 49 | } -------------------------------------------------------------------------------- /projects/core/src/forms/url.validator.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from "@angular/core/testing"; 2 | import { FormControl } from "@angular/forms"; 3 | import { NgEzUrlValidator } from "./url.validator"; 4 | 5 | describe('UrlValidator', () => { 6 | 7 | let directive: NgEzUrlValidator; 8 | 9 | beforeEach(() => { 10 | directive = new NgEzUrlValidator(); 11 | }); 12 | 13 | it('should return null if the url is valid', () => { 14 | expect(directive.validate(new FormControl('ftp://www.google.com'))).toBeNull(); 15 | expect(directive.validate(new FormControl('http://www.google.com'))).toBeNull(); 16 | expect(directive.validate(new FormControl('https://www.google.com'))).toBeNull(); 17 | expect(directive.validate(new FormControl('https://www.google.com/search?q=angular'))).toBeNull(); 18 | }); 19 | 20 | it('should return an error object if the url is invalid.', () => { 21 | expect(directive.validate(new FormControl('invalid-url'))).toBeTruthy(); 22 | expect(directive.validate(new FormControl('www.google.com'))).toBeTruthy(); 23 | expect(directive.validate(new FormControl('http//www.google.com'))).toBeTruthy(); 24 | expect(directive.validate(new FormControl('http:/www.google.com'))).toBeTruthy(); 25 | expect(directive.validate(new FormControl('http:///www.google.com'))).toBeTruthy(); 26 | expect(directive.validate(new FormControl('htt://www.google.com'))).toBeTruthy(); 27 | expect(directive.validate(new FormControl('http:///www.google'))).toBeTruthy(); 28 | }); 29 | }); -------------------------------------------------------------------------------- /projects/core/src/utils/byte.spec.ts: -------------------------------------------------------------------------------- 1 | import { NgEzByteUtils } from "./byte"; 2 | 3 | describe('NgEzByteUtils', () => { 4 | describe('convert()', () => { 5 | it('should return the same input if "to" and "from" params are undefined', () => { 6 | expect(NgEzByteUtils.convert(1000, null)).toBe(1000); 7 | expect(NgEzByteUtils.convert(1000, null, null)).toBe(1000); 8 | expect(NgEzByteUtils.convert(0, null, null)).toBe(0); 9 | }); 10 | 11 | it('should return the input converted from the specified unit', () => { 12 | expect(NgEzByteUtils.convert(1, 'kilobyte')).toBe(1000); 13 | expect(NgEzByteUtils.convert(1.15, 'megabyte')).toBe(1150000); 14 | expect(NgEzByteUtils.convert(8.5, 'gigabyte')).toBe(8500000000); 15 | expect(NgEzByteUtils.convert(1, 'kibibyte')).toBe(1024); 16 | expect(NgEzByteUtils.convert(2, 'kibibyte')).toBe(2048); 17 | }); 18 | 19 | it('should return the input converted from a specified unit to a specified unit', () => { 20 | expect(NgEzByteUtils.convert(1, 'kilobyte', 'byte')).toBe(1000); 21 | expect(NgEzByteUtils.convert(1000, 'kilobyte', 'megabyte')).toBe(1); 22 | expect(NgEzByteUtils.convert(1, 'megabyte', 'mebibyte')).toBe(0.95367431640625); 23 | expect(NgEzByteUtils.convert(1000000, null, 'megabyte')).toBe(1); 24 | expect(NgEzByteUtils.convert(1000000, 'byte', 'megabyte')).toBe(1); 25 | expect(NgEzByteUtils.convert(1, 'gibibyte', 'mebibyte')).toBe(1024); 26 | }); 27 | }); 28 | }) -------------------------------------------------------------------------------- /projects/core/src/outside-click/outside-click.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Optional, Inject, Output, EventEmitter, OnInit, OnDestroy, PLATFORM_ID } from "@angular/core"; 2 | import { fromEvent, Subscription } from "rxjs"; 3 | import { DOCUMENT, isPlatformBrowser } from "@angular/common"; 4 | import { filter } from "rxjs/operators"; 5 | 6 | @Directive({ 7 | selector: '[ngezOutsideClick]' 8 | }) 9 | export class NgEzOutsideClickDirective implements OnInit, OnDestroy { 10 | 11 | @Output('ngezOutsideClick') outsideClick = new EventEmitter(); 12 | 13 | private subscription: Subscription; 14 | 15 | constructor( 16 | private element: ElementRef, 17 | @Optional() @Inject(DOCUMENT) private document: any, 18 | @Inject(PLATFORM_ID) private platformId: Object) { } 19 | 20 | ngOnInit() { 21 | if(!isPlatformBrowser(this.platformId)) return; 22 | 23 | setTimeout(() => { 24 | this.subscription = fromEvent(this.document, 'click') 25 | .pipe( 26 | filter(event => { 27 | const clickTarget = event.target as HTMLElement; 28 | return !this.isOrContainsClickTarget(this.element.nativeElement, clickTarget); 29 | }) 30 | ) 31 | .subscribe(event => this.outsideClick.emit(event)); 32 | }, 0); 33 | } 34 | 35 | ngOnDestroy() { 36 | if(this.subscription) 37 | this.subscription.unsubscribe(); 38 | } 39 | 40 | private isOrContainsClickTarget(element: HTMLElement, clickTarget: HTMLElement){ 41 | return element == clickTarget || element.contains(clickTarget); 42 | } 43 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/forms/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { NgEzByteUtils, NgEzValidators } from "@ngez/core"; 3 | import { FormBuilder, FormGroup } from "@angular/forms"; 4 | 5 | @Component({ 6 | selector: 'app', 7 | templateUrl: './app.component.html' 8 | }) 9 | export class AppComponent { 10 | 11 | form: FormGroup; 12 | 13 | constructor(fb: FormBuilder){ 14 | this.form = fb.group({ 15 | password: '', 16 | confirmPassword: '', 17 | url: ['', NgEzValidators.url], 18 | file: [null, [ 19 | //The accept attribute on a file input is useful to narrow down results for the user 20 | //but it's still quite easy to select files with a different extension, so add this client-side validation 21 | //This only checks the mime type and extension. Anyone could simply change the file extension and then pick that file. 22 | //Do not rely solely on this, you should still implement server-side validation. 23 | NgEzValidators.fileType('.jpg, .txt, audio/mp3, video/*'), 24 | //Checks that none of the files exceed the limit 25 | //Expects the amount in bytes, but you can use our NgEzByteUtils class to quickly make the conversion for you 26 | NgEzValidators.maxSize(NgEzByteUtils.convert(1, 'megabyte')), 27 | //Checks that the sum of all file sizes does not exceed the limit 28 | NgEzValidators.totalSize(NgEzByteUtils.convert(10, 'megabyte')) 29 | ]] 30 | }, {validators: NgEzValidators.equals('password', 'confirmPassword')}); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/autocomplete/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, AfterViewInit, ViewChild } from "@angular/core"; 2 | import { FormGroup, FormBuilder } from "@angular/forms"; 3 | import { NgEzAutocompleteDirective, NgEzAutocompleteConfig } from "@ngez/core"; 4 | 5 | @Component({ 6 | selector: 'app', 7 | templateUrl: './app.component.html' 8 | }) 9 | export class AppComponent implements AfterViewInit{ 10 | 11 | form: FormGroup; 12 | 13 | config: NgEzAutocompleteConfig = { 14 | //The user is an object, so we can provide a function to display the selected option in the input. 15 | labelExtractor: option => option.username 16 | } 17 | 18 | @ViewChild(NgEzAutocompleteDirective) autocomplete: NgEzAutocompleteDirective; 19 | 20 | users = []; 21 | 22 | constructor(formBuilder: FormBuilder){ 23 | this.form = formBuilder.group({ 24 | user: null 25 | }); 26 | 27 | for(let i = 0; i < 10; i++){ 28 | const user = { 29 | id: i, 30 | username: `user${i}` 31 | } 32 | this.users.push(user); 33 | } 34 | } 35 | 36 | ngAfterViewInit() { 37 | //Assuming you need the actual input text 38 | this.autocomplete.text$.subscribe(text => { 39 | //Perform custom filtering or http call. 40 | console.log(text) 41 | }) 42 | } 43 | 44 | //In case you need to programmatically open or close the panel. 45 | 46 | onOpen() { 47 | this.autocomplete.open(); 48 | } 49 | 50 | onClose() { 51 | this.autocomplete.close(); 52 | } 53 | 54 | onToggle() { 55 | this.autocomplete.toggle(); 56 | } 57 | } -------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/file-input/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from "@angular/core"; 2 | import { NgEzByteUtils, NgEzValidators, NgEzFileInputDirective } from "@ngez/core"; 3 | import { FormBuilder, FormGroup } from "@angular/forms"; 4 | 5 | @Component({ 6 | selector: 'app', 7 | templateUrl: './app.component.html' 8 | }) 9 | export class AppComponent { 10 | 11 | @ViewChild(NgEzFileInputDirective) fileInput: NgEzFileInputDirective; 12 | 13 | form: FormGroup; 14 | 15 | constructor(fb: FormBuilder){ 16 | this.form = fb.group({ 17 | file: [null, [ 18 | //The accept attribute on a file input is useful to narrow down results for the user 19 | //but it's still quite easy to select files with a different extension, so add this client-side validation 20 | //This only checks the mime type and extension. Anyone could simply change the file extension and then pick that file. 21 | //Do not rely solely on this, you should still implement server-side validation. 22 | NgEzValidators.fileType('.jpg, .txt, audio/mp3, video/*'), 23 | //Checks that none of the files exceed the limit 24 | //Expects the amount in bytes, but you can use our NgEzByteUtils class to quickly make the conversion for you 25 | NgEzValidators.maxSize(NgEzByteUtils.convert(1, 'megabyte')), 26 | //Checks that the sum of all file sizes does not exceed the limit 27 | NgEzValidators.totalSize(NgEzByteUtils.convert(10, 'megabyte')) 28 | ]] 29 | }); 30 | } 31 | 32 | //Programtically open file browser 33 | onBrowse(){ 34 | this.fileInput.browse(); 35 | } 36 | } -------------------------------------------------------------------------------- /projects/core/src/window/window.service.ts: -------------------------------------------------------------------------------- 1 | import { isPlatformBrowser } from "@angular/common"; 2 | import { ClassProvider, FactoryProvider, InjectionToken, PLATFORM_ID } from '@angular/core'; 3 | 4 | /* Create a new injection token for injecting the window into a component. */ 5 | export const WINDOW = new InjectionToken('WindowToken'); 6 | 7 | /* Define abstract class for obtaining reference to the global window object. */ 8 | export abstract class WindowRef { 9 | 10 | get nativeWindow(): Window | Object { 11 | throw new Error('Not implemented.'); 12 | } 13 | 14 | } 15 | 16 | /* Define class that implements the abstract class and returns the native window object. */ 17 | export class BrowserWindowRef extends WindowRef { 18 | 19 | constructor() { 20 | super(); 21 | } 22 | 23 | get nativeWindow(): Window | Object { 24 | return window; 25 | } 26 | 27 | } 28 | 29 | /* Create an factory function that returns the native window object. */ 30 | export function windowFactory(browserWindowRef: BrowserWindowRef, platformId: Object): Window | Object { 31 | if (isPlatformBrowser(platformId)) { 32 | return browserWindowRef.nativeWindow; 33 | } 34 | return new Object(); 35 | } 36 | 37 | /* Create a injectable provider for the WindowRef token that uses the BrowserWindowRef class. */ 38 | const browserWindowProvider: ClassProvider = { 39 | provide: WindowRef, 40 | useClass: BrowserWindowRef 41 | }; 42 | 43 | /* Create an injectable provider that uses the windowFactory function for returning the native window object. */ 44 | const windowProvider: FactoryProvider = { 45 | provide: WINDOW, 46 | useFactory: windowFactory, 47 | deps: [ WindowRef, PLATFORM_ID ] 48 | }; 49 | 50 | /* Create an array of providers. */ 51 | export const WINDOW_PROVIDERS = [ 52 | browserWindowProvider, 53 | windowProvider 54 | ]; -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/file-dropzone/file-dropzone.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core'; 2 | import { FormBuilder, FormGroup, FormGroupDirective } from '@angular/forms'; 3 | import { NgEzCodePrettifyConfig, NgEzFileDropzoneDirective, NgEzFileDropzoneEvent } from '@ngez/core'; 4 | 5 | @Component({ 6 | selector: "file-dropzone", 7 | templateUrl: "./file-dropzone.page.html", 8 | styleUrls: ["./file-dropzone.page.scss"] 9 | }) 10 | export class FileDropzonePage { 11 | @ViewChild(FormGroupDirective) ngForm: FormGroupDirective; 12 | 13 | @ViewChild(NgEzFileDropzoneDirective) dropzone: NgEzFileDropzoneDirective; 14 | 15 | form: FormGroup; 16 | 17 | code1: NgEzCodePrettifyConfig = { 18 | language: "html", 19 | path: "assets/code-snippets/core/file-dropzone/app.component.html", 20 | theme: "dark", 21 | canCopy: true, 22 | header: "app.component.html" 23 | }; 24 | 25 | code2: NgEzCodePrettifyConfig = { 26 | language: "typescript", 27 | path: "assets/code-snippets/core/file-dropzone/app.component.scss", 28 | theme: "dark", 29 | canCopy: true, 30 | header: "app.component.scss" 31 | }; 32 | 33 | code3: NgEzCodePrettifyConfig = { 34 | language: "typescript", 35 | path: "assets/code-snippets/core/file-dropzone/app.component.ts", 36 | theme: "dark", 37 | canCopy: true, 38 | header: "app.component.ts" 39 | }; 40 | 41 | constructor(fb: FormBuilder) { 42 | this.form = fb.group({ 43 | files: [] 44 | }); 45 | } 46 | 47 | onRemove(file: File) { 48 | const control = this.form.get("files"); 49 | control.setValue((control.value as File[]).filter(f => file != f)); 50 | } 51 | 52 | onBrowse() { 53 | this.dropzone.browse(); 54 | } 55 | 56 | onSelected(selection: NgEzFileDropzoneEvent) { 57 | console.log(selection); 58 | } 59 | 60 | onReset() { 61 | this.ngForm.resetForm(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /projects/io/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild, ChangeDetectorRef } from '@angular/core'; 2 | import { debounceTime, map, tap, filter, switchMap } from 'rxjs/operators'; 3 | import { FormGroup, FormBuilder } from '@angular/forms'; 4 | import { MediaObserver } from '@angular/flex-layout'; 5 | import { Observable } from 'rxjs'; 6 | import { MatSidenav } from '@angular/material/sidenav'; 7 | import { Router, NavigationEnd } from '@angular/router'; 8 | import { MatIconRegistry } from '@angular/material'; 9 | import { faGithub } from '@fortawesome/free-brands-svg-icons'; 10 | import { DomSanitizer } from '@angular/platform-browser'; 11 | 12 | @Component({ 13 | selector: 'app-root', 14 | templateUrl: './app.component.html', 15 | styleUrls: ['./app.component.scss'] 16 | }) 17 | export class AppComponent { 18 | 19 | @ViewChild(MatSidenav) snav: MatSidenav; 20 | 21 | mode$: Observable<'side' | 'over'>; 22 | 23 | routerLinkOptions = { 24 | exact: true 25 | }; 26 | 27 | constructor(public observer: MediaObserver, private router: Router, private sanitizer: DomSanitizer, iconRegistry: MatIconRegistry) { 28 | const { icon, iconName, prefix } = faGithub; 29 | const svg = ``; 30 | iconRegistry.addSvgIconLiteralInNamespace(prefix, iconName, sanitizer.bypassSecurityTrustHtml(svg)) 31 | } 32 | 33 | ngOnInit() { 34 | this.mode$ = this.observer.media$.pipe( 35 | map(() => this.observer.isActive('gt-sm') ? 'side' : 'over') 36 | ); 37 | 38 | // this.router.events 39 | // .pipe(filter(e => e instanceof NavigationEnd)) 40 | // .subscribe(e => { 41 | // const isLarge = this.media.isActive('gt-sm'); 42 | // if (isLarge && !this.snav.opened) 43 | // this.snav.open(); 44 | // if (!isLarge && this.snav.opened) 45 | // this.snav.close(); 46 | // }); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/overview/core-overview.page.html: -------------------------------------------------------------------------------- 1 |

Core

2 |

3 | @ngez/core is a library for Angular applications, which consists mostly of directives and other utilities, 4 | although our few components can still easily be overridden with new styles. This way you're not constrained 5 | by the theme of a component framework. Instead you can focus on how the view should look with your own styles, and we'll take care of the functionality. 6 |

7 |

8 | Tested on Chrome, Firefox and IE11. 9 |

10 |

Installation

11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 | Something went wrong while loading the code. 20 | 21 |
22 |
23 |
24 |

25 | Core depends on the @angular/cdk, so install it if you haven't already: 26 |

27 | 28 | 29 |
30 | 31 |
32 |
33 | 34 |
35 | Something went wrong while loading the code. 36 | 37 |
38 |
39 |
-------------------------------------------------------------------------------- /projects/core/src/forms/file-type.validator.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from "@angular/core/testing"; 2 | import { NgEzFileTypeValidator } from "./file-type.validator"; 3 | import { FormControl } from "@angular/forms"; 4 | 5 | describe('FileTypeValidator', () => { 6 | 7 | let directive: NgEzFileTypeValidator; 8 | 9 | beforeEach(() => { 10 | directive = new NgEzFileTypeValidator(); 11 | }); 12 | 13 | it('should return null if the "accept" input is undefined', () => { 14 | 15 | const file = new File(["blob"], "song.mp3", { type: 'audio/mp3' }); 16 | 17 | expect(directive.validate(new FormControl(file))).toBeNull(); 18 | }); 19 | 20 | it('should return null if the file type is valid', () => { 21 | const f1 = new File(["blob"], "song.mp3", { type: 'audio/mp3' }); 22 | const f2 = new File(["blob"], "99872_n.jpg", { type: 'image/jpeg' }); 23 | const f3 = new File(["blob"], "asdf.txt", { type: 'text/plain' }); 24 | 25 | directive.accept = 'audio/mp3, .txt, image/*'; 26 | 27 | directive.ngOnChanges(null); 28 | 29 | expect(directive.validate(new FormControl(f1))).toBeNull(); 30 | expect(directive.validate(new FormControl(f2))).toBeNull(); 31 | expect(directive.validate(new FormControl(f3))).toBeNull(); 32 | }); 33 | 34 | it('should return an error object if any of the file types are invalid', () => { 35 | const f1 = new File(["blob"], "song.mp3", { type: 'audio/mp3' }); 36 | const f2 = new File(["blob"], "99872_n.jpg", { type: 'image/jpeg' }); 37 | const f3 = new File(["blob"], "asdf.txt", { type: 'text/plain' }); 38 | 39 | directive.accept = 'audio/mp3, image/*'; 40 | 41 | directive.ngOnChanges(null); 42 | 43 | expect(directive.validate(new FormControl(f3))).toBeTruthy(); 44 | expect(directive.validate(new FormControl([f2, f3]))).toBeTruthy(); 45 | expect(directive.validate(new FormControl([f1, f3]))).toBeTruthy(); 46 | }); 47 | }); -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/in-viewport/in-viewport.page.html: -------------------------------------------------------------------------------- 1 |

In Viewport

2 |

3 | Receive events indicating whether the element is currently in the viewport or not. 4 |

5 |

Demo

6 |

7 | Open your devtools and scroll down. Notice the logged events when the element enters/leaves the viewport. 8 |

9 |
10 |
Some content
11 |

Import

12 |

13 | Begin by importing the module like this: 14 |

15 |
16 |     import {{'{'}} NgEzInViewportModule {{'}'}} from '@ngez/core';
17 | 
18 |

Usage

19 | 20 | 21 |
22 | 23 |
24 |
25 | 26 |
27 | Something went wrong while loading the code. 28 | 29 |
30 |
31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |
39 | 40 |
41 | Something went wrong while loading the code. 42 | 43 |
44 |
45 |
-------------------------------------------------------------------------------- /projects/io/src/app/core/pages/forms/forms.page.html: -------------------------------------------------------------------------------- 1 |

Forms

2 |

3 | Custom validators for your forms. Supports FormsModule and ReactiveFormsModule. 4 |

5 |

Validators

6 |
    7 |
  • equals: The value of two form controls must be equal.
  • 8 |
  • url: The value must be a valid url.
  • 9 |
  • fileType: The file's type (mime type or extension) must be valid.
  • 10 |
  • maxSize: The file's size must be valid.
  • 11 |
  • totalSize: The sum of all file sizes must be valid.
  • 12 |
13 |

Import (ignore for ReactiveFormsModule)

14 |

Import the module like this:

15 |
16 |     import {{'{'}} NgEzFormsModule {{'}'}} from '@ngez/core';
17 | 
18 |

Usage

19 | 20 | 21 |
22 | 23 |
24 |
25 | 26 |
27 | Something went wrong while loading the code. 28 | 29 |
30 |
31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |
39 | 40 |
41 | Something went wrong while loading the code. 42 | 43 |
44 |
45 |
-------------------------------------------------------------------------------- /projects/io/src/app/core/pages/outside-click/outside-click.page.html: -------------------------------------------------------------------------------- 1 |

Outside Click

2 |

3 | Receive an event whenever a click outside of the specified element occurs. 4 |

5 |

Demo

6 |
7 |
8 | Click outside 9 | Clicks: {{clicks}} 10 |
11 |
12 |

Import

13 |

14 | Begin by importing the module like this: 15 |

16 |
17 |     import {{'{'}} NgEzOutsideClickModule {{'}'}} from '@ngez/core';
18 | 
19 |

Usage

20 | 21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 | Something went wrong while loading the code. 29 | 30 |
31 |
32 |
33 |
34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 |
42 | Something went wrong while loading the code. 43 | 44 |
45 |
46 |
47 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/clipboard/clipboard.page.html: -------------------------------------------------------------------------------- 1 |

Clipboard

2 |

3 | Copy any string to the clipboard. 4 |

5 |

Demo

6 |
7 | 8 | 9 | 10 | 11 |
12 |

Import

13 |

Begin by importing the module like this:

14 |
15 |     import {{'{'}} NgEzClipboardModule {{'}'}} from '@ngez/core';
16 | 
17 |

Usage

18 | You can place the Directive on a button and upon click, the payload will be copied to clipboard. Or you can directly use the Service: 19 |

20 | 21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 | Something went wrong while loading the code. 29 | 30 |
31 |
32 |
33 |
34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 |
42 | Something went wrong while loading the code. 43 | 44 |
45 |
46 |
-------------------------------------------------------------------------------- /projects/io/src/app/core/pages/file-input/file-input.page.html: -------------------------------------------------------------------------------- 1 |

File Input

2 |

3 | Turn any element into a File Input. The control's value will be the actual File or File[]. 4 |

5 |

Features

6 |
    7 |
  • Works with FormsModule and ReactiveFormsModule.
  • 8 |
  • Same options as regular file input. Accept only certain file types and select multiple files.
  • 9 |
10 |

Demo

11 |
12 | 13 |


14 | 15 |
16 |

Import

17 |

Import the module like this:

18 |
19 |     import {{'{'}} NgEzFileModule {{'}'}} from '@ngez/core';
20 | 
21 |

Usage

22 | 23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 | Something went wrong while loading the code. 31 | 32 |
33 |
34 |
35 |
36 | 37 | 38 |
39 | 40 |
41 |
42 | 43 |
44 | Something went wrong while loading the code. 45 | 46 |
47 |
48 |
-------------------------------------------------------------------------------- /projects/io/src/assets/code-snippets/core/code-prettify/custom.scss: -------------------------------------------------------------------------------- 1 | .custom-theme { 2 | 3 | .ngez-copy-button:hover{ 4 | color: #000b29; 5 | } 6 | 7 | .ngez-code-prettify-header{ 8 | background-color: #000b29; 9 | color: white; 10 | } 11 | 12 | .ngez-code-prettify-content{ 13 | background-color: rgba(241,241,241,.2); 14 | } 15 | 16 | .ngez-copy-button{ 17 | color: rgb(30, 30, 30); 18 | } 19 | 20 | .ngez-code-prettify{ 21 | background-color: rgba(241,241,241,.2); 22 | 23 | /* string content */ 24 | 25 | .str { 26 | color: #183691; 27 | } 28 | 29 | /* keyword */ 30 | 31 | .kwd { 32 | color: #a71d5d; 33 | } 34 | 35 | /* comment */ 36 | 37 | .com { 38 | color: #969896; 39 | } 40 | 41 | /* type name */ 42 | 43 | .typ { 44 | color: #0086b3; 45 | } 46 | 47 | /* literal value */ 48 | 49 | .lit { 50 | color: #0086b3; 51 | } 52 | 53 | /* punctuation */ 54 | 55 | .pun { 56 | color: #333; 57 | } 58 | 59 | /* lisp open bracket */ 60 | 61 | .opn { 62 | color: #333; 63 | } 64 | 65 | /* lisp close bracket */ 66 | 67 | .clo { 68 | color: #333; 69 | } 70 | 71 | /* markup tag name */ 72 | 73 | .tag { 74 | color: #000080; 75 | } 76 | 77 | /* markup attribute name */ 78 | 79 | .atn { 80 | color: #795da3; 81 | } 82 | 83 | /* markup attribute value */ 84 | 85 | .atv { 86 | color: #183691; 87 | } 88 | 89 | /* declaration */ 90 | 91 | .dec { 92 | color: #333; 93 | } 94 | 95 | /* variable name */ 96 | 97 | .var { 98 | color: #008080; 99 | } 100 | 101 | /* function name */ 102 | 103 | .fun { 104 | color: #900; 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/autocomplete/autocomplete.page.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from "@angular/core"; 2 | import * as faker from 'faker'; 3 | import { FormBuilder, FormGroup } from "@angular/forms"; 4 | import { NgEzAutocompleteConfig, NgEzCodePrettifyConfig, NgEzAutocompleteDirective } from "@ngez/core"; 5 | 6 | @Component({ 7 | selector: 'autocomplete', 8 | templateUrl: './autocomplete.page.html' 9 | }) 10 | export class AutocompletePage { 11 | 12 | @ViewChild(NgEzAutocompleteDirective) autocomplete: NgEzAutocompleteDirective; 13 | 14 | form: FormGroup; 15 | 16 | config: NgEzAutocompleteConfig = { 17 | labelExtractor: option => option.username 18 | } 19 | 20 | options1; 21 | 22 | code1: NgEzCodePrettifyConfig = { 23 | language: 'scss', 24 | path: 'assets/code-snippets/core/autocomplete/styles.scss', 25 | theme: 'dark', 26 | canCopy: true, 27 | header: 'styles.scss' 28 | }; 29 | 30 | code2: NgEzCodePrettifyConfig = { 31 | language: 'html', 32 | path: 'assets/code-snippets/core/autocomplete/app.component.html', 33 | theme: 'dark', 34 | canCopy: true, 35 | header: 'app.component.html' 36 | }; 37 | 38 | code3: NgEzCodePrettifyConfig = { 39 | language: 'typescript', 40 | path: 'assets/code-snippets/core/autocomplete/app.component.ts', 41 | theme: 'dark', 42 | canCopy: true, 43 | header: 'app.component.ts' 44 | }; 45 | 46 | code4: NgEzCodePrettifyConfig = { 47 | language: 'css', 48 | path: 'assets/code-snippets/core/autocomplete/custom.scss', 49 | theme: 'dark', 50 | canCopy: true, 51 | header: 'styles.scss' 52 | }; 53 | 54 | constructor(formBuilder: FormBuilder){ 55 | this.form = formBuilder.group({ 56 | c1: null 57 | }); 58 | } 59 | 60 | ngOnInit(){ 61 | this.options1 = this.setOptions(); 62 | 63 | } 64 | 65 | setOptions() { 66 | const options = []; 67 | for (let i = 0; i < 100; i++) { 68 | const user = { 69 | id: faker.random.uuid(), 70 | username: faker.internet.userName(), 71 | avatar: faker.internet.avatar() 72 | } 73 | options.push(user) 74 | } 75 | 76 | return options; 77 | } 78 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/lazy-renderer/lazy-renderer.page.html: -------------------------------------------------------------------------------- 1 |

Lazy Renderer

2 |

3 | Html and components are rendered until they've actually entered the viewport. 4 |

5 |

Features

6 |
    7 |
  • Lazy load images.
  • 8 |
  • Components are instantiated until they've entered the viewport, thus delaying their lifecycle hooks.
  • 9 |
10 |

Demo

11 |

12 | Open your devtools and scroll down. Notice how the component's ngOnInit method is called until it enters the viewport. Go to the network tab and the image was also lazily loaded. 13 |

14 |
15 | 16 | 17 | 18 |
19 | 20 |
21 |
22 |
23 |

Import

24 |

25 | Begin by importing the module like this: 26 |

27 |
28 |     import {{'{'}} NgEzLazyRendererModule {{'}'}} from '@ngez/core';
29 | 
30 |

Usage

31 | 32 | 33 |
34 | 35 |
36 |
37 | 38 |
39 | Something went wrong while loading the code. 40 | 41 |
42 |
43 |
44 |
45 | 46 | 47 |
48 | 49 |
50 |
51 | 52 |
53 | Something went wrong while loading the code. 54 | 55 |
56 |
57 |
-------------------------------------------------------------------------------- /projects/core/src/forms/max-size.validator.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from "@angular/core/testing"; 2 | import { FormControl } from "@angular/forms"; 3 | import { NgEzMaxSizeValidator } from "./max-size.validator"; 4 | 5 | describe('MaxSizeValidator', () => { 6 | 7 | let directive: NgEzMaxSizeValidator; 8 | 9 | beforeEach(() => { 10 | directive = new NgEzMaxSizeValidator(); 11 | }); 12 | 13 | it('should return null if the "maxSize" input is undefined', () => { 14 | 15 | const file = new File(["blob"], "song.mp3", { type: 'audio/mp3' }); 16 | 17 | expect(directive.validate(new FormControl(file))).toBeNull(); 18 | }); 19 | 20 | it('should return null if all the file sizes are valid.', () => { 21 | const f1 = new File([getRandomText(1)], "file1.txt", { type: 'text/plain' }); 22 | const f2 = new File([getRandomText(999999)], "file2.txt", { type: 'text/plain' }); 23 | const f3 = new File([getRandomText(500)], "file3.txt", { type: 'text/plain' }); 24 | 25 | directive.maxSize = 1000000; 26 | 27 | directive.ngOnChanges(null); 28 | 29 | expect(directive.validate(new FormControl(f1))).toBeNull(); 30 | expect(directive.validate(new FormControl(f2))).toBeNull(); 31 | expect(directive.validate(new FormControl(f3))).toBeNull(); 32 | expect(directive.validate(new FormControl([f1, f3]))).toBeNull(); 33 | expect(directive.validate(new FormControl([f1, f2]))).toBeNull(); 34 | }); 35 | 36 | it('should return an error object if any of the file sizes are invalid', () => { 37 | const f1 = new File([getRandomText(1)], "file1.txt", { type: 'text/plain' }); 38 | const f2 = new File([getRandomText(1000001)], "file2.txt", { type: 'text/plain' }); 39 | const f3 = new File([getRandomText(5000000)], "file3.txt", { type: 'text/plain' }); 40 | 41 | directive.maxSize = 1000000; 42 | 43 | directive.ngOnChanges(null); 44 | 45 | expect(directive.validate(new FormControl(f3))).toBeTruthy(); 46 | expect(directive.validate(new FormControl([f1, f2]))).toBeTruthy(); 47 | }); 48 | }); 49 | 50 | function getRandomText(length: number) { 51 | let text = ""; 52 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 53 | 54 | for (let i = 0; i < length; i++) 55 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 56 | 57 | return text; 58 | } -------------------------------------------------------------------------------- /projects/core/src/forms/total-size.validator.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from "@angular/core/testing"; 2 | import { FormControl } from "@angular/forms"; 3 | import { NgEzTotalSizeValidator } from "./total-size.validator"; 4 | 5 | describe('TotalSizeValidator', () => { 6 | 7 | let directive: NgEzTotalSizeValidator; 8 | 9 | beforeEach(() => { 10 | directive = new NgEzTotalSizeValidator(); 11 | }); 12 | 13 | it('should return null if the "totalSize" input is undefined', () => { 14 | 15 | const file = new File(["blob"], "song.mp3", { type: 'audio/mp3' }); 16 | 17 | expect(directive.validate(new FormControl(file))).toBeNull(); 18 | }); 19 | 20 | it('should return null if the sum of all file sizes is valid.', () => { 21 | const f1 = new File([getRandomText(1)], "file1.txt", { type: 'text/plain' }); 22 | const f2 = new File([getRandomText(999997)], "file2.txt", { type: 'text/plain' }); 23 | const f3 = new File([getRandomText(2)], "file3.txt", { type: 'text/plain' }); 24 | 25 | directive.totalSize = 1000000; 26 | 27 | directive.ngOnChanges(null); 28 | 29 | expect(directive.validate(new FormControl(f1))).toBeNull(); 30 | expect(directive.validate(new FormControl(f2))).toBeNull(); 31 | expect(directive.validate(new FormControl(f3))).toBeNull(); 32 | expect(directive.validate(new FormControl([f1, f3]))).toBeNull(); 33 | expect(directive.validate(new FormControl([f1, f2]))).toBeNull(); 34 | expect(directive.validate(new FormControl([f1, f2, f3]))).toBeNull(); 35 | }); 36 | 37 | it('should return an error object if the sum of all file sizes exceeds the limit.', () => { 38 | const f1 = new File([getRandomText(1)], "file1.txt", { type: 'text/plain' }); 39 | const f2 = new File([getRandomText(1000001)], "file2.txt", { type: 'text/plain' }); 40 | const f3 = new File([getRandomText(1000000)], "file3.txt", { type: 'text/plain' }); 41 | 42 | directive.totalSize = 1000000; 43 | 44 | directive.ngOnChanges(null); 45 | 46 | expect(directive.validate(new FormControl(f2))).toBeTruthy(); 47 | expect(directive.validate(new FormControl([f1, f3]))).toBeTruthy(); 48 | }); 49 | }); 50 | 51 | function getRandomText(length: number) { 52 | let text = ""; 53 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 54 | 55 | for (let i = 0; i < length; i++) 56 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 57 | 58 | return text; 59 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngez", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "build_core": "ng build core && npm run copy && npm run copy_readme && npm run copy_license", 9 | "copy": "xcopy .\\projects\\core\\_variables.scss .\\dist\\core\\", 10 | "copy_readme": "xcopy .\\README.md .\\dist\\core\\", 11 | "copy_license": "xcopy .\\LICENSE .\\dist\\core\\", 12 | "test": "ng test", 13 | "lint": "ng lint", 14 | "e2e": "ng e2e", 15 | "stats": "webpack-bundle-analyzer dist/io/stats.json" 16 | }, 17 | "private": true, 18 | "dependencies": { 19 | "@angular/animations": "^7.2.2", 20 | "@angular/cdk": "^7.2.2", 21 | "@angular/common": "^7.2.2", 22 | "@angular/compiler": "^7.2.2", 23 | "@angular/core": "^7.2.2", 24 | "@angular/flex-layout": "^7.0.0-beta.23", 25 | "@angular/forms": "^7.2.2", 26 | "@angular/http": "^7.2.2", 27 | "@angular/material": "^7.2.2", 28 | "@angular/platform-browser": "^7.2.2", 29 | "@angular/platform-browser-dynamic": "^7.2.2", 30 | "@angular/router": "^7.2.2", 31 | "@fortawesome/angular-fontawesome": "^0.3.0", 32 | "@fortawesome/fontawesome-svg-core": "^1.2.12", 33 | "@fortawesome/free-brands-svg-icons": "^5.6.3", 34 | "@fortawesome/free-regular-svg-icons": "^5.6.3", 35 | "@fortawesome/free-solid-svg-icons": "^5.6.3", 36 | "bootstrap": "^4.2.1", 37 | "classlist.js": "^1.1.20150312", 38 | "core-js": "^2.6.3", 39 | "faker": "^4.1.0", 40 | "rxjs": "^6.0.0", 41 | "zone.js": "^0.8.29" 42 | }, 43 | "devDependencies": { 44 | "@angular-devkit/build-angular": "~0.12.3", 45 | "@angular-devkit/build-ng-packagr": "~0.12.3", 46 | "@angular/cli": "~7.2.3", 47 | "@angular/compiler-cli": "^7.2.2", 48 | "@angular/language-service": "^7.2.2", 49 | "@types/faker": "^4.1.5", 50 | "@types/jasmine": "~3.3.8", 51 | "@types/jasminewd2": "~2.0.3", 52 | "@types/node": "~10.12.18", 53 | "codelyzer": "~4.5.0", 54 | "jasmine-core": "~3.3.0", 55 | "jasmine-spec-reporter": "~4.2.1", 56 | "karma": "~4.0.0", 57 | "karma-chrome-launcher": "~2.2.0", 58 | "karma-coverage-istanbul-reporter": "~2.0.0", 59 | "karma-jasmine": "~2.0.1", 60 | "karma-jasmine-html-reporter": "^1.4.0", 61 | "ng-packagr": "^4.7.0", 62 | "protractor": "~5.4.2", 63 | "ts-node": "~8.0.2", 64 | "tsickle": ">=0.29.0", 65 | "tslib": "^1.9.0", 66 | "tslint": "~5.12.1", 67 | "typescript": "~3.2.4", 68 | "webpack-bundle-analyzer": "^3.0.3" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /projects/core/src/clipboard/clipboard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Optional, PLATFORM_ID, Inject } from "@angular/core"; 2 | import { DOCUMENT, isPlatformBrowser } from "@angular/common"; 3 | import { WINDOW } from "../window/window.service"; 4 | 5 | @Injectable() 6 | export class NgEzClipboardService{ 7 | 8 | constructor( 9 | @Inject(PLATFORM_ID) private platformId: Object, 10 | @Inject(WINDOW) private window: any, 11 | @Optional() @Inject(DOCUMENT) private document: any){} 12 | 13 | copy(payload: string): boolean{ 14 | if(!isPlatformBrowser(this.platformId)) return false; 15 | 16 | const textarea = this.createTextarea(payload); 17 | 18 | this.appendTextarea(textarea); 19 | 20 | const selected = this.getSelection(); 21 | 22 | this.select(textarea, payload); 23 | 24 | const didCopy = document.execCommand('copy'); 25 | 26 | this.removeTextarea(textarea); 27 | 28 | this.restoreSelection(selected); 29 | 30 | return didCopy && this.didCopyInIE11(); 31 | } 32 | 33 | private getSelection(): Range | false { 34 | const documentSelection = document.getSelection(); 35 | return documentSelection.rangeCount > 0 ? documentSelection.getRangeAt(0) : false; 36 | } 37 | 38 | private select(textarea: HTMLTextAreaElement, payload: string) { 39 | textarea.select(); 40 | textarea.selectionStart = 0; 41 | textarea.selectionEnd = payload.length; 42 | } 43 | 44 | private createTextarea(payload: string){ 45 | const textarea: HTMLTextAreaElement = this.document.createElement('textarea'); 46 | textarea.value = payload; 47 | textarea.setAttribute('readonly', ''); 48 | textarea.style.position = 'absolute'; 49 | textarea.style.left = '-9999px'; 50 | return textarea; 51 | } 52 | 53 | private appendTextarea(textarea: HTMLTextAreaElement){ 54 | this.document.body.appendChild(textarea); 55 | } 56 | 57 | private removeTextarea(textarea: HTMLTextAreaElement) { 58 | this.document.body.removeChild(textarea); 59 | } 60 | 61 | private restoreSelection(selected: Range | false) { 62 | if(!selected) return; 63 | 64 | const documentSelection = this.document.getSelection(); 65 | documentSelection.removeAllRanges(); 66 | documentSelection.addRange(selected); 67 | } 68 | 69 | private didCopyInIE11(){ 70 | const clipboardData = this.window['clipboardData']; 71 | if (clipboardData && clipboardData.getData && !clipboardData.getData('text')) 72 | return false; 73 | return true; 74 | } 75 | } -------------------------------------------------------------------------------- /projects/io/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 7 | NgEz 8 |
9 | 10 | 11 | GitHub 12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 | 20 | Overview 21 | 22 | Autocomplete 23 | Clipboard 24 | Code Prettify 25 | File Dropzone 26 | File Input 27 | Forms 28 | In Viewport 29 | Lazy Renderer 30 | Nested Nav 31 | Outside Click 32 | 33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
-------------------------------------------------------------------------------- /projects/io/src/app/core/pages/code-prettify/code-prettify.page.html: -------------------------------------------------------------------------------- 1 |

Code Prettify

2 |

3 | Easily display code snippets 4 |

5 |

Features

6 |
    7 |
  • Light and dark theme, although you can easily add your own.
  • 8 |
  • Copy to clipboard.
  • 9 |
  • Pass the code as a string or a url to get an actual file and display its text.
  • 10 |
  • Display "loading" and "error" components when getting the code asynchronously.
  • 11 |
12 |

Demo

13 | 14 | 15 |
16 | 17 |
18 |
19 | 20 |
21 | Something went wrong while loading the code. 22 | 23 |
24 |
25 |
26 |

Import

27 |

28 | Begin by importing the module like this: 29 |

30 |
31 |     import {{'{'}} NgEzCodePrettifyModule {{'}'}} from '@ngez/core';
32 | 
33 |

Usage

34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 |
42 | Something went wrong while loading the code. 43 | 44 |
45 |
46 |
47 |
48 | 49 | 50 |
51 | 52 |
53 |
54 | 55 |
56 | Something went wrong while loading the code. 57 | 58 |
59 |
60 |
61 |

Styling

62 |

You can create your own theme, or overwrite the existing ones with new colors quite easily:

63 | 64 | 65 |
66 | 67 |
68 |
69 | 70 |
71 | Something went wrong while loading the code. 72 | 73 |
74 |
75 |
-------------------------------------------------------------------------------- /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 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | true, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /projects/io/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/guide/browser-support 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 | /** 38 | * If the application will be indexed by Google Search, the following is required. 39 | * Googlebot uses a renderer based on Chrome 41. 40 | * https://developers.google.com/search/docs/guides/rendering 41 | **/ 42 | import 'core-js/es6/array'; 43 | 44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 45 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 46 | 47 | /** IE10 and IE11 requires the following for the Reflect API. */ 48 | import 'core-js/es6/reflect'; 49 | 50 | /** 51 | * Web Animations `@angular/platform-browser/animations` 52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 54 | **/ 55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 56 | 57 | /** 58 | * By default, zone.js will patch all possible macroTask and DomEvents 59 | * user can disable parts of macroTask/DomEvents patch by setting following flags 60 | */ 61 | 62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 65 | 66 | /* 67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 69 | */ 70 | // (window as any).__Zone_enable_cross_context_check = true; 71 | 72 | /*************************************************************************************************** 73 | * Zone JS is required by default for Angular itself. 74 | */ 75 | import 'zone.js/dist/zone'; // Included with Angular CLI. 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /projects/core/src/nested-nav/nav-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ContentChildren, QueryList, AfterContentInit, OnInit, OnDestroy, OnChanges, ViewEncapsulation } from "@angular/core"; 2 | import { faAngleUp, faAngleDown, IconDefinition } from '@fortawesome/free-solid-svg-icons'; 3 | import { RouterLink, RouterLinkWithHref, Router, NavigationEnd } from "@angular/router"; 4 | import { Subscription } from "rxjs"; 5 | import { filter } from "rxjs/operators"; 6 | 7 | @Component({ 8 | selector: 'ngez-nav-list', 9 | templateUrl: './nav-list.component.html', 10 | styleUrls: ['./nav-list.component.scss'], 11 | encapsulation: ViewEncapsulation.None 12 | }) 13 | export class NgEzNavListComponent implements OnChanges, OnInit, AfterContentInit, OnDestroy { 14 | 15 | @Input() title: string; 16 | 17 | @Input() routerLinkActiveOptions: { exact: boolean } = { exact: false }; 18 | 19 | @ContentChildren(NgEzNavListComponent) lists: QueryList; 20 | 21 | @ContentChildren(RouterLink) links: QueryList; 22 | 23 | @ContentChildren(RouterLinkWithHref) linksWithHrefs: QueryList; 24 | 25 | faAngleUp: IconDefinition = faAngleUp; 26 | 27 | faAngleDown: IconDefinition = faAngleDown; 28 | 29 | open: boolean = false; 30 | 31 | active: boolean = false; 32 | 33 | navigationSubscription: Subscription; 34 | 35 | linksQueryListChangesSubscription: Subscription; 36 | 37 | linksWithHrefsQueryListChangesSubscription: Subscription; 38 | 39 | constructor(private router: Router) { } 40 | 41 | ngOnChanges() { 42 | this.update(); 43 | } 44 | 45 | ngOnInit() { 46 | this.navigationSubscription = this.router.events 47 | .pipe(filter(e => e instanceof NavigationEnd)) 48 | .subscribe(e => this.update()) 49 | } 50 | 51 | ngAfterContentInit() { 52 | this.linksQueryListChangesSubscription = 53 | this.links.changes.subscribe((c) => this.update()); 54 | this.linksWithHrefsQueryListChangesSubscription = 55 | this.linksWithHrefs.changes.subscribe((c) => this.update()); 56 | this.update(); 57 | } 58 | 59 | ngOnDestroy() { 60 | if(this.navigationSubscription) 61 | this.navigationSubscription.unsubscribe(); 62 | if(this.linksQueryListChangesSubscription) 63 | this.linksQueryListChangesSubscription.unsubscribe(); 64 | if(this.linksWithHrefsQueryListChangesSubscription) 65 | this.linksWithHrefsQueryListChangesSubscription.unsubscribe(); 66 | } 67 | 68 | onToggle() { 69 | this.open = !this.open; 70 | } 71 | 72 | private update(){ 73 | if(!this.links || !this.linksWithHrefs || !this.router.navigated) 74 | return; 75 | this.active = this.hasActiveLinks(); 76 | 77 | if(!this.open) 78 | this.open = this.active; 79 | } 80 | 81 | private isLinkActive(router: Router): (link: (RouterLink | RouterLinkWithHref)) => boolean { 82 | return (link: RouterLink | RouterLinkWithHref) => 83 | router.isActive(link.urlTree, this.exact); 84 | } 85 | 86 | private hasActiveLinks(): boolean { 87 | return this.links.some(this.isLinkActive(this.router)) || 88 | this.linksWithHrefs.some(this.isLinkActive(this.router)); 89 | } 90 | 91 | private get exact(): boolean { 92 | return this.routerLinkActiveOptions ? this.routerLinkActiveOptions.exact : false; 93 | } 94 | } -------------------------------------------------------------------------------- /projects/core/src/autocomplete/autocomplete.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | OnInit, 4 | TemplateRef, 5 | ViewChild, 6 | QueryList, 7 | ContentChildren, 8 | EventEmitter, 9 | Output, 10 | ElementRef, 11 | Input 12 | } from '@angular/core'; 13 | import { ActiveDescendantKeyManager } from "@angular/cdk/a11y"; 14 | import { NgEzAutocompleteOptionComponent } from './autocomplete-option.component'; 15 | import { UP_ARROW, DOWN_ARROW, ENTER, TAB } from '@angular/cdk/keycodes'; 16 | import { merge, Subscription, Observable } from 'rxjs'; 17 | import { NgEzAutocompleteConfig, defaultConfig } from "./models"; 18 | 19 | @Component({ 20 | selector: 'ngez-autocomplete', 21 | templateUrl: './autocomplete.component.html', 22 | styleUrls: ['./autocomplete.component.scss'], 23 | host: { 24 | 'style.max-height.px': `config.maxHeight` 25 | } 26 | }) 27 | export class NgEzAutocompleteComponent { 28 | 29 | defaultConfig = defaultConfig; 30 | 31 | @Input() config: NgEzAutocompleteConfig 32 | 33 | @Output() opened = new EventEmitter(); 34 | 35 | @Output() closed = new EventEmitter(); 36 | 37 | @ViewChild(TemplateRef) template: TemplateRef; 38 | 39 | @ViewChild('panel') panel: ElementRef; 40 | 41 | @ContentChildren(NgEzAutocompleteOptionComponent, { descendants: true }) options: QueryList; 42 | 43 | keyboardEventsManager: ActiveDescendantKeyManager; 44 | 45 | subscription: Subscription; 46 | 47 | optionEventEmitters: Observable>; 48 | 49 | ngAfterViewInit() { 50 | this.keyboardEventsManager = 51 | new ActiveDescendantKeyManager(this.options).withWrap(true); 52 | } 53 | 54 | reset() { 55 | this.keyboardEventsManager.setActiveItem(-1); 56 | } 57 | 58 | handleKeyDown(event: KeyboardEvent) { 59 | event.stopImmediatePropagation(); 60 | if (this.keyboardEventsManager) { 61 | if (event.keyCode === DOWN_ARROW || event.keyCode === UP_ARROW || event.keyCode === TAB) { 62 | // passing the event to key manager so we get a change fired 63 | this.keyboardEventsManager.onKeydown(event); 64 | return false; 65 | } else if (event.keyCode === ENTER) { 66 | // when we hit enter, the keyboardManager should call the selectItem method of the `ListItemComponent` 67 | this.keyboardEventsManager.activeItem.onSelect(); 68 | return false; 69 | } 70 | } 71 | } 72 | 73 | getScrollTop() { 74 | const index = this.keyboardEventsManager.activeItemIndex; 75 | const option = this.keyboardEventsManager.activeItem; 76 | const offset = 77 | this.options.toArray().slice(0, index + 1).reduce((offset, option) => offset + option.getOffsetHeight(), 0); 78 | const currentScrollPosition = this.panel.nativeElement.scrollTop; 79 | 80 | if (offset - option.getOffsetHeight() < currentScrollPosition) 81 | return offset - option.getOffsetHeight() + 3; 82 | 83 | const optionHeight = option.getOffsetHeight(); 84 | 85 | const panelHeight = this.panel.nativeElement.getBoundingClientRect().height; 86 | 87 | if(offset > currentScrollPosition + panelHeight) 88 | return Math.max(0, offset - panelHeight); 89 | 90 | return currentScrollPosition; 91 | } 92 | 93 | setScrollTop() { 94 | if (!this.panel) 95 | return; 96 | 97 | const scrollTop = this.getScrollTop(); 98 | 99 | this.panel.nativeElement.scrollTop = scrollTop; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ngezplatform@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /projects/io/src/theme.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | // Plus imports for other components in your app. 3 | 4 | // Include the common styles for Angular Material. We include this here so that you only 5 | // have to load a single css file for Angular Material in your app. 6 | // Be sure that you only ever include this mixin once! 7 | @include mat-core(); 8 | 9 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 10 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 11 | // hue. Available color palettes: https://www.google.com/design/spec/style/color.html 12 | $custom-primary: ( 13 | 50 : #e0e2e5, 14 | 100 : #b3b6bf, 15 | 200 : #808594, 16 | 300 : #4d5469, 17 | 400 : #263049, 18 | 500 : #000b29, 19 | 600 : #000a24, 20 | 700 : #00081f, 21 | 800 : #000619, 22 | 900 : #00030f, 23 | A100 : #5252ff, 24 | A200 : #1f1fff, 25 | A400 : #0000eb, 26 | A700 : #0000d1, 27 | contrast: ( 28 | 50 : #000000, 29 | 100 : #000000, 30 | 200 : #000000, 31 | 300 : #ffffff, 32 | 400 : #ffffff, 33 | 500 : #ffffff, 34 | 600 : #ffffff, 35 | 700 : #ffffff, 36 | 800 : #ffffff, 37 | 900 : #ffffff, 38 | A100 : #ffffff, 39 | A200 : #ffffff, 40 | A400 : #ffffff, 41 | A700 : #ffffff, 42 | ) 43 | ); 44 | 45 | $custom-accent: ( 46 | 50 : #fae0e5, 47 | 100 : #f3b3be, 48 | 200 : #eb8093, 49 | 300 : #e34d67, 50 | 400 : #dd2647, 51 | 500 : #d70026, 52 | 600 : #d30022, 53 | 700 : #cd001c, 54 | 800 : #c70017, 55 | 900 : #be000d, 56 | A100 : #ffe6e7, 57 | A200 : #ffb3b5, 58 | A400 : #ff8084, 59 | A700 : #ff676b, 60 | contrast: ( 61 | 50 : #000000, 62 | 100 : #000000, 63 | 200 : #000000, 64 | 300 : #ffffff, 65 | 400 : #ffffff, 66 | 500 : #ffffff, 67 | 600 : #ffffff, 68 | 700 : #ffffff, 69 | 800 : #ffffff, 70 | 900 : #ffffff, 71 | A100 : #000000, 72 | A200 : #000000, 73 | A400 : #000000, 74 | A700 : #000000, 75 | ) 76 | ); 77 | 78 | $custom-warn: ( 79 | 50 : #fdf6e8, 80 | 100 : #faeac5, 81 | 200 : #f6dc9e, 82 | 300 : #f2cd77, 83 | 400 : #f0c35a, 84 | 500 : #edb83d, 85 | 600 : #ebb137, 86 | 700 : #e8a82f, 87 | 800 : #e5a027, 88 | 900 : #e0911a, 89 | A100 : #ffffff, 90 | A200 : #fff2e0, 91 | A400 : #ffdcad, 92 | A700 : #ffd194, 93 | contrast: ( 94 | 50 : #000000, 95 | 100 : #000000, 96 | 200 : #000000, 97 | 300 : #000000, 98 | 400 : #000000, 99 | 500 : #000000, 100 | 600 : #000000, 101 | 700 : #000000, 102 | 800 : #000000, 103 | 900 : #000000, 104 | A100 : #000000, 105 | A200 : #000000, 106 | A400 : #000000, 107 | A700 : #000000, 108 | ) 109 | ); 110 | 111 | 112 | $app-primary: mat-palette($custom-primary); 113 | $app-accent: mat-palette($custom-accent); 114 | 115 | // The warn palette is optional (defaults to red). 116 | $app-warn: mat-palette($custom-warn); 117 | 118 | // Create the theme object (a Sass map containing all of the palettes). 119 | $app-theme: mat-light-theme($app-primary, $app-accent, $app-warn); 120 | 121 | // Include theme styles for core and each component used in your app. 122 | // Alternatively, you can import and @include the theme mixins for each component 123 | // that you are using. 124 | @include angular-material-theme($app-theme); -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/file-dropzone/file-dropzone.page.html: -------------------------------------------------------------------------------- 1 |

File Dropzone

2 |

3 | Turn any element into a file dropzone and you decide how you want to render 4 | the files. The control's value will be the File[]. 5 |

6 |

Features

7 |
    8 |
  • Works with FormsModule and ReactiveFormsModule.
  • 9 |
  • Dropping a folder will extract the files.
  • 10 |
  • Easily style the dropzone while dropping files.
  • 11 |
12 |

Caveat

13 |

14 | Internet Explorer doesn't allow dropping folders or extracting the files from 15 | a folder, so if the dropped files contain at least one folder, it'll return an 16 | empty array. Works as expected on Edge. 17 |

18 |

Demo

19 |
20 | 21 |
28 |

29 | Drop your files 30 |

31 | 34 |
35 |

36 | Dropped files: 37 |

38 |
    39 |
  • 40 | {{ file.name }} 41 | 49 |
  • 50 |
51 |
52 |

Import

53 |
 54 |     import {{ "{" }} NgEzFileModule {{ "}" }} from '@ngez/core';
 55 | 
56 |

Usage

57 | 58 | 59 |
60 | 61 |
62 |
63 | 64 |
65 | Something went wrong while loading the code. 68 | 69 |
70 |
71 |
72 |
73 | 74 | 75 |
76 | 77 |
78 |
79 | 80 |
81 | Something went wrong while loading the code. 84 | 85 |
86 |
87 |
88 |
89 | 90 | 91 |
92 | 93 |
94 |
95 | 96 |
97 | Something went wrong while loading the code. 100 | 101 |
102 |
103 |
104 | -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/nested-nav/nested-nav.page.html: -------------------------------------------------------------------------------- 1 |

Nested Nav

2 |

3 | Nest your router links in lists which can be collapsed and expanded. 4 |

5 |

Features

6 |
    7 |
  • Easily apply styling to active links.
  • 8 |
  • Nest router links which can be expanded and collapsed.
  • 9 |
  • Nav lists will automatically expand when navigating to a child route.
  • 10 |
11 |

Demo

12 | 13 | 14 | Overview 15 | 16 | Autocomplete 17 | Nested Nav 18 | Code Prettify 19 | 20 | 21 | Outside Click 22 | Clipboard 23 | 24 | 25 | 26 |

Import

27 |

28 | Begin by importing the module like this: 29 |

30 |
31 |     import {{'{'}} NgEzNestedNavModule {{'}'}} from '@ngez/core';
32 | 
33 |

Usage

34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 |
42 | Something went wrong while loading the code. 43 | 44 |
45 |
46 |
47 |
48 | 49 | 50 |
51 | 52 |
53 |
54 | 55 |
56 | Something went wrong while loading the code. 57 | 58 |
59 |
60 |
61 |

Styling

62 |

By default when a router link is active or being hovered, it'll have a light background, this can easily be changed:

63 | 64 | 65 |
66 | 67 |
68 |
69 | 70 |
71 | Something went wrong while loading the code. 72 | 73 |
74 |
75 |
-------------------------------------------------------------------------------- /projects/core/src/file/file-dropzone.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { Component, DebugElement } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | 5 | import { NgEzFileDropzoneDirective } from './file-dropzone.directive'; 6 | 7 | @Component({ 8 | selector: 'test', 9 | template: `
` 10 | }) 11 | class FileDropzoneTestComponent{} 12 | 13 | describe('FileDropzoneDirective', () => { 14 | 15 | let directive: NgEzFileDropzoneDirective; 16 | let fixture: ComponentFixture; 17 | let debugElement: DebugElement; 18 | 19 | beforeEach(() => { 20 | TestBed.configureTestingModule({ 21 | declarations: [FileDropzoneTestComponent, NgEzFileDropzoneDirective] 22 | }).compileComponents(); 23 | 24 | fixture = TestBed.createComponent(FileDropzoneTestComponent); 25 | debugElement = fixture.debugElement.query(By.directive(NgEzFileDropzoneDirective)); 26 | directive = debugElement.injector.get(NgEzFileDropzoneDirective); 27 | fixture.detectChanges(); 28 | }); 29 | 30 | it('should be instantiated', () => { 31 | expect(directive).toBeDefined(); 32 | }); 33 | 34 | it('should pass the "accept" input', () => { 35 | expect(directive.accept).toBe('.jpg'); 36 | }); 37 | 38 | it('should set/remove the active class', () => { 39 | const target = {}; 40 | expect(debugElement.classes.active).toBe(false); 41 | directive.onDragEnter(target); 42 | fixture.detectChanges(); 43 | expect(debugElement.classes.active).toBe(true); 44 | directive.onDragLeave(target); 45 | fixture.detectChanges(); 46 | expect(debugElement.classes.active).toBe(false); 47 | }); 48 | 49 | it('should not set the active class while disabled', () => { 50 | const target = {}; 51 | directive.setDisabledState(true); 52 | directive.onDragEnter(target); 53 | fixture.detectChanges(); 54 | expect(debugElement.classes.active).toBe(false); 55 | }); 56 | 57 | it('should set the dropped files as the value', (done) => { 58 | 59 | directive.multiple = true; 60 | 61 | const dt = new DataTransfer(); 62 | 63 | const files = [ 64 | new File(["blob"], "song.mp3", { type: 'audio/mp3' }), 65 | new File(["blob"], "99872_n.jpg", { type: 'image/jpeg' }), 66 | new File(["blob"], "asdf.txt", { type: 'text/plain' }) 67 | ]; 68 | 69 | files.forEach(file => dt.items.add(file)); 70 | 71 | const event = { 72 | dataTransfer: dt, 73 | stopPropagation: function(){}, 74 | preventDefault: function(){} 75 | }; 76 | 77 | 78 | 79 | spyOn(directive as any, 'getFilesFromDataTransferItemList').and.returnValue(Promise.resolve(files)); 80 | 81 | directive.onDrop(event).then(() => { 82 | expect(directive.value).toEqual(files); 83 | done(); 84 | }); 85 | }); 86 | 87 | it('should set the first file as the value', (done) => { 88 | 89 | directive.multiple = false; 90 | 91 | const dt = new DataTransfer(); 92 | 93 | const files = [ 94 | new File(["blob"], "song.mp3", { type: 'audio/mp3' }), 95 | new File(["blob"], "99872_n.jpg", { type: 'image/jpeg' }), 96 | new File(["blob"], "asdf.txt", { type: 'text/plain' }) 97 | ]; 98 | 99 | files.forEach(file => dt.items.add(file)); 100 | 101 | const event = { 102 | dataTransfer: dt, 103 | stopPropagation: function(){}, 104 | preventDefault: function(){} 105 | }; 106 | 107 | 108 | 109 | spyOn(directive as any, 'getFilesFromDataTransferItemList').and.returnValue(Promise.resolve(files)); 110 | 111 | directive.onDrop(event).then(() => { 112 | expect(directive.value).toEqual(files[0]); 113 | done(); 114 | }); 115 | }) 116 | }); -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-prettify.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Input, 4 | ViewEncapsulation, 5 | ViewChild, 6 | ElementRef, 7 | ContentChild, 8 | AfterContentInit, 9 | Output, 10 | EventEmitter, 11 | OnDestroy, 12 | HostBinding, 13 | OnChanges, 14 | OnInit, 15 | PLATFORM_ID, 16 | Inject} from "@angular/core"; 17 | import { PrettifyService } from "./prettify.service"; 18 | import { CodeService } from "./code.service"; 19 | import { map, switchMap, tap, delay } from "rxjs/operators"; 20 | import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; 21 | import { NgEzCodeLoadingComponent } from "./code-loading.component"; 22 | import { NgEzCodeLoadingErrorComponent } from "./code-loading-error.component"; 23 | import { Observable, of, Subscription } from "rxjs"; 24 | import { NgEzCodePrettifyConfig } from "./models"; 25 | import { NgEzReloadDirective } from "./reload.directive"; 26 | import { faClone, IconDefinition } from '@fortawesome/free-regular-svg-icons'; 27 | import { isPlatformBrowser } from "@angular/common"; 28 | 29 | @Component({ 30 | selector: 'ngez-code-prettify', 31 | templateUrl: './code-prettify.component.html', 32 | styleUrls: ['./code-prettify.component.scss'], 33 | encapsulation: ViewEncapsulation.None 34 | }) 35 | export class NgEzCodePrettifyComponent implements OnChanges, OnInit, AfterContentInit, OnDestroy { 36 | 37 | @Input() config: NgEzCodePrettifyConfig; 38 | 39 | @Input() loading: boolean; 40 | 41 | @Input() error: boolean; 42 | 43 | @Output() reload = new EventEmitter(); 44 | 45 | @ViewChild('pre') pre: ElementRef; 46 | 47 | @ContentChild(NgEzCodeLoadingComponent) codeLoadingComponent: NgEzCodeLoadingComponent; 48 | 49 | @ContentChild(NgEzCodeLoadingErrorComponent) codeLoadingErrorComponent: NgEzCodeLoadingErrorComponent; 50 | 51 | @HostBinding('class') get classes(): string { 52 | return this.config && this.config.theme ? this.config.theme : 'dark'; 53 | } 54 | 55 | faClone: IconDefinition = faClone; 56 | 57 | _code: string; 58 | 59 | _loading = false; 60 | 61 | _error = false; 62 | 63 | prettifiedCode$: Observable; 64 | 65 | private subscription: Subscription; 66 | 67 | constructor( 68 | @Inject(PLATFORM_ID) private platformId: Object, 69 | private sanitizer: DomSanitizer, 70 | private prettify: PrettifyService, 71 | private service: CodeService) { } 72 | 73 | ngOnChanges() { 74 | this.update(); 75 | } 76 | 77 | ngOnInit() { 78 | this.update(); 79 | } 80 | 81 | ngAfterContentInit() { 82 | if (!this.codeLoadingErrorComponent || !this.codeLoadingErrorComponent.reloadDirective) 83 | return; 84 | this.subscription = this.codeLoadingErrorComponent.reloadDirective.reload.subscribe(() => { 85 | this.reload.emit(); 86 | this.update(); 87 | }); 88 | } 89 | 90 | ngOnDestroy() { 91 | if(this.subscription) 92 | this.subscription.unsubscribe(); 93 | } 94 | 95 | private update() { 96 | 97 | if (!isPlatformBrowser(this.platformId) || !this.config || !(this.config.code || this.config.path)) 98 | return; 99 | 100 | this._loading = true; 101 | this._error = false; 102 | 103 | const code$ = this.config.code 104 | ? of(this.config.code) 105 | : this.service.get(this.config.path); 106 | 107 | const prettifiedCode$ = code$.pipe( 108 | tap(code => this._code = code), 109 | map(code => this.prettify.formatCode(code, this.config.language, this.config.linenums)), 110 | map(code => this.sanitizer.bypassSecurityTrustHtml(code)), 111 | tap(() => this._loading = false, () => { 112 | this._loading = false; 113 | this._error = true; 114 | }) 115 | ); 116 | 117 | this.prettifiedCode$ = prettifiedCode$; 118 | } 119 | 120 | get isLoading(): boolean { 121 | return this.loading || this._loading; 122 | } 123 | 124 | get hasError(): boolean { 125 | return this.error || this._error; 126 | } 127 | } -------------------------------------------------------------------------------- /projects/io/src/app/core/pages/autocomplete/autocomplete.page.html: -------------------------------------------------------------------------------- 1 |

Autocomplete

2 |

3 | Use your own styled input and enhance it with panel from which the user may select the desired option. 4 |

5 |

Features

6 |
    7 |
  • Options may contain any HTML.
  • 8 |
  • Value may be an object. Simply provide a label extractor function to get the correct string.
  • 9 |
  • Navigate and select with the keyboard.
  • 10 |
  • Disable individual options.
  • 11 |
  • Programatically open/close/toggle the panel.
  • 12 |
13 |

Demo

14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 | Avatar 28 |
29 | {{option.username}} 30 |
31 |
32 |
33 |
34 |

Import

35 |

First add the following line to your styles.scss file:

36 | 37 | 38 |
39 | 40 |
41 |
42 | 43 |
44 | Something went wrong while loading the code. 45 | 46 |
47 |
48 |
49 |

Then you import the module like:

50 |
51 |     import {{'{'}} NgEzAutocompleteModule {{'}'}} from '@ngez/core';
52 | 
53 |

Usage

54 | 55 | 56 |
57 | 58 |
59 |
60 | 61 |
62 | Something went wrong while loading the code. 63 | 64 |
65 |
66 |
67 |
68 | 69 | 70 |
71 | 72 |
73 |
74 | 75 |
76 | Something went wrong while loading the code. 77 | 78 |
79 |
80 |
81 |

Styling

82 |

Customizing the appearance of the autocomplete panel and its options is quite simple:

83 | 84 | 85 |
86 | 87 |
88 |
89 | 90 |
91 | Something went wrong while loading the code. 92 | 93 |
94 |
95 |
-------------------------------------------------------------------------------- /projects/core/src/in-viewport/in-viewport.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | ElementRef, 4 | Inject, 5 | PLATFORM_ID, 6 | Optional, 7 | OnInit, 8 | Input, 9 | TemplateRef, 10 | OnDestroy, 11 | Output, 12 | EventEmitter } from "@angular/core"; 13 | import { WINDOW } from "../window/window.service"; 14 | import { DOCUMENT, isPlatformBrowser } from "@angular/common"; 15 | import { NgEzInViewportConfig, defaultConfig, defaultOffsetConfig, NgEzInViewportEvent } from "./models"; 16 | import { fromEvent, merge, Subscription, of } from "rxjs"; 17 | 18 | @Directive({ 19 | selector: '[ngezInViewport]', 20 | exportAs: 'ngezInViewport' 21 | }) 22 | export class NgEzInViewportDirective implements OnInit, OnDestroy{ 23 | 24 | @Input() set config(config: NgEzInViewportConfig){ 25 | this._config = config; 26 | } 27 | 28 | @Output() inViewportChange = new EventEmitter(); 29 | 30 | private _config: NgEzInViewportConfig; 31 | 32 | private container: Element; 33 | 34 | private subscription: Subscription; 35 | 36 | private latest: NgEzInViewportEvent = { 37 | top: false, 38 | bottom: false, 39 | left: false, 40 | right: false, 41 | all: false, 42 | any: false 43 | }; 44 | 45 | constructor( 46 | private element: ElementRef, 47 | @Inject(PLATFORM_ID) private platformId: Object, 48 | @Inject(WINDOW) private window: any, 49 | @Optional() @Inject(DOCUMENT) private document: any){} 50 | 51 | ngOnInit() { 52 | if(!isPlatformBrowser(this.platformId)) return; 53 | 54 | this.container = this.getClosestScrollableParent(this.element.nativeElement); 55 | 56 | //Check on scroll, window resize and oninit 57 | this.subscription = merge( 58 | fromEvent(this.isDocumentTheScrollableContainer() ? this.document : this.container, 'scroll'), 59 | fromEvent(this.window, 'resize'), 60 | of(null)) 61 | .subscribe(() => this.check()); 62 | } 63 | 64 | ngOnDestroy() { 65 | if(this.subscription) 66 | this.subscription.unsubscribe(); 67 | } 68 | 69 | 70 | get config(): NgEzInViewportConfig { 71 | const { offset = {}, ...config } = this._config || {}; 72 | 73 | return { 74 | ...defaultConfig, 75 | ...config, 76 | offset: { 77 | ...defaultOffsetConfig, 78 | ...offset 79 | } 80 | }; 81 | } 82 | 83 | check(): NgEzInViewportEvent{ 84 | const previous = this.latest; 85 | const current = this.calculateVisibility(); 86 | 87 | const hasChanged = previous.top != current.top 88 | || previous.bottom != current.bottom 89 | || previous.left != current.left 90 | || previous.right != current.right; 91 | 92 | if(hasChanged) 93 | this.inViewportChange.emit(current); 94 | 95 | this.latest = current; 96 | return current; 97 | } 98 | 99 | private calculateVisibility(): NgEzInViewportEvent{ 100 | const elementRect = this.element.nativeElement.getBoundingClientRect(); 101 | 102 | const height = elementRect.height; 103 | const width = elementRect.width; 104 | 105 | const containerRect = this.container.getBoundingClientRect(); 106 | 107 | const top = elementRect.top - (this.isDocumentTheScrollableContainer() 108 | ? 0 109 | : containerRect.top); 110 | 111 | const bottom = top + height; 112 | 113 | const left = elementRect.left - (this.isDocumentTheScrollableContainer() 114 | ? 0 115 | : containerRect.left); 116 | 117 | const right = left + width; 118 | 119 | const containerHeight = this.container.clientHeight || this.window.innerHeight; 120 | const containerWidth = this.container.clientWidth || this.window.innerWidth; 121 | 122 | const topInView = top + this.config.offset.top >= 0 && top <= (containerHeight + this.config.offset.top); 123 | const bottomInView = bottom + this.config.offset.bottom >= 0 && bottom <= (containerHeight + this.config.offset.bottom); 124 | const leftInView = left + this.config.offset.left >= 0 && left <= (containerWidth + this.config.offset.left); 125 | const rightInView = right + this.config.offset.right >= 0 && right <= (containerWidth + this.config.offset.right); 126 | 127 | return { 128 | top: topInView, 129 | bottom: bottomInView, 130 | left: leftInView && (topInView || bottomInView), 131 | right: rightInView && (topInView || bottomInView), 132 | any: (topInView || bottomInView) && (leftInView || rightInView), 133 | all: topInView && bottomInView && leftInView && rightInView 134 | }; 135 | } 136 | 137 | private getClosestScrollableParent(node: Node) { 138 | return !node || node === this.document.body 139 | ? this.document.documentElement 140 | : this.isScrollable(node) 141 | ? node 142 | : this.getClosestScrollableParent(node.parentNode); 143 | } 144 | 145 | private isScrollable(node: Node){ 146 | const regex = /(auto|scroll)/; 147 | return regex.test( 148 | this.getComputedStyle(node, 'overflow') + 149 | this.getComputedStyle(node, 'overflow-y') + 150 | this.getComputedStyle(node, 'overflow-x')); 151 | } 152 | 153 | private getComputedStyle(node: Node, style: string){ 154 | return this.window.getComputedStyle(node).getPropertyValue(style); 155 | } 156 | 157 | private isDocumentTheScrollableContainer(){ 158 | return this.document.documentElement === this.container; 159 | } 160 | } -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "core": { 7 | "root": "projects/core", 8 | "sourceRoot": "projects/core/src", 9 | "projectType": "library", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-ng-packagr:build", 14 | "options": { 15 | "tsConfig": "projects/core/tsconfig.lib.json", 16 | "project": "projects/core/ng-package.json" 17 | } 18 | }, 19 | "test": { 20 | "builder": "@angular-devkit/build-angular:karma", 21 | "options": { 22 | "main": "projects/core/src/test.ts", 23 | "tsConfig": "projects/core/tsconfig.spec.json", 24 | "karmaConfig": "projects/core/karma.conf.js" 25 | } 26 | }, 27 | "lint": { 28 | "builder": "@angular-devkit/build-angular:tslint", 29 | "options": { 30 | "tsConfig": [ 31 | "projects/core/tsconfig.lib.json", 32 | "projects/core/tsconfig.spec.json" 33 | ], 34 | "exclude": [ 35 | "**/node_modules/**" 36 | ] 37 | } 38 | } 39 | } 40 | }, 41 | "io": { 42 | "root": "projects/io/", 43 | "sourceRoot": "projects/io/src", 44 | "projectType": "application", 45 | "prefix": "app", 46 | "schematics": { 47 | "@schematics/angular:component": { 48 | "styleext": "scss" 49 | } 50 | }, 51 | "architect": { 52 | "build": { 53 | "builder": "@angular-devkit/build-angular:browser", 54 | "options": { 55 | "outputPath": "dist/io", 56 | "index": "projects/io/src/index.html", 57 | "main": "projects/io/src/main.ts", 58 | "polyfills": "projects/io/src/polyfills.ts", 59 | "tsConfig": "projects/io/tsconfig.app.json", 60 | "assets": [ 61 | "projects/io/src/favicon.ico", 62 | "projects/io/src/assets" 63 | ], 64 | "styles": [ 65 | "projects/io/src/styles.scss", 66 | "projects/io/src/theme.scss" 67 | ], 68 | "scripts": [] 69 | }, 70 | "configurations": { 71 | "production": { 72 | "fileReplacements": [ 73 | { 74 | "replace": "projects/io/src/environments/environment.ts", 75 | "with": "projects/io/src/environments/environment.prod.ts" 76 | } 77 | ], 78 | "optimization": true, 79 | "outputHashing": "all", 80 | "sourceMap": false, 81 | "extractCss": true, 82 | "namedChunks": true, 83 | "aot": true, 84 | "extractLicenses": true, 85 | "vendorChunk": false, 86 | "buildOptimizer": true, 87 | "budgets": [ 88 | { 89 | "type": "initial", 90 | "maximumWarning": "2mb", 91 | "maximumError": "5mb" 92 | } 93 | ] 94 | } 95 | } 96 | }, 97 | "serve": { 98 | "builder": "@angular-devkit/build-angular:dev-server", 99 | "options": { 100 | "browserTarget": "io:build" 101 | }, 102 | "configurations": { 103 | "production": { 104 | "browserTarget": "io:build:production" 105 | } 106 | } 107 | }, 108 | "extract-i18n": { 109 | "builder": "@angular-devkit/build-angular:extract-i18n", 110 | "options": { 111 | "browserTarget": "io:build" 112 | } 113 | }, 114 | "test": { 115 | "builder": "@angular-devkit/build-angular:karma", 116 | "options": { 117 | "main": "projects/io/src/test.ts", 118 | "polyfills": "projects/io/src/polyfills.ts", 119 | "tsConfig": "projects/io/tsconfig.spec.json", 120 | "karmaConfig": "projects/io/karma.conf.js", 121 | "styles": [ 122 | "projects/io/src/styles.scss" 123 | ], 124 | "scripts": [], 125 | "assets": [ 126 | "projects/io/src/favicon.ico", 127 | "projects/io/src/assets" 128 | ] 129 | } 130 | }, 131 | "lint": { 132 | "builder": "@angular-devkit/build-angular:tslint", 133 | "options": { 134 | "tsConfig": [ 135 | "projects/io/tsconfig.app.json", 136 | "projects/io/tsconfig.spec.json" 137 | ], 138 | "exclude": [ 139 | "**/node_modules/**" 140 | ] 141 | } 142 | } 143 | } 144 | }, 145 | "io-e2e": { 146 | "root": "projects/io-e2e/", 147 | "projectType": "application", 148 | "prefix": "", 149 | "architect": { 150 | "e2e": { 151 | "builder": "@angular-devkit/build-angular:protractor", 152 | "options": { 153 | "protractorConfig": "projects/io-e2e/protractor.conf.js", 154 | "devServerTarget": "io:serve" 155 | }, 156 | "configurations": { 157 | "production": { 158 | "devServerTarget": "io:serve:production" 159 | } 160 | } 161 | }, 162 | "lint": { 163 | "builder": "@angular-devkit/build-angular:tslint", 164 | "options": { 165 | "tsConfig": "projects/io-e2e/tsconfig.e2e.json", 166 | "exclude": [ 167 | "**/node_modules/**" 168 | ] 169 | } 170 | } 171 | } 172 | } 173 | }, 174 | "defaultProject": "io" 175 | } -------------------------------------------------------------------------------- /projects/core/src/code-prettify/code-prettify.component.scss: -------------------------------------------------------------------------------- 1 | @import "../../variables"; 2 | 3 | ngez-code-prettify{ 4 | display: block; 5 | } 6 | .ngez-code-prettify{ 7 | margin-top: 0; 8 | margin-bottom: 0; 9 | } 10 | 11 | .ngez-code-prettify-content-container{ 12 | &:hover .ngez-copy-button{ 13 | display: inline-block; 14 | } 15 | } 16 | 17 | 18 | .ngez-code-prettify-content{ 19 | overflow: auto; 20 | padding: 12px; 21 | } 22 | 23 | 24 | .ngez-copy-container{ 25 | 26 | z-index: 2; 27 | position: absolute; 28 | top: 5px; 29 | right: 16px; 30 | 31 | .ngez-copy-button{ 32 | outline:none; 33 | display: none; 34 | border: none; 35 | background-color: transparent; 36 | margin: 0; 37 | text-decoration: none; 38 | cursor: pointer; 39 | text-align: center; 40 | transition: background 250ms ease-in-out, 41 | transform 150ms ease; 42 | -webkit-appearance: none; 43 | -moz-appearance: none; 44 | } 45 | } 46 | 47 | 48 | ol.linenums { 49 | margin-top: 0; 50 | margin-bottom: 0; 51 | color: #aeaeae; 52 | 53 | li.L0, li.L1, li.L2, li.L3, li.L5, li.L6, li.L7, li.L8, li.L9 { 54 | list-style-type: decimal; 55 | background-color: inherit; 56 | } 57 | } 58 | 59 | .ngez-code-prettify-header{ 60 | padding: 10px; 61 | } 62 | 63 | .light { 64 | 65 | .ngez-copy-button:hover{ 66 | color: $primary; 67 | } 68 | 69 | .ngez-code-prettify-header{ 70 | background-color: $primary; 71 | color: white; 72 | } 73 | 74 | .ngez-code-prettify-content{ 75 | // border: .5px solid #dbdbdb; 76 | background-color: rgba(241,241,241,.2); 77 | 78 | } 79 | 80 | .ngez-copy-button{ 81 | color: rgb(30, 30, 30); 82 | } 83 | 84 | .ngez-code-prettify{ 85 | background-color: rgba(241,241,241,.2); 86 | 87 | /* string content */ 88 | 89 | .str { 90 | color: #183691; 91 | } 92 | 93 | /* keyword */ 94 | 95 | .kwd { 96 | color: #a71d5d; 97 | } 98 | 99 | /* comment */ 100 | 101 | .com { 102 | color: #969896; 103 | } 104 | 105 | /* type name */ 106 | 107 | .typ { 108 | color: #0086b3; 109 | } 110 | 111 | /* literal value */ 112 | 113 | .lit { 114 | color: #0086b3; 115 | } 116 | 117 | /* punctuation */ 118 | 119 | .pun { 120 | color: #333; 121 | } 122 | 123 | /* lisp open bracket */ 124 | 125 | .opn { 126 | color: #333; 127 | } 128 | 129 | /* lisp close bracket */ 130 | 131 | .clo { 132 | color: #333; 133 | } 134 | 135 | /* markup tag name */ 136 | 137 | .tag { 138 | color: #000080; 139 | } 140 | 141 | /* markup attribute name */ 142 | 143 | .atn { 144 | color: #795da3; 145 | } 146 | 147 | /* markup attribute value */ 148 | 149 | .atv { 150 | color: #183691; 151 | } 152 | 153 | /* declaration */ 154 | 155 | .dec { 156 | color: #333; 157 | } 158 | 159 | /* variable name */ 160 | 161 | .var { 162 | color: #008080; 163 | } 164 | 165 | /* function name */ 166 | 167 | .fun { 168 | color: #900; 169 | } 170 | } 171 | } 172 | 173 | .dark { 174 | 175 | .ngez-copy-button:hover{ 176 | color: $accent; 177 | } 178 | 179 | .ngez-code-prettify-header{ 180 | background-color: $accent; 181 | color: white; 182 | } 183 | 184 | .ngez-code-prettify-content{ 185 | border: 0; 186 | background-color: rgb(30, 30, 30); 187 | 188 | } 189 | 190 | .ngez-copy-button{ 191 | color: white; 192 | } 193 | 194 | .ngez-code-prettify{ 195 | background-color: rgb(30, 30, 30); 196 | 197 | &.nocode { 198 | color: white; 199 | } 200 | 201 | /* string content */ 202 | 203 | .str { 204 | color: #b5bd68; 205 | } 206 | 207 | /* keyword */ 208 | 209 | .kwd { 210 | color: #b294bb; 211 | } 212 | 213 | /* plaintext */ 214 | 215 | .pln { 216 | color: #fff; 217 | } 218 | 219 | /* comment */ 220 | 221 | .com { 222 | color: #969896; 223 | } 224 | 225 | /* type name */ 226 | 227 | .typ { 228 | color: #81a2be; 229 | } 230 | 231 | /* literal value */ 232 | 233 | .lit { 234 | color: #de935f; 235 | } 236 | 237 | /* punctuation */ 238 | 239 | .pun { 240 | color: #c5c8c6; 241 | } 242 | 243 | /* lisp open bracket */ 244 | 245 | .opn { 246 | color: #c5c8c6; 247 | } 248 | 249 | /* lisp close bracket */ 250 | 251 | .clo { 252 | color: #c5c8c6; 253 | } 254 | 255 | /* markup tag name */ 256 | 257 | .tag { 258 | color: #cc6666; 259 | } 260 | 261 | /* markup attribute name */ 262 | 263 | .atn { 264 | color: #de935f; 265 | } 266 | 267 | /* markup attribute value */ 268 | 269 | .atv { 270 | color: #8abeb7; 271 | } 272 | 273 | /* declaration */ 274 | 275 | .dec { 276 | color: #de935f; 277 | } 278 | 279 | /* variable name */ 280 | 281 | .var { 282 | color: #cc6666; 283 | } 284 | 285 | /* function name */ 286 | 287 | .fun { 288 | color: #81a2be; 289 | } 290 | } 291 | } -------------------------------------------------------------------------------- /projects/core/src/file/file-input.directive.ts: -------------------------------------------------------------------------------- 1 | import { DOCUMENT, isPlatformBrowser } from '@angular/common'; 2 | import { 3 | Directive, 4 | ElementRef, 5 | EventEmitter, 6 | forwardRef, 7 | HostListener, 8 | Inject, 9 | Input, 10 | OnChanges, 11 | OnDestroy, 12 | OnInit, 13 | Optional, 14 | Output, 15 | PLATFORM_ID, 16 | Renderer2, 17 | SimpleChanges, 18 | } from '@angular/core'; 19 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; 20 | import { fromEvent, Subscription } from 'rxjs'; 21 | 22 | import { NgEzFileBase } from './file'; 23 | 24 | @Directive({ 25 | selector: ':not([type="file"])[ngezFileInput]', 26 | exportAs: 'ngezFileInput', 27 | providers: [{ 28 | provide: NG_VALUE_ACCESSOR, 29 | useExisting: forwardRef(() => NgEzFileInputDirective), 30 | multi: true 31 | }] 32 | }) 33 | export class NgEzFileInputDirective extends NgEzFileBase implements ControlValueAccessor, OnChanges, OnInit, OnDestroy { 34 | 35 | @Output() selected = new EventEmitter(); 36 | 37 | onChange: Function; 38 | 39 | onTouched: Function; 40 | 41 | isDisabled = false; 42 | 43 | private subscription: Subscription; 44 | 45 | constructor( 46 | private element: ElementRef, 47 | @Inject(PLATFORM_ID) private platformId: Object, 48 | @Optional() @Inject(DOCUMENT) private document: any, 49 | private renderer: Renderer2) { super(); } 50 | 51 | ngOnChanges(changes: SimpleChanges) { 52 | if (!isPlatformBrowser(this.platformId)) return; 53 | 54 | const { currentValue: accept = null, previousValue: prevAccept = null } = changes.accept || {}; 55 | const { currentValue: multiple = null, previousValue: prevMultiple = null } = changes.multiple || {}; 56 | 57 | if ((multiple != prevMultiple) || (accept != prevAccept)) 58 | this.appendFileInput(); 59 | } 60 | 61 | ngOnInit() { 62 | if (!isPlatformBrowser(this.platformId)) return; 63 | 64 | if (!this.fileInput) 65 | this.appendFileInput(); 66 | 67 | this.subscription = fromEvent(this.element.nativeElement, this.isInputOrTextarea() ? 'focus' : 'click') 68 | .subscribe(e => this.browse()); 69 | } 70 | 71 | ngOnDestroy() { 72 | if (this.subscription) 73 | this.subscription.unsubscribe(); 74 | this.removeFileInput(); 75 | } 76 | 77 | @HostListener('blur') 78 | private onBlur() { 79 | if (this.onTouched) 80 | this.onTouched(); 81 | } 82 | 83 | browse() { 84 | if(this.isDisabled) return; 85 | 86 | if (this.isInputOrTextarea()) 87 | this.element.nativeElement.blur(); 88 | 89 | this.fileInput.click(); 90 | } 91 | 92 | clear() { 93 | this.setValueAndUpdate(null); 94 | } 95 | 96 | writeValue(value: any): void { 97 | let file: File | File[] = null; 98 | 99 | if (value) { 100 | if (value instanceof File || (Array.isArray(value) && value.every(value => value instanceof File))) 101 | file = value; 102 | else if (value instanceof FileList) 103 | file = Array.from(value); 104 | else 105 | return console.warn('Expected value of type File, FileList or File[], instead got: ', value); 106 | } 107 | 108 | this.setValue(file); 109 | } 110 | 111 | registerOnChange(fn: (value: any) => {}): void { 112 | this.onChange = fn; 113 | } 114 | 115 | registerOnTouched(fn: () => {}) { 116 | this.onTouched = fn; 117 | } 118 | 119 | setDisabledState(isDisabled: boolean) { 120 | this.renderer.setProperty(this.element.nativeElement, 'disabled', isDisabled); 121 | this.isDisabled = isDisabled; 122 | } 123 | 124 | private setValue(value: File | File[]) { 125 | const text = value ? this.getText(value) : ''; 126 | this.renderer.setProperty(this.element.nativeElement, 'value', text); 127 | } 128 | 129 | private setValueAndUpdate(value: File | FileList) { 130 | const fileValue = value instanceof FileList ? Array.from(value) : value; 131 | if (this.onChange) 132 | this.onChange(fileValue); 133 | this.setValue(fileValue); 134 | this.selected.emit(fileValue); 135 | } 136 | 137 | private appendFileInput() { 138 | if (this.fileInput) 139 | this.clear(); 140 | 141 | this.removeFileInput(); 142 | 143 | this.fileInput = this.createFileInput(); 144 | this.renderer.appendChild(this.document.body, this.fileInput); 145 | this.listener = this.renderer.listen(this.fileInput, 'change', e => { 146 | const files: FileList = e.target.files; 147 | const value = this.multiple ? files : files.item(0); 148 | this.setValueAndUpdate(value); 149 | }); 150 | } 151 | 152 | private createFileInput(): HTMLInputElement { 153 | const input = this.renderer.createElement('input'); 154 | this.renderer.setAttribute(input, 'type', 'file'); 155 | this.renderer.setAttribute(input, 'aria-hidden', 'true'); 156 | this.renderer.setProperty(input, 'hidden', true); 157 | this.renderer.setProperty(input, 'multiple', this.multiple ? true : false); 158 | if (this.accept) 159 | this.renderer.setAttribute(input, 'accept', this.accept); 160 | return input; 161 | } 162 | 163 | private removeFileInput() { 164 | if (this.fileInput) 165 | this.renderer.removeChild(this.document.body, this.fileInput); 166 | if (this.listener) 167 | this.listener(); 168 | } 169 | 170 | private getText(value: File | File[]): string { 171 | const files = value instanceof File ? [value] : value; 172 | return files.reduce((text, file, index) => `${text}${index > 0 ? ', ' : ''}${file.name}`, ''); 173 | } 174 | 175 | private isInputOrTextarea(): boolean { 176 | const element = this.element.nativeElement; 177 | return element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement; 178 | } 179 | } --------------------------------------------------------------------------------