diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..e20a9680 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..1025ddc1 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,200 @@ +{ + "env": { + "browser": 2, + "node": 2, + "mocha": 2, + "es6": 2 + }, + "ecmaFeatures": { + "modules": true + }, + "globals": { + "_": 2, + "$": 2, + "angular": 2, + "jQuery": 2 + }, + "rules": { + // Possible Errors + "comma-dangle": 2, + // no-comma-dangle - (deprecated) + "no-cond-assign": [2, "always"], + "no-console": 2, + "no-constant-condition": 2, + "no-control-regex": 2, + "no-debugger": 2, + "no-dupe-keys": 2, + "no-dupe-args": 2, + "no-duplicate-case": 2, + "no-empty-character-class": 2, + // no-empty-class - (deprecated) + "no-empty": 2, + "no-ex-assign": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 2, + "no-extra-semi": 2, + "no-func-assign": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-negated-in-lhs": 2, + "no-obj-calls": 2, + "no-regex-spaces": 2, + "no-sparse-arrays": 2, + "no-unreachable": 2, + "use-isnan": 2, + "valid-jsdoc": 2, + "valid-typeof": 2, + "no-unexpected-multiline": 2, + + // Best Practices + // not sure + "accessor-pairs": [2, {"getWithoutSet": false, "setWithoutGet": true}], + "block-scoped-var": 2, + "complexity": [2, 6], + "consistent-return": 2, + "curly": 2, + "default-case": 2, + "dot-notation": 2, + // not good for chain calls, but not for properties + "dot-location": [2, "property"], + "eqeqeq": 2, + "guard-for-in": 2, + "no-alert": 2, + "no-caller": 2, + "no-div-regex": 2, + "no-else-return": 2, + "no-empty-label": 2, + "no-eq-null": 2, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-iterator": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-loop-func": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-native-reassign": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-param-reassign": 2, + "no-process-env": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-throw-literal": 2, + "no-unused-expressions": 2, + "no-void": 2, + "no-warning-comments": [1, { "terms": ["todo", "fixme"], "location": "anywhere" }], + "no-with": 2, + "radix": 2, + "vars-on-top": 0, + "wrap-iife": [2, "inside"], + "yoda": [2, "never"], + + // Strict Mode + "strict": [2, "global"], + + // Variables + "no-catch-shadow": 2, + "no-delete-var": 2, + "no-label-var": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 2, + "no-unused-vars": 2, + "no-use-before-define": [2, "nofunc"], + + // Node.js + "handle-callback-err": [2, "^.*(e|E)rr" ], + "no-mixed-requires": [2, true], + "no-new-require": 2, + "no-path-concat": 2, + "no-process-exit": 2, + "no-sync": 2, + + // Stylistic Issues + "array-bracket-spacing": [2, "never"], + "brace-style": [2, "1tbs", { "allowSingleLine": true }], + "camelcase": 2, + "comma-spacing": [2, {"before": false, "after": true}], + "comma-style": [2, "last"], + "computed-property-spacing": [2, "never"], + "consistent-this": [2, "self"], + // not sure + "eol-last": 0, + "func-names": 0, + "func-style": [2, "declaration"], + "indent": [2, 2], + "key-spacing": [2, { "beforeColon": false, "afterColon": true}], + "max-nested-callbacks": [2, 3], + "new-cap": [2, {"newIsCap": true, "capIsNew": true, "capIsNewExceptions":[ + "ObjectId", + "Object", + "Function", + "Number", + "String", + "Boolean", + "Date", + "Array", + "Symbol", + "RegExp" + ]}], + "new-parens": 2, + "newline-after-var": 0, + "no-array-constructor": 2, + "no-inline-comments": 2, + "no-lonely-if": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multiple-empty-lines": [2, {"max": 1}], + "no-nested-ternary": 2, + "no-new-object": 2, + "no-spaced-func": 2, + "no-ternary": 0, + "no-trailing-spaces": 2, + "no-underscore-dangle": 2, + "no-unneeded-ternary": 2, + "object-curly-spacing": [2, "never"], + "one-var": [2, { + "var": "never", // Exactly one var declaration per function + "let": "never", // Exactly one let declaration per block + "const": "never" // Exactly one declarator per const declaration per block + }], + "operator-assignment": [1, "always"], + "operator-linebreak": [2, "after"], + "padded-blocks": [2, "never"], + "quote-props": [2, "as-needed"], + "quotes": [2, "single", "avoid-escape"], + "semi": [2, "always"], + "semi-spacing": 2, + // "sort-vars": [1, { "ignoreCase": true }], + "space-after-keywords": [2, "always" ], + "space-before-blocks": [2, "always"], + "space-in-parens": [2, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "spaced-comment": 0, + "wrap-regexp": 0, + + // Legacy + "max-len": [1, 120, 4], + // todo: apply max-params + "max-params": [0, 3], + // todo: apply max-statements + "max-statements": [2, 30], + "no-bitwise": 2 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b5d688c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +/node_modules +/demo/lib +npm-debug.log + +# type script artifacts +/typings + +# WebStorm +.idea + +# ignore build and dist for now +/build +/dist + +/demo/**/*.js +/demo/**/*.js.map +/components/**/*.js +/components/**/*.js.map + +/logs diff --git a/README.md b/README.md index 1ae461e8..b584d169 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,42 @@ # ng2-select -Angular2 based replacement for select boxes -## Placeholder for `ng2-select` angular2 module +Native Select Angular2 component -Please read central `ng2` modules [readme](https://github.com/valor-software/ng2-plans) for details, plans and approach +## Quick start + +1. A recommended way to install ***ng2-select*** is through [npm](https://www.npmjs.com/search?q=ng2-select) package manager using the following command: + + `npm i ng2-select --save` + + Alternatively, you can [download it in a ZIP file](https://github.com/valor-software/ng2-select/archive/master.zip). + +2. More information regardidocng using of ***ng2-select*** is located in + [demo](http://valor-software.github.io/ng2-select/) and [demo sources](https://github.com/valor-software/ng2-select/tree/master/demo). + +## API + +### Properties + + - `items` - (`Array`) - Array of items from which to select. Should be an array of objects with `id` and `text` properties. + As convenience, you may also pass an array of strings, in which case the same string is used for both the ID and the text. + Items may be nested by adding a `children` property to any item, whose value should be another array of items. Items that have children may omit having an ID. + If `items` are specified, all items are expected to be available locally and all selection operations operate on this local array only. + If omitted, items are not available locally, and the `query` option should be provided to fetch data. + - `allowClear` (`?boolean=false`) (*not yet supported*) - Set to `true` to allow the selection to be cleared. This option only applies to single-value inputs. + - `placeholder` (`?string=''`) - Placeholder text to display when the element has no focus and selected items. + - `multiple` - (`?boolean=false`) - Mode of this component. If set `true` user can select more than one option. + - `showSearchInputInDropdown` - (`?boolean=true`) (*not yet supported*) - Set to `false` to remove the search input used in dropdowns. + This option only applies to single-value inputs, as multiple-value inputs don't have the search input in the dropdown to begin with. + +# Troubleshooting + +Please follow this guidelines when reporting bugs and feature requests: + +1. Use [GitHub Issues](https://github.com/valor-software/ng2-select/issues) board to report bugs and feature requests (not our email address) +2. Please **always** write steps to reproduce the error. That way we can focus on fixing the bug, not scratching our heads trying to reproduce it. + +Thanks for understanding! + +### License + +The MIT License (see the [LICENSE](https://github.com/valor-software/ng2-select/blob/master/LICENSE) file for the full text) \ No newline at end of file diff --git a/components/index.ts b/components/index.ts new file mode 100644 index 00000000..91842f02 --- /dev/null +++ b/components/index.ts @@ -0,0 +1,4 @@ +/// + +export * from './select/select'; +export * from './ng2-select-config'; diff --git a/components/module.ts b/components/module.ts new file mode 100644 index 00000000..c7eb1995 --- /dev/null +++ b/components/module.ts @@ -0,0 +1,3 @@ +declare module 'ng2-select' { + export * from 'index'; +} diff --git a/components/ng2-select-config.ts b/components/ng2-select-config.ts new file mode 100644 index 00000000..fe42301b --- /dev/null +++ b/components/ng2-select-config.ts @@ -0,0 +1,16 @@ +export enum Ng2SelectTheme {BS3 = 1, BS4 = 2} + +export class Ng2SelectConfig { + private static _theme: Ng2SelectTheme; + static get theme():Ng2SelectTheme { + // hack as for now + let w: any = window; + if (w && w.__theme === 'bs4') { + return Ng2SelectTheme.BS4; + } + return (this._theme || Ng2SelectTheme.BS3); + } + static set theme(v:Ng2SelectTheme){ + this._theme = v; + } +} diff --git a/components/select/common.css b/components/select/common.css new file mode 100644 index 00000000..d099f7db --- /dev/null +++ b/components/select/common.css @@ -0,0 +1,260 @@ + +/* Style when highlighting a search. */ +.ui-select-highlight { + font-weight: bold; +} + +.ui-select-offscreen { + clip: rect(0 0 0 0) !important; + width: 1px !important; + height: 1px !important; + border: 0 !important; + margin: 0 !important; + padding: 0 !important; + overflow: hidden !important; + position: absolute !important; + outline: 0 !important; + left: 0px !important; + top: 0px !important; +} + + +.ui-select-choices-row:hover { + background-color: #f5f5f5; +} + +/* Select2 theme */ + +/* Mark invalid Select2 */ +.ng-dirty.ng-invalid > a.select2-choice { + border-color: #D44950; +} + +.select2-result-single { + padding-left: 0; +} + +.select2-locked > .select2-search-choice-close{ + display:none; +} + +.select-locked > .ui-select-match-close{ + display:none; +} + +body > .select2-container.open { + z-index: 9999; /* The z-index Select2 applies to the select2-drop */ +} + +/* Handle up direction Select2 */ +.ui-select-container[theme="select2"].direction-up .ui-select-match { + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.ui-select-container[theme="select2"].direction-up .ui-select-dropdown { + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + + border-top-width: 1px; /* FIXME hardcoded value :-/ */ + border-top-style: solid; + + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); + + margin-top: -4px; /* FIXME hardcoded value :-/ */ +} +.ui-select-container[theme="select2"].direction-up .ui-select-dropdown .select2-search { + margin-top: 4px; /* FIXME hardcoded value :-/ */ +} +.ui-select-container[theme="select2"].direction-up.select2-dropdown-open .ui-select-match { + border-bottom-color: #5897fb; +} + +/* Selectize theme */ + +/* Helper class to show styles when focus */ +.selectize-input.selectize-focus{ + border-color: #007FBB !important; +} + +/* Fix input width for Selectize theme */ +.selectize-control > .selectize-input > input { + width: 100%; +} + +/* Fix dropdown width for Selectize theme */ +.selectize-control > .selectize-dropdown { + width: 100%; +} + +/* Mark invalid Selectize */ +.ng-dirty.ng-invalid > div.selectize-input { + border-color: #D44950; +} + +/* Handle up direction Selectize */ +.ui-select-container[theme="selectize"].direction-up .ui-select-dropdown { + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); + + margin-top: -2px; /* FIXME hardcoded value :-/ */ +} + +/* Bootstrap theme */ + +/* Helper class to show styles when focus */ +.btn-default-focus { + color: #333; + background-color: #EBEBEB; + border-color: #ADADAD; + text-decoration: none; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +} + +.ui-select-bootstrap .ui-select-toggle { + position: relative; +} + +.ui-select-bootstrap .ui-select-toggle > .caret { + position: absolute; + height: 10px; + top: 50%; + right: 10px; + margin-top: -2px; +} + +/* Fix Bootstrap dropdown position when inside a input-group */ +.input-group > .ui-select-bootstrap.dropdown { + /* Instead of relative */ + position: static; +} + +.input-group > .ui-select-bootstrap > input.ui-select-search.form-control { + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group > .ui-select-bootstrap > input.ui-select-search.form-control.direction-up { + border-radius: 4px !important; /* FIXME hardcoded value :-/ */ + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} + +.ui-select-bootstrap > .ui-select-match > .btn{ + /* Instead of center because of .btn */ + text-align: left !important; +} + +.ui-select-bootstrap > .ui-select-match > .caret { + position: absolute; + top: 45%; + right: 15px; +} + +/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */ +.ui-select-bootstrap > .ui-select-choices { + width: 100%; + height: auto; + max-height: 200px; + overflow-x: hidden; + margin-top: -1px; +} + +body > .ui-select-bootstrap.open { + z-index: 1000; /* Standard Bootstrap dropdown z-index */ +} + +.ui-select-multiple.ui-select-bootstrap { + height: auto; + padding: 3px 3px 0 3px; +} + +.ui-select-multiple.ui-select-bootstrap input.ui-select-search { + background-color: transparent !important; /* To prevent double background when disabled */ + border: none; + outline: none; + height: 1.666666em; + margin-bottom: 3px; +} + +.ui-select-multiple.ui-select-bootstrap .ui-select-match .close { + font-size: 1.6em; + line-height: 0.75; +} + +.ui-select-multiple.ui-select-bootstrap .ui-select-match-item { + outline: 0; + margin: 0 3px 3px 0; +} + +.ui-select-multiple .ui-select-match-item { + position: relative; +} + +.ui-select-multiple .ui-select-match-item.dropping-before:before { + content: ""; + position: absolute; + top: 0; + right: 100%; + height: 100%; + margin-right: 2px; + border-left: 1px solid #428bca; +} + +.ui-select-multiple .ui-select-match-item.dropping-after:after { + content: ""; + position: absolute; + top: 0; + left: 100%; + height: 100%; + margin-left: 2px; + border-right: 1px solid #428bca; +} + +.ui-select-bootstrap .ui-select-choices-row>a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} + +.ui-select-bootstrap .ui-select-choices-row>a:hover, .ui-select-bootstrap .ui-select-choices-row>a:focus { + text-decoration: none; + color: #262626; + background-color: #f5f5f5; +} + +.ui-select-bootstrap .ui-select-choices-row.active>a { + color: #fff; + text-decoration: none; + outline: 0; + background-color: #428bca; +} + +.ui-select-bootstrap .ui-select-choices-row.disabled>a, +.ui-select-bootstrap .ui-select-choices-row.active.disabled>a { + color: #777; + cursor: not-allowed; + background-color: #fff; +} + +/* fix hide/show angular animation */ +.ui-select-match.ng-hide-add, +.ui-select-search.ng-hide-add { + display: none !important; +} + +/* Mark invalid Bootstrap */ +.ui-select-bootstrap.ng-dirty.ng-invalid > button.btn.ui-select-match { + border-color: #D44950; +} + +/* Handle up direction Bootstrap */ +.ui-select-container[theme="bootstrap"].direction-up .ui-select-dropdown { + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); +} diff --git a/components/select/readme.md b/components/select/readme.md new file mode 100644 index 00000000..a0466fd8 --- /dev/null +++ b/components/select/readme.md @@ -0,0 +1,39 @@ +### Usage +```typescript +import {select} from 'ng2-select'; +``` + +### Annotations +```typescript +// class Select +@Component({ + selector: 'ng2-select', + properties: [ + 'allowClear', + 'placeholder', + 'items', + 'multiple', + 'showSearchInputInDropdown'] +}) +``` + +### Select properties + + - `items` - (`Array`) - Array of items from which to select. Should be an array of objects with `id` and `text` properties. + As convenience, you may also pass an array of strings, in which case the same string is used for both the ID and the text. + Items may be nested by adding a `children` property to any item, whose value should be another array of items. Items that have children may omit having an ID. + If `items` are specified, all items are expected to be available locally and all selection operations operate on this local array only. + If omitted, items are not available locally, and the `query` option should be provided to fetch data. + - `data` (`?Array`) - Initial selection data to set. This should be an object with `id` and `text` properties in the case of input type 'Single', + or an array of such objects otherwise. This option is mutually exclusive with value. + - `allowClear` (`?boolean=false`) (*not yet supported*) - Set to `true` to allow the selection to be cleared. This option only applies to single-value inputs. + - `placeholder` (`?string=''`) - Placeholder text to display when the element has no focus and selected items. + - `multiple` - (`?boolean=false`) - Mode of this component. If set `true` user can select more than one option. + - `showSearchInputInDropdown` - (`?boolean=true`) - Set to `false` to remove the search input used in dropdowns. + This option only applies to single-value inputs, as multiple-value inputs don't have the search input in the dropdown to begin with. + +### Select events + + - `data` - it fires during all events of this component; returns `Array` - current selected data + - `selected` - it fires after a new option selected; returns object with `id` and `text` properties that describes a new option. + - `removed` - it fires after an option removed; returns object with `id` and `text` properties that describes a removed option. diff --git a/components/select/select-item.ts b/components/select/select-item.ts new file mode 100644 index 00000000..7659d751 --- /dev/null +++ b/components/select/select-item.ts @@ -0,0 +1,42 @@ +export class SelectItem { + public id:string; + public text:string; + public children:Array; + public parent:SelectItem; + + constructor(source:any) { + if (typeof source === 'string') { + this.id = this.text = source; + } + + if (typeof source === 'object') { + this.id = source.id || source.text; + this.text = source.text; + + if (source.children && source.text) { + this.children = source.children.map(c => { + let r:SelectItem = new SelectItem(c); + r.parent = this; + return r; + }); + this.text = source.text; + } + + if (source.submenu) { + this.subMenu = new SelectSubMenu(source.submenu, this); + } + } + } + + public hasChildren():boolean { + return this.children && this.children.length > 0; + } + + public getSimilar():SelectItem { + let r:SelectItem = new SelectItem(false); + r.id = this.id; + r.text = this.text; + r.parent = this.parent; + return r; + } +} diff --git a/components/select/select.ts b/components/select/select.ts new file mode 100644 index 00000000..f08e053f --- /dev/null +++ b/components/select/select.ts @@ -0,0 +1,391 @@ +/// + +import { + Component, View, OnInit, OnDestroy, + Directive, ViewEncapsulation, Self, + EventEmitter, ElementRef, ComponentRef, + DynamicComponentLoader, + CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass, NgStyle +} from 'angular2/angular2'; + +import {bind, forwardRef, ResolvedBinding, Injector} from 'angular2/di'; + +import {SelectItem} from './select-item'; + +let cssCommon = require('./common.css'); + +function getIndex(a:Array, v:SelectItem):number { + for (let i = 0; i < a.length; i++) { + if (a[i].id === v.id) { + return i; + } + } + + return -1; +} + +@Component({ + selector: 'ng2-select', + properties: [ + 'allowClear', + 'placeholder', + 'initData:data', + 'items', + 'multiple', + 'showSearchInputInDropdown'], + events: ['selected', 'removed', 'data'] +}) +@View({ + template: ` + + `, + styles: [cssCommon], + directives: [CORE_DIRECTIVES, FORM_DIRECTIVES] +}) +export class Select implements OnInit, OnDestroy { + private data:EventEmitter = new EventEmitter(); + private multiple:boolean = false; + private selected:EventEmitter = new EventEmitter(); + private removed:EventEmitter = new EventEmitter(); + private allowClear:boolean = false; + private placeholder:string = ''; + private initData:Array = []; + private _items:Array = []; + private options:Array = []; + private itemObjects:Array = []; + private active:Array = []; + private activeOption:SelectItem; + private showSearchInputInDropdown:boolean = true; + private offSideClickHandler:any; + private inputMode:boolean = false; + private optionsOpened:boolean = false; + + constructor(public element:ElementRef) { + } + + private focusToInput(value:string = '') { + setTimeout(() => { + let el = this.element.nativeElement.querySelector('div.ui-select-container > input'); + el.focus(); + el.value = value; + }, 0); + } + + private f() { + this.inputMode = !this.inputMode; + if (this.inputMode === true) { + this.focusToInput(); + this.open(); + } + } + + private ff(e:any) { + if (this.inputMode === true) { + return; + } + + if (e.keyCode === 46) { + e.preventDefault(); + this.inputEvent(e); + return; + } + + if (e.keyCode === 9 || e.keyCode === 8 || e.keyCode === 13 || + e.keyCode === 27 || (e.keyCode >= 37 && e.keyCode <= 40)) { + e.preventDefault(); + return; + } + + this.inputMode = true; + let value = String + .fromCharCode(96 <= e.keyCode && e.keyCode <= 105 ? e.keyCode - 48 : e.keyCode) + .toLowerCase(); + this.focusToInput(value); + this.open(); + e.srcElement.value = value; + this.inputEvent(e); + } + + private open() { + this.options = this.itemObjects + .filter(option => (this.multiple === false || + this.multiple === true && !this.active.find(o => option.text === o.text))); + + if (this.options.length > 0) { + this.activeOption = this.options[0]; + } + + this.optionsOpened = true; + } + + private get items():Array { + return this._items; + } + + private set items(value:Array) { + this._items = value; + this.itemObjects = this._items.map((item:any) => new SelectItem(item)); + } + + onInit() { + this.offSideClickHandler = this.getOffSideClickHandler(this); + document.addEventListener('click', this.offSideClickHandler); + + if (this.initData) { + this.active = this.initData.map(d => new SelectItem(d)); + this.data.next(this.active); + } + } + + onDestroy() { + document.removeEventListener('click', this.offSideClickHandler); + this.offSideClickHandler = null; + } + + private getOffSideClickHandler(context:any) { + return function (e:any) { + if (e.target && e.target.nodeName === 'INPUT' + && e.target.className && e.target.className.indexOf('ui-select') >= 0) { + return; + } + + if (e.srcElement && e.srcElement.className && + e.srcElement.className.indexOf('ui-select') >= 0) { + if (e.target.nodeName !== 'INPUT') { + context.f(); + } + return; + } + + context.inputMode = false; + context.optionsOpened = false; + }; + } + + public remove(item:SelectItem) { + if (this.multiple === true && this.active) { + let index = this.active.indexOf(item); + this.active.splice(index, 1); + this.data.next(this.active); + this.doEvent('removed', item); + } + + if (this.multiple === false) { + this.active = []; + this.data.next(this.active); + this.doEvent('removed', item); + } + } + + public doEvent(type:string, value:any) { + if (this[type] && value) { + this[type].next(value); + } + } + + private _getActiveIndex():number { + return this.options.indexOf(this.activeOption); + } + + private _ensureHighlightVisible() { + let container = this.element.nativeElement.children[0].querySelector('.ui-select-choices-content'); + if (!container) { + return; + } + + let choices = container.querySelectorAll('.ui-select-choices-row'); + if (choices.length < 1) { + return; + } + + if (this._getActiveIndex() < 0) { + return; + } + + let highlighted:any = choices[this._getActiveIndex()]; + if (!highlighted) { + return; + } + + let posY:number = highlighted.offsetTop + highlighted.clientHeight - container.scrollTop; + let height:number = container.offsetHeight; + + if (posY > height) { + container.scrollTop += posY - height; + } else if (posY < highlighted.clientHeight) { + // if (ctrl.isGrouped && ctrl.activeIndex === 0) + // container[0].scrollTop = 0; //To make group header visible when going all the way up + // else + container.scrollTop -= highlighted.clientHeight - posY; + } + } + + private hideOptions() { + this.inputMode = false; + this.optionsOpened = false; + } + + public inputEvent(e:any, isUpMode:boolean = false) { + // esc and tab + if (!isUpMode && (e.keyCode === 27 || e.keyCode === 9)) { + this.hideOptions(); + this.element.nativeElement.children[0].focus(); + e.preventDefault(); + return; + } + + // del + if (!isUpMode && e.keyCode === 46) { + if (this.active.length > 0) { + this.remove(this.active[this.active.length - 1]); + } + e.preventDefault(); + } + + // left + if (!isUpMode && e.keyCode === 37 && this.items.length > 0) { + this.activeOption = this.options[0]; + this._ensureHighlightVisible(); + e.preventDefault(); + return; + } + + // right + if (!isUpMode && e.keyCode === 39 && this.items.length > 0) { + this.activeOption = this.options[this.options.length - 1]; + this._ensureHighlightVisible(); + e.preventDefault(); + return; + } + + // up + if (!isUpMode && e.keyCode === 38) { + let index = this.options.indexOf(this.activeOption); + this.activeOption = this.options[index - 1 < 0 ? this.options.length - 1 : index - 1]; + this._ensureHighlightVisible(); + e.preventDefault(); + return; + } + + // down + if (!isUpMode && e.keyCode === 40) { + let index = this.options.indexOf(this.activeOption); + this.activeOption = this.options[index + 1 > this.options.length - 1 ? 0 : index + 1]; + this._ensureHighlightVisible(); + e.preventDefault(); + return; + } + + // enter + if (!isUpMode && e.keyCode === 13) { + this.selectActiveMatch(); + e.preventDefault(); + return; + } + + if (e.srcElement) { + let query:RegExp = new RegExp(e.srcElement.value, 'ig'); + let {options, isActiveAvailable} = this.filter(query); + this.options = options; + + if (this.options.length > 0 && !isActiveAvailable) { + this.activeOption = this.options[0]; + } + + this._ensureHighlightVisible(); + } + } + + private filter(query:RegExp):any { + let options = this.itemObjects + .filter(option => query.test(option.text) && + (this.multiple === false || + (this.multiple === true && + this.active.indexOf(option) < 0))); + let isActiveAvailable = getIndex(options, this.activeOption) >= 0; + return {options, isActiveAvailable}; + } + + private selectActiveMatch() { + this.selectMatch(this.activeOption); + } + + private selectMatch(value:SelectItem, e:Event = null) { + if (e) { + e.stopPropagation(); + e.preventDefault(); + } + + if (this.options.length <= 0) { + return; + } + + if (this.multiple === true) { + this.active.push(value); + this.data.next(this.active); + } + + if (this.multiple === false) { + this.active[0] = value; + this.data.next(this.active[0]); + } + + this.doEvent('selected', value); + this.hideOptions(); + this.element.nativeElement.children[0].focus(); + } + + private selectActive(value:SelectItem) { + this.activeOption = value; + } + + private isActive(value:SelectItem):boolean { + return this.activeOption.text === value.text; + } + + public hasSearchInput():boolean { + if (this.multiple === true) { + return false; + } + + return this.showSearchInputInDropdown; + } +} + +export const select:Array = [Select]; diff --git a/demo/assets/css/glyphicons.css b/demo/assets/css/glyphicons.css new file mode 100644 index 00000000..6e518b9c --- /dev/null +++ b/demo/assets/css/glyphicons.css @@ -0,0 +1,805 @@ +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} diff --git a/demo/assets/css/prism-okaidia.css b/demo/assets/css/prism-okaidia.css new file mode 100644 index 00000000..0fac6828 --- /dev/null +++ b/demo/assets/css/prism-okaidia.css @@ -0,0 +1,119 @@ +/** + * okaidia theme for JavaScript, CSS and HTML + * Loosely based on Monokai textmate theme by http://www.monokai.nl/ + * @author ocodia + */ + +code[class*="language-"], +pre[class*="language-"] { + color: #f8f8f2; + text-shadow: 0 1px rgba(0, 0, 0, 0.3); + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; + border-radius: 0.3em; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #272822; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #f8f8f2; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #f92672; +} + +.token.boolean, +.token.number { + color: #ae81ff; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #a6e22e; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string, +.token.variable { + color: #f8f8f2; +} + +.token.atrule, +.token.attr-value, +.token.function { + color: #e6db74; +} + +.token.keyword { + color: #66d9ef; +} + +.token.regex, +.token.important { + color: #fd971f; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/demo/assets/css/style.css b/demo/assets/css/style.css new file mode 100644 index 00000000..224171bb --- /dev/null +++ b/demo/assets/css/style.css @@ -0,0 +1,278 @@ +/*! + * Bootstrap Docs (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under the Creative Commons Attribution 3.0 Unported License. For + * details, see http://creativecommons.org/licenses/by/3.0/. + */ + +.h1, .h2, .h3, h1, h2, h3 { + margin-top: 20px; + margin-bottom: 10px; +} + +.h1, h1 { + font-size: 36px; +} + +.btn-group-lg > .btn, .btn-lg { + font-size: 18px; +} + +section { + padding-top: 30px; +} + +.bd-pageheader { + margin-top: 51px; +} + +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} + +.navbar-default .navbar-nav > li > a { + color: #777; +} + +.navbar { + padding: 0; +} + +.navbar-nav .nav-item { + margin-left: 0 !important; +} + +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} + +.nav .navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; + margin-right: 0 !important; +} + +.navbar-brand { + color: #777; + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} + +.navbar-toggler { + margin-top: 8px; + margin-right: 15px; +} + +.navbar-default .navbar-nav > li > a:focus, .navbar-default .navbar-nav > li > a:hover { + color: #333; + background-color: transparent; +} + +.bd-pageheader, .bs-docs-masthead { + position: relative; + padding: 30px 0; + color: #cdbfe3; + text-align: center; + text-shadow: 0 1px 0 rgba(0, 0, 0, .1); + background-color: #6f5499; + background-image: -webkit-gradient(linear, left top, left bottom, from(#563d7c), to(#6f5499)); + background-image: -webkit-linear-gradient(top, #563d7c 0, #6f5499 100%); + background-image: -o-linear-gradient(top, #563d7c 0, #6f5499 100%); + background-image: linear-gradient(to bottom, #563d7c 0, #6f5499 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#563d7c', endColorstr='#6F5499', GradientType=0); + background-repeat: repeat-x; +} + +.bd-pageheader { + margin-bottom: 40px; + font-size: 20px; +} + +.bd-pageheader h1 { + margin-top: 0; + color: #fff; +} + +.bd-pageheader p { + margin-bottom: 0; + font-weight: 300; + line-height: 1.4; +} + +.bd-pageheader .btn { + margin: 10px 0; +} + +.scrollable-menu .nav-link { + color: #337ab7; + font-size: 14px; +} + +.scrollable-menu .nav-link:hover { + color: #23527c; + background-color: #eee; +} + +@media (min-width: 992px) { + .bd-pageheader h1, .bd-pageheader p { + margin-right: 380px; + } +} + +@media (min-width: 768px) { + .bd-pageheader { + padding-top: 60px; + padding-bottom: 60px; + font-size: 24px; + text-align: left; + } + + .bd-pageheader h1 { + font-size: 60px; + line-height: 1; + } + + .navbar-nav > li > a.nav-link { + padding-top: 15px; + padding-bottom: 15px; + font-size: 14px; + } + + .navbar > .container .navbar-brand, .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} + +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } + + .navbar .container { + width: 100%; + max-width: 100%; + } + .navbar .container, + .navbar .container .navbar-header { + padding: 0; + margin: 0; + } +} + +@media (max-width: 400px) { + code, kbd { + font-size: 60%; + } +} + +/** + * VH and VW units can cause issues on iOS devices: http://caniuse.com/#feat=viewport-units + * + * To overcome this, create media queries that target the width, height, and orientation of iOS devices. + * It isn't optimal, but there is really no other way to solve the problem. In this example, I am fixing + * the height of element '.scrollable-menu' —which is a full width and height cover image. + * + * iOS Resolution Quick Reference: http://www.iosres.com/ + */ + +.scrollable-menu { + height: 90vh !important; + width: 100vw; + overflow-x: hidden; + padding: 0 0 20px; +} + +/** + * iPad with portrait orientation. + */ +@media all and (device-width: 768px) and (device-height: 1024px) and (orientation: portrait) { + .scrollable-menu { + height: 1024px !important; + } +} + +/** + * iPad with landscape orientation. + */ +@media all and (device-width: 768px) and (device-height: 1024px) and (orientation: landscape) { + .scrollable-menu { + height: 768px !important; + } +} + +/** + * iPhone 5 + * You can also target devices with aspect ratio. + */ +@media screen and (device-aspect-ratio: 40/71) { + .scrollable-menu { + height: 500px !important; + } +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} + +.navbar-toggle:focus { + outline: 0 +} + +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px +} + +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px +} + +pre { + white-space: pre-wrap; /* CSS 3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +.chart-legend, .bar-legend, .line-legend, .pie-legend, .radar-legend, .polararea-legend, .doughnut-legend { + list-style-type: none; + margin-top: 5px; + text-align: center; + -webkit-padding-start: 0; + -moz-padding-start: 0; + padding-left: 0 +} + +.chart-legend li, .bar-legend li, .line-legend li, .pie-legend li, .radar-legend li, .polararea-legend li, .doughnut-legend li { + display: inline-block; + white-space: nowrap; + position: relative; + margin-bottom: 4px; + border-radius: 5px; + padding: 2px 8px 2px 28px; + font-size: smaller; + cursor: default +} + +.chart-legend li span, .bar-legend li span, .line-legend li span, .pie-legend li span, .radar-legend li span, .polararea-legend li span, .doughnut-legend li span { + display: block; + position: absolute; + left: 0; + top: 0; + width: 20px; + height: 20px; + border-radius: 5px +} \ No newline at end of file diff --git a/demo/assets/fonts/glyphicons-halflings-regular.eot b/demo/assets/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..b93a4953 Binary files /dev/null and b/demo/assets/fonts/glyphicons-halflings-regular.eot differ diff --git a/demo/assets/fonts/glyphicons-halflings-regular.svg b/demo/assets/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..94fb5490 --- /dev/null +++ b/demo/assets/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/assets/fonts/glyphicons-halflings-regular.ttf b/demo/assets/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..1413fc60 Binary files /dev/null and b/demo/assets/fonts/glyphicons-halflings-regular.ttf differ diff --git a/demo/assets/fonts/glyphicons-halflings-regular.woff b/demo/assets/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..9e612858 Binary files /dev/null and b/demo/assets/fonts/glyphicons-halflings-regular.woff differ diff --git a/demo/assets/fonts/glyphicons-halflings-regular.woff2 b/demo/assets/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 00000000..64539b54 Binary files /dev/null and b/demo/assets/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/demo/components/select-section.ts b/demo/components/select-section.ts new file mode 100644 index 00000000..9eee6d72 --- /dev/null +++ b/demo/components/select-section.ts @@ -0,0 +1,99 @@ +/// + +import {Component, View, CORE_DIRECTIVES, NgNonBindable} from 'angular2/angular2'; + +import {tabs} from 'ng2-bootstrap'; +import {SingleDemo} from './select/single-demo'; +// import {MultipleDemo} from './select/multiple-demo'; +// import {ChildrenDemo} from './select/children-demo'; +// import {MenuDemo} from './select/menu-demo'; + +let name = 'Select'; +let src = 'https://github.com/valor-software/ng2-select/blob/master/components/select/select.ts'; +// webpack html imports +let doc = require('../../components/select/readme.md'); + +let tabDesc:Array = [ + { + heading: 'Single', + ts: require('!!prismjs?lang=typescript!./select/single-demo.ts'), + html: require('!!prismjs?lang=markup!./select/single-demo.html') + }/*, + { + heading: 'Multiple', + ts: require('!!prismjs?lang=typescript!./select/multiple-demo.ts'), + html: require('!!prismjs?lang=markup!./select/multiple-demo.html') + }, + { + heading: 'Children', + ts: require('!!prismjs?lang=typescript!./select/children-demo.ts'), + html: require('!!prismjs?lang=markup!./select/children-demo.html') + }, + { + heading: 'Menu', + ts: require('!!prismjs?lang=typescript!./select/menu-demo.ts'), + html: require('!!prismjs?lang=markup!./select/menu-demo.html') + }*/ +]; + +let tabsContent:string = ``; +tabDesc.forEach(desc => { + tabsContent += ` + +
+ + <${desc.heading.toLowerCase()}-demo *ng-if="currentHeading === '${desc.heading}'"> + +
+ +
+ + +
+
${desc.html}
+
+
+ +
+
${desc.ts}
+
+
+
+
+
+
+ `; +}); + +@Component({ + selector: 'select-section' +}) +@View({ + template: ` +
+
+ + + ${tabsContent} + + +
+ +
+

API

+
${doc}
+
+
+ `, + // directives: [SingleDemo, MultipleDemo, ChildrenDemo, MenuDemo, tabs, CORE_DIRECTIVES, NgNonBindable] + directives: [SingleDemo, tabs, CORE_DIRECTIVES, NgNonBindable] +}) +export class SelectSection { + private currentHeading:string = 'Single'; + + private select(e) { + if (e.heading) { + this.currentHeading = e.heading; + } + } +} diff --git a/demo/components/select/children-demo.html b/demo/components/select/children-demo.html new file mode 100644 index 00000000..4016f1ae --- /dev/null +++ b/demo/components/select/children-demo.html @@ -0,0 +1,10 @@ +
+

Select a city by country

+ +

{{value.text}}

+
diff --git a/demo/components/select/children-demo.ts b/demo/components/select/children-demo.ts new file mode 100644 index 00000000..83f97cac --- /dev/null +++ b/demo/components/select/children-demo.ts @@ -0,0 +1,210 @@ +/// + +import { + Component, View, + CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass +} from 'angular2/angular2'; + +import {select} from '../../../components/index'; + +// webpack html imports +let template = require('./children-demo.html'); + +@Component({ + selector: 'children-demo' +}) +@View({ + template: template, + directives: [select, NgClass, CORE_DIRECTIVES, FORM_DIRECTIVES] +}) +export class ChildrenDemo { + private value:any = {}; + private items:Array = [ + { + text: 'Austria', + children: [ + {id: 54, text: 'Vienna'} + ] + }, + { + text: 'Belgium', + children: [ + {id: 2, text: 'Antwerp'}, + {id: 9, text: 'Brussels'} + ] + }, + { + text: 'Bulgaria', + children: [ + {id: 48, text: 'Sofia'} + ] + }, + { + text: 'Croatia', + children: [ + {id: 58, text: 'Zagreb'} + ] + }, + { + text: 'Czech Republic', + children: [ + {id: 42, text: 'Prague'} + ] + }, + { + text: 'Denmark', + children: [ + {id: 13, text: 'Copenhagen'} + ] + }, + { + text: 'England', + children: [ + {id: 6, text: 'Birmingham'}, + {id: 7, text: 'Bradford'}, + {id: 26, text: 'Leeds'}, + {id: 30, text: 'London'}, + {id: 34, text: 'Manchester'}, + {id: 47, text: 'Sheffield'} + ] + }, + { + text: 'Finland', + children: [ + {id: 25, text: 'Helsinki'} + ] + }, + { + text: 'France', + children: [ + {id: 35, text: 'Marseille'}, + {id: 40, text: 'Paris'} + ] + }, + { + text: 'Germany', + children: [ + {id: 5, text: 'Berlin'}, + {id: 8, text: 'Bremen'}, + {id: 12, text: 'Cologne'}, + {id: 14, text: 'Dortmund'}, + {id: 15, text: 'Dresden'}, + {id: 17, text: 'Düsseldorf'}, + {id: 18, text: 'Essen'}, + {id: 19, text: 'Frankfurt'}, + {id: 23, text: 'Hamburg'}, + {id: 24, text: 'Hannover'}, + {id: 27, text: 'Leipzig'}, + {id: 37, text: 'Munich'}, + {id: 50, text: 'Stuttgart'} + ] + }, + { + text: 'Greece', + children: [ + {id: 3, text: 'Athens'} + ] + }, + { + text: 'Hungary', + children: [ + {id: 11, text: 'Budapest'} + ] + }, + { + text: 'Ireland', + children: [ + {id: 16, text: 'Dublin'} + ] + }, + { + text: 'Italy', + children: [ + {id: 20, text: 'Genoa'}, + {id: 36, text: 'Milan'}, + {id: 38, text: 'Naples'}, + {id: 39, text: 'Palermo'}, + {id: 44, text: 'Rome'}, + {id: 52, text: 'Turin'} + ] + }, + { + text: 'Latvia', + children: [ + {id: 43, text: 'Riga'} + ] + }, + { + text: 'Lithuania', + children: [ + {id: 55, text: 'Vilnius'} + ] + }, + { + text: 'Netherlands', + children: [ + {id: 1, text: 'Amsterdam'}, + {id: 45, text: 'Rotterdam'}, + {id: 51, text: 'The Hague'} + ] + }, + { + text: 'Poland', + children: [ + {id: 29, text: 'Łódź'}, + {id: 31, text: 'Kraków'}, + {id: 41, text: 'Poznań'}, + {id: 56, text: 'Warsaw'}, + {id: 57, text: 'Wrocław'} + ] + }, + { + text: 'Portugal', + children: [ + {id: 28, text: 'Lisbon'} + ] + }, + { + text: 'Romania', + children: [ + {id: 10, text: 'Bucharest'} + ] + }, + { + text: 'Scotland', + children: [ + {id: 21, text: 'Glasgow'} + ] + }, + { + text: 'Spain', + children: [ + {id: 4, text: 'Barcelona'}, + {id: 32, text: 'Madrid'}, + {id: 33, text: 'Málaga'}, + {id: 46, text: 'Seville'}, + {id: 53, text: 'Valencia'}, + {id: 59, text: 'Zaragoza'} + ] + }, + { + text: 'Sweden', + children: [ + {id: 22, text: 'Gothenburg'}, + {id: 49, text: 'Stockholm'} + ] + } + ]; + + private selected(value:any) { + console.log('Selected value is: ', value); + } + + private removed(value:any) { + console.log('Removed value is: ', value); + } + + private refreshValue(value:any) { + this.value = value; + } +} diff --git a/demo/components/select/menu-demo.html b/demo/components/select/menu-demo.html new file mode 100644 index 00000000..81e35f11 --- /dev/null +++ b/demo/components/select/menu-demo.html @@ -0,0 +1,10 @@ +
+

Select a city by timezone

+ +

{{value.text}}

+
diff --git a/demo/components/select/menu-demo.ts b/demo/components/select/menu-demo.ts new file mode 100644 index 00000000..4e099b4b --- /dev/null +++ b/demo/components/select/menu-demo.ts @@ -0,0 +1,121 @@ +/// + +import { + Component, View, + CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass +} from 'angular2/angular2'; + +import {select} from '../../../components/index'; + +// webpack html imports +let template = require('./menu-demo.html'); + +@Component({ + selector: 'menu-demo' +}) +@View({ + template: template, + directives: [select, NgClass, CORE_DIRECTIVES, FORM_DIRECTIVES] +}) +export class MenuDemo { + private value:any = {}; + private items:Array = [ + { + id: '+00:00', + text: 'Western European Time Zone', + submenu: { + items: [ + {id: 4, text: 'Barcelona'}, + {id: 6, text: 'Birmingham'}, + {id: 7, text: 'Bradford'}, + {id: 16, text: 'Dublin'}, + {id: 21, text: 'Glasgow'}, + {id: 26, text: 'Leeds'}, + {id: 28, text: 'Lisbon'}, + {id: 30, text: 'London'}, + {id: 32, text: 'Madrid'}, + {id: 33, text: 'Málaga'}, + {id: 34, text: 'Manchester'}, + {id: 46, text: 'Seville'}, + {id: 47, text: 'Sheffield'}, + {id: 53, text: 'Valencia'}, + {id: 59, text: 'Zaragoza'} + ], + showSearchInput: true + } + }, + { + id: '+01:00', + text: 'Central European Time Zone', + submenu: { + items: [ + {id: 1, text: 'Amsterdam'}, + {id: 2, text: 'Antwerp'}, + {id: 5, text: 'Berlin'}, + {id: 8, text: 'Bremen'}, + {id: 9, text: 'Brussels'}, + {id: 11, text: 'Budapest'}, + {id: 12, text: 'Cologne'}, + {id: 13, text: 'Copenhagen'}, + {id: 14, text: 'Dortmund'}, + {id: 15, text: 'Dresden'}, + {id: 17, text: 'Düsseldorf'}, + {id: 18, text: 'Essen'}, + {id: 19, text: 'Frankfurt'}, + {id: 20, text: 'Genoa'}, + {id: 22, text: 'Gothenburg'}, + {id: 23, text: 'Hamburg'}, + {id: 24, text: 'Hannover'}, + {id: 27, text: 'Leipzig'}, + {id: 29, text: 'Łódź'}, + {id: 31, text: 'Kraków'}, + {id: 35, text: 'Marseille'}, + {id: 36, text: 'Milan'}, + {id: 37, text: 'Munich'}, + {id: 38, text: 'Naples'}, + {id: 39, text: 'Palermo'}, + {id: 40, text: 'Paris'}, + {id: 41, text: 'Poznań'}, + {id: 42, text: 'Prague'}, + {id: 44, text: 'Rome'}, + {id: 45, text: 'Rotterdam'}, + {id: 49, text: 'Stockholm'}, + {id: 50, text: 'Stuttgart'}, + {id: 51, text: 'The Hague'}, + {id: 52, text: 'Turin'}, + {id: 54, text: 'Vienna'}, + {id: 56, text: 'Warsaw'}, + {id: 57, text: 'Wrocław'}, + {id: 58, text: 'Zagreb'} + ], + showSearchInput: true + } + }, + { + id: '+02:00', + text: 'Eastern European Time Zone', + submenu: { + items: [ + {id: 3, text: 'Athens'}, + {id: 10, text: 'Bucharest'}, + {id: 25, text: 'Helsinki'}, + {id: 43, text: 'Riga'}, + {id: 48, text: 'Sofia'}, + {id: 55, text: 'Vilnius'} + ] + } + } + ]; + + private selected(value:any) { + console.log('Selected value is: ', value); + } + + private removed(value:any) { + console.log('Removed value is: ', value); + } + + private refreshValue(value:any) { + this.value = value; + } +} diff --git a/demo/components/select/multiple-demo.html b/demo/components/select/multiple-demo.html new file mode 100644 index 00000000..397f5047 --- /dev/null +++ b/demo/components/select/multiple-demo.html @@ -0,0 +1,11 @@ +
+

Select multiple cities

+ +

{{itemsToString(value)}}

+
diff --git a/demo/components/select/multiple-demo.ts b/demo/components/select/multiple-demo.ts new file mode 100644 index 00000000..8bf1ae83 --- /dev/null +++ b/demo/components/select/multiple-demo.ts @@ -0,0 +1,50 @@ +/// + +import { + Component, View, + CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass +} from 'angular2/angular2'; + +import {select} from '../../../components/index'; + +// webpack html imports +let template = require('./multiple-demo.html'); + +@Component({ + selector: 'multiple-demo' +}) +@View({ + template: template, + directives: [select, NgClass, CORE_DIRECTIVES, FORM_DIRECTIVES] +}) +export class MultipleDemo { + private value:any = ['Athens']; + private items:Array = ['Amsterdam', 'Antwerp', 'Athens', 'Barcelona', + 'Berlin', 'Birmingham', 'Bradford', 'Bremen', 'Brussels', 'Bucharest', + 'Budapest', 'Cologne', 'Copenhagen', 'Dortmund', 'Dresden', 'Dublin', 'Düsseldorf', + 'Essen', 'Frankfurt', 'Genoa', 'Glasgow', 'Gothenburg', 'Hamburg', 'Hannover', + 'Helsinki', 'Leeds', 'Leipzig', 'Lisbon', 'Łódź', 'London', 'Kraków', 'Madrid', + 'Málaga', 'Manchester', 'Marseille', 'Milan', 'Munich', 'Naples', 'Palermo', + 'Paris', 'Poznań', 'Prague', 'Riga', 'Rome', 'Rotterdam', 'Seville', 'Sheffield', + 'Sofia', 'Stockholm', 'Stuttgart', 'The Hague', 'Turin', 'Valencia', 'Vienna', + 'Vilnius', 'Warsaw', 'Wrocław', 'Zagreb', 'Zaragoza']; + + private selected(value:any) { + console.log('Selected value is: ', value); + } + + private removed(value:any) { + console.log('Removed value is: ', value); + } + + private refreshValue(value:any) { + this.value = value; + } + + private itemsToString(value:Array = []) { + return value + .map(item => { + return item.text; + }).join(','); + } +} diff --git a/demo/components/select/single-demo.html b/demo/components/select/single-demo.html new file mode 100644 index 00000000..7936ed72 --- /dev/null +++ b/demo/components/select/single-demo.html @@ -0,0 +1,10 @@ +
+

Select a single city

+ +

{{value.text}}

+
diff --git a/demo/components/select/single-demo.ts b/demo/components/select/single-demo.ts new file mode 100644 index 00000000..8520c270 --- /dev/null +++ b/demo/components/select/single-demo.ts @@ -0,0 +1,43 @@ +/// + +import { + Component, View, + CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass +} from 'angular2/angular2'; + +import {select} from '../../../components/index'; + +// webpack html imports +let template = require('./single-demo.html'); + +@Component({ + selector: 'single-demo' +}) +@View({ + template: template, + directives: [select, NgClass, CORE_DIRECTIVES, FORM_DIRECTIVES] +}) +export class SingleDemo { + private value:any = {}; + private items:Array = ['Amsterdam', 'Antwerp', 'Athens', 'Barcelona', + 'Berlin', 'Birmingham', 'Bradford', 'Bremen', 'Brussels', 'Bucharest', + 'Budapest', 'Cologne', 'Copenhagen', 'Dortmund', 'Dresden', 'Dublin', 'Düsseldorf', + 'Essen', 'Frankfurt', 'Genoa', 'Glasgow', 'Gothenburg', 'Hamburg', 'Hannover', + 'Helsinki', 'Leeds', 'Leipzig', 'Lisbon', 'Łódź', 'London', 'Kraków', 'Madrid', + 'Málaga', 'Manchester', 'Marseille', 'Milan', 'Munich', 'Naples', 'Palermo', + 'Paris', 'Poznań', 'Prague', 'Riga', 'Rome', 'Rotterdam', 'Seville', 'Sheffield', + 'Sofia', 'Stockholm', 'Stuttgart', 'The Hague', 'Turin', 'Valencia', 'Vienna', + 'Vilnius', 'Warsaw', 'Wrocław', 'Zagreb', 'Zaragoza']; + + private selected(value:any) { + console.log('Selected value is: ', value); + } + + private removed(value:any) { + console.log('Removed value is: ', value); + } + + private refreshValue(value:any) { + this.value = value; + } +} diff --git a/demo/getting-started.md b/demo/getting-started.md new file mode 100644 index 00000000..47823918 --- /dev/null +++ b/demo/getting-started.md @@ -0,0 +1,4 @@ +# Getting started + +### First of all, Welcome! + diff --git a/demo/index-bs4.html b/demo/index-bs4.html new file mode 100644 index 00000000..e4888e04 --- /dev/null +++ b/demo/index-bs4.html @@ -0,0 +1,30 @@ + + + + Angular2 Select + + + + + + + + + + + + + + + + Loading... + + + + + + + + + + diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 00000000..3d7c4c63 --- /dev/null +++ b/demo/index.html @@ -0,0 +1,44 @@ + + + + Angular2 Select + + + + + + + + + + + + + + + + + + + Loading... + + + + + + + + + + diff --git a/demo/index.ts b/demo/index.ts new file mode 100644 index 00000000..24389179 --- /dev/null +++ b/demo/index.ts @@ -0,0 +1,66 @@ +/// +import {Component, View, bootstrap, NgClass} from 'angular2/angular2'; + +import {Ng2SelectConfig, Ng2SelectTheme} from '../components/index'; + +let w:any = window; +if (w && w.__theme === 'bs4') { + Ng2SelectConfig.theme = Ng2SelectTheme.BS4; +} + + +import {SelectSection} from './components/select-section'; + +let gettingStarted = require('./getting-started.md'); + +@Component({ + selector: 'app' +}) +@View({ + template: ` + + +
+
+

ng2-select

+

Native Angular2 component for Select

+ View on GitHub +
+ +
+
+
+ +
+
+ +

ng2-select available with: + Bootstrap 3 + Bootstrap 4 +

+
+
+
+
${gettingStarted}
+ + +
+ + + + `, + directives: [ + NgClass, + SelectSection + ] +}) +export class Demo { + private isBs3:boolean = Ng2SelectConfig.theme === Ng2SelectTheme.BS3; +} + +bootstrap(Demo); diff --git a/gulp-tasks/lint.js b/gulp-tasks/lint.js new file mode 100644 index 00000000..1371ed52 --- /dev/null +++ b/gulp-tasks/lint.js @@ -0,0 +1,23 @@ +var gulp = require('gulp'); +var esLint = require('gulp-eslint'); +var tslint = require('gulp-tslint'); + +var paths = gulp.paths; + +gulp.task('eslint', function() { + return gulp.src(paths.jssrc) + .pipe(esLint({useEslintrc: true})) + .pipe(esLint.format()) + .pipe(esLint.failOnError()); +}); + +gulp.task('tslint', function() { + return gulp.src(paths.tssrc) + .pipe(tslint()) + .pipe(tslint.report('verbose', { + emitError: true, + reportLimit: 0 + })); +}); + +gulp.task('lint', ['tslint', 'eslint']); diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..55030f8a --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,57 @@ +var gulp = require('gulp'); + +gulp.paths = { + tssrc: [ + '**/*.ts', + '!node_modules/**/*', + '!dist/**/*', + '!typings/**/*', + '!**/*.{ts,coffee}.js'], + jssrc: [ + '*.js', + '!angular2-select.js', + 'gulp-tasks/*.js', + '!node_modules', + '!**/*.{ts,coffee}.js'] +}; + +require('require-dir')('./gulp-tasks'); + +var typescript = require('gulp-tsc'); +var options = require('./tsconfig.json').compilerOptions; +options.emitError = false; + +var o = { + target: 'es5', + module: 'commonjs', + outDir: 'dist', + sourceRoot: 'dist', + mapRoot: 'dist', + keepTree: true, + declaration: true, + noEmitOnError: true, + emitError: false, + sourceMap: true, + removeComments: true, + noResolve: false, + suppressImplicitAnyIndexErrors: true, + safe: false, + emitDecoratorMetadata: true, + experimentalDecorators: true +}; + +var clean = require('gulp-clean'); +gulp.task('clean', function () { + return gulp.src('dist', {read: false}) + .pipe(clean()); +}); + +gulp.task('compile', ['clean'], function () { + gulp.src(['components/**/*.ts']) + .pipe(typescript(o)) + .pipe(gulp.dest(options.outDir)); +}); + +gulp.task('default', function () { + gulp.start('lint'); +}); diff --git a/package.json b/package.json index 39ce364b..9c06bf80 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,78 @@ { "name": "ng2-select", "version": "0.0.1", - "description": "Angular2 based replacement for select boxes", - "main": "index.js", + "description": "angular2 select component", "scripts": { - "test": "npm test" + "deploy": "NODE_ENV=production webpack -p --progress --color --optimize-minimize --optimize-dedupe --optimize-occurence-order", + "prepublish": "gulp compile", + "prestart": "npm install", + "server": "webpack-dev-server --hot --inline --colors --display-error-details --display-cached", + "start": "npm run server", + "test": "gulp lint" }, - "repository": { - "type": "git", - "url": "git+https://github.com/valor-software/ng2-select.git" + "main": "dist/index.js", + "typescript": { + "definition": [ + "dist/module.d.ts", + "typings/es6-object.d.ts" + ] }, + "files": [ + "dist", + "components", + "typings", + "tsd.d.ts" + ], "keywords": [ "angular2", - "ng2", - "select", - "angular2-select", - "ng2-select" + "bootstrap", + "angularjs", + "twitter-bootstrap" ], - "author": "Dmitriy Shekhovtsov ", + "author": "Vyacheslav Chub ", "license": "MIT", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/valor-software/ng2-select.git" + }, "bugs": { "url": "https://github.com/valor-software/ng2-select/issues" }, - "homepage": "https://github.com/valor-software/ng2-select#readme" + "homepage": "https://github.com/valor-software/ng2-select#readme", + "dependencies": { + "angular2": "2.0.0-alpha.37", + "es6-promise": "3.0.2", + "moment": "^2.10.6", + "ng2-bootstrap": "^0.37.0", + "reflect-metadata": "0.1.1", + "rtts_assert": "2.0.0-alpha.37", + "traceur-runtime": "0.0.59", + "zone.js": "0.5.4" + }, + "devDependencies": { + "bootstrap": "^3.3.5", + "clean-webpack-plugin": "^0.1.3", + "compression-webpack-plugin": "^0.2.0", + "eslint": "^1.1.0", + "exports-loader": "^0.6.2", + "file-loader": "^0.8.4", + "gulp": "^3.9.0", + "gulp-clean": "^0.3.1", + "gulp-eslint": "^1.0.0", + "gulp-size": "^2.0.0", + "gulp-tsc": "^1.1.1", + "gulp-tslint": "^3.1.2", + "html-loader": "^0.3.0", + "markdown-loader": "^0.1.7", + "marked": "^0.3.5", + "pre-commit": "^1.1.1", + "prismjs": "valorkin/prism", + "prismjs-loader": "0.0.2", + "raw-loader": "^0.5.1", + "require-dir": "^0.3.0", + "typescript": "^1.5.3", + "typescript-simple-loader": "^0.3.4", + "webpack": "^1.11.0", + "webpack-dev-server": "^1.10.1" + } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..ae0e6588 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "version": "1.5.3", + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "dist", + "sourceMap": true, + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "listFiles": false, + "noLib": false, + "noEmitOnError": false, + "noImplicitAny": false + }, + "filesGlob": [ + "./**/*.ts", + "!./node_modules/**/*.ts" + ], + "files": [ + "./components/select/select.ts", + "./components/select/select-item.ts", + "./components/index.ts", + "./components/module.ts", + "./demo/components/select-section.ts", + "./demo/components/select/single-demo.ts", + "./demo/components/select/multiple-demo.ts", + "./demo/components/select/children-demo.ts", + "./demo/index.ts", + "./tsd.d.ts", + "./typings/tsd.d.ts" + ] +} diff --git a/tsd.d.ts b/tsd.d.ts new file mode 100644 index 00000000..8754f737 --- /dev/null +++ b/tsd.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000..a4120611 --- /dev/null +++ b/tslint.json @@ -0,0 +1,56 @@ +{ + "rules": { + "class-name": true, + "comment-format": [true, "check-space"], + "curly": true, + "eofline": true, + "forin": true, + "indent": [true, "spaces"], + "label-position": true, + "label-undefined": true, + "max-line-length": [false, 140], + "no-arg": true, + "no-bitwise": true, + "no-console": [true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-key": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-eval": true, + "no-shadowed-variable": true, + "no-string-literal": true, + "no-switch-case-fall-through": true, + "no-trailing-comma": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-unused-variable": false, + "no-unreachable": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "one-line": [true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [true, "single"], + "radix": true, + "semicolon": true, + "sort-object-literal-keys": false, + "triple-equals": [true, "allow-null-check"], + "variable-name": false, + "whitespace": [true, + "check-branch", + "check-decl", + "check-operator", + "check-separator" + ] + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..a5efe304 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,161 @@ +var path = require('path'); +var marked = require('marked'); +var webpack = require('webpack'); + +var Clean = require('clean-webpack-plugin'); +var CompressionPlugin = require('compression-webpack-plugin'); + +// marked renderer hack +marked.Renderer.prototype.code = function (code, lang) { + var out = this.options.highlight(code, lang); + + if (!lang) { + return '
' + out + '\n
'; + } + + var classMap = this.options.langPrefix + lang; + return '
' + out + '\n
\n'; +}; + +/*eslint no-process-env:0, camelcase:0*/ +var isProduction = (process.env.NODE_ENV || 'development') === 'production'; + +var src = 'demo'; +//var absSrc = path.join(__dirname, src); +var dest = '/build'; +var absDest = path.join(__dirname, dest); + +var config = { + // isProduction ? 'source-map' : 'evale', + devtool: 'source-map', + + debug: true, + cache: false, + context: __dirname, + + resolve: { + root: __dirname, + extensions: ['', '.ts', '.js', '.json'], + alias: {} + }, + + entry: { + angular2: [ + // Angular 2 Deps + 'traceur-runtime', + 'zone.js', + 'reflect-metadata', + 'rtts_assert/rtts_assert', + 'angular2/angular2' + ], + 'angular2-select': ['components'], + 'angular2-select-demo': 'demo' + }, + + output: { + path: absDest, + filename: '[name].js', + sourceMapFilename: '[name].js.map', + chunkFilename: '[id].chunk.js' + }, + + // our Development Server configs + devServer: { + inline: true, + colors: true, + historyApiFallback: true, + contentBase: src, + publicPath: dest + }, + markdownLoader: { + langPrefix: 'language-', + highlight: function (code, lang) { + var language = !lang || lang === 'html' ? 'markup' : lang; + if (!global.Prism) { + global.Prism = require('prismjs'); + } + var Prism = global.Prism; + if (!Prism.languages[language]) { + require('prismjs/components/prism-' + language + '.js'); + } + return Prism.highlight(code, Prism.languages[language]); + } + }, + module: { + loaders: [ + // support markdown + {test: /\.md$/, loader: 'html!markdown'}, + + // Support for *.json files. + {test: /\.json$/, loader: 'json'}, + + // Support for CSS as raw text + {test: /\.css$/, loader: 'raw'}, + + // support for .html as raw text + {test: /\.html$/, loader: 'raw'}, + + // Support for .ts files. + { + test: /\.ts$/, + loader: 'typescript-simple', + exclude: [ + /\.spec\.ts$/, + /\.e2e\.ts$/, + /web_modules/, + /test/, + /node_modules/ + ] + } + ], + noParse: [ + /rtts_assert\/src\/rtts_assert/ + ] + }, + + plugins: [ + new Clean(['build']), + new webpack.optimize.CommonsChunkPlugin({ + name: 'angular2', + minChunks: Infinity, + filename: 'angular2.js' + }), + new webpack.optimize.DedupePlugin({ + __isProduction: isProduction + }), + new webpack.optimize.OccurenceOrderPlugin(), + new webpack.optimize.DedupePlugin() + ], + pushPlugins: function () { + if (!isProduction) { + return; + } + + this.plugins.push.apply(this.plugins, [ + //production only + new webpack.optimize.UglifyJsPlugin({ + compress: { + warnings: false, + drop_debugger: false + }, + output: { + comments: false + }, + beautify: false + }), + new CompressionPlugin({ + asset: '{file}.gz', + algorithm: 'gzip', + regExp: /\.js$|\.html|\.css|.map$/, + threshold: 10240, + minRatio: 0.8 + }) + ]); + }, + + stats: {colors: true, reasons: true} +}; + +config.pushPlugins(); + +module.exports = config;