├── .csscomb.json ├── .editorconfig ├── .gitignore ├── .sass-lint.yml ├── LICENSE ├── README.md ├── angular.json ├── browserslist ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts ├── tsconfig.e2e.json └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── projects └── radial-color-picker │ ├── LICENSE │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── ng-package.prod.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── assets │ │ │ └── lib.scss │ │ ├── components │ │ │ ├── color-preview │ │ │ │ ├── color-preview.component.html │ │ │ │ ├── color-preview.component.scss │ │ │ │ ├── color-preview.component.spec.ts │ │ │ │ └── color-preview.component.ts │ │ │ └── radial-color-picker │ │ │ │ ├── radial-color-picker.component.html │ │ │ │ ├── radial-color-picker.component.scss │ │ │ │ ├── radial-color-picker.component.spec.ts │ │ │ │ └── radial-color-picker.component.ts │ │ ├── directives │ │ │ ├── rotatable.directive.spec.ts │ │ │ └── rotatable.directive.ts │ │ ├── helpers │ │ │ ├── animations.ts │ │ │ ├── color-functions.ts │ │ │ ├── color-gradient.ts │ │ │ ├── constants.ts │ │ │ └── helpers.ts │ │ └── radial-color-picker.module.ts │ ├── public-api.ts │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ ├── tsconfig.spec.json │ └── tslint.json ├── screenshots └── thumbnail.png ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.scss │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ └── components │ │ ├── closed │ │ ├── closed.component.html │ │ ├── closed.component.scss │ │ ├── closed.component.spec.ts │ │ └── closed.component.ts │ │ ├── collapsible │ │ ├── collapsible.component.html │ │ ├── collapsible.component.scss │ │ ├── collapsible.component.spec.ts │ │ └── collapsible.component.ts │ │ ├── formcontrol │ │ ├── formcontrol.component.html │ │ ├── formcontrol.component.scss │ │ ├── formcontrol.component.spec.ts │ │ └── formcontrol.component.ts │ │ ├── home │ │ ├── home.component.html │ │ ├── home.component.scss │ │ ├── home.component.spec.ts │ │ └── home.component.ts │ │ ├── ngmodel │ │ ├── ngmodel.component.html │ │ ├── ngmodel.component.scss │ │ ├── ngmodel.component.spec.ts │ │ └── ngmodel.component.ts │ │ ├── noanimations │ │ ├── noanimations.component.html │ │ ├── noanimations.component.scss │ │ ├── noanimations.component.spec.ts │ │ └── noanimations.component.ts │ │ ├── pagenotfound │ │ ├── pagenotfound.component.html │ │ ├── pagenotfound.component.scss │ │ ├── pagenotfound.component.spec.ts │ │ └── pagenotfound.component.ts │ │ └── simple │ │ ├── simple.component.html │ │ ├── simple.component.scss │ │ ├── simple.component.spec.ts │ │ └── simple.component.ts ├── assets │ └── .gitkeep ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.csscomb.json: -------------------------------------------------------------------------------- 1 | { 2 | "remove-empty-rulesets": true, 3 | "always-semicolon": true, 4 | "color-case": "lower", 5 | "block-indent": " ", 6 | "color-shorthand": false, 7 | "element-case": "lower", 8 | "eof-newline": true, 9 | "leading-zero": true, 10 | "quotes": "single", 11 | "sort-order-fallback": "abc", 12 | "space-before-colon": "", 13 | "space-after-colon": " ", 14 | "space-before-combinator": " ", 15 | "space-after-combinator": " ", 16 | "space-between-declarations": "\n", 17 | "space-before-opening-brace": " ", 18 | "space-after-opening-brace": "\n", 19 | "space-after-selector-delimiter": " ", 20 | "space-before-selector-delimiter": "", 21 | "space-before-closing-brace": "\n", 22 | "strip-spaces": true, 23 | "tab-size": true, 24 | "unitless-zero": true, 25 | "vendor-prefix-align": true, 26 | "sort-order": [ 27 | [ "$variable" ], 28 | [ "$include" ], 29 | [ 30 | "font", 31 | "font-family", 32 | "font-size", 33 | "font-weight", 34 | "font-style", 35 | "font-variant", 36 | "font-size-adjust", 37 | "font-stretch", 38 | "font-effect", 39 | "font-emphasize", 40 | "font-emphasize-position", 41 | "font-emphasize-style", 42 | "font-smooth", 43 | "line-height", 44 | 45 | "position", 46 | "z-index", 47 | "top", 48 | "right", 49 | "bottom", 50 | "left", 51 | 52 | "display", 53 | "visibility", 54 | "float", 55 | "clear", 56 | "overflow", 57 | "overflow-x", 58 | "overflow-y", 59 | "-ms-overflow-x", 60 | "-ms-overflow-y", 61 | "clip", 62 | "zoom", 63 | "-webkit-align-content", 64 | "-ms-flex-line-pack", 65 | "align-content", 66 | "-webkit-box-align", 67 | "-moz-box-align", 68 | "-webkit-align-items", 69 | "align-items", 70 | "-ms-flex-align", 71 | "-webkit-align-self", 72 | "-ms-flex-item-align", 73 | "-ms-grid-row-align", 74 | "align-self", 75 | "-webkit-box-flex", 76 | "-webkit-flex", 77 | "-moz-box-flex", 78 | "-ms-flex", 79 | "flex", 80 | "-webkit-flex-flow", 81 | "-ms-flex-flow", 82 | "flex-flow", 83 | "-webkit-flex-basis", 84 | "-ms-flex-preferred-size", 85 | "flex-basis", 86 | "-webkit-box-orient", 87 | "-webkit-box-direction", 88 | "-webkit-flex-direction", 89 | "-moz-box-orient", 90 | "-moz-box-direction", 91 | "-ms-flex-direction", 92 | "flex-direction", 93 | "-webkit-flex-grow", 94 | "-ms-flex-positive", 95 | "flex-grow", 96 | "-webkit-flex-shrink", 97 | "-ms-flex-negative", 98 | "flex-shrink", 99 | "-webkit-flex-wrap", 100 | "-ms-flex-wrap", 101 | "flex-wrap", 102 | "-webkit-box-pack", 103 | "-moz-box-pack", 104 | "-ms-flex-pack", 105 | "-webkit-justify-content", 106 | "justify-content", 107 | "-webkit-box-ordinal-group", 108 | "-webkit-order", 109 | "-moz-box-ordinal-group", 110 | "-ms-flex-order", 111 | "order", 112 | 113 | 114 | "-webkit-box-sizing", 115 | "-moz-box-sizing", 116 | "box-sizing", 117 | "width", 118 | "min-width", 119 | "max-width", 120 | "height", 121 | "min-height", 122 | "max-height", 123 | "margin", 124 | "margin-top", 125 | "margin-right", 126 | "margin-bottom", 127 | "margin-left", 128 | "padding", 129 | "padding-top", 130 | "padding-right", 131 | "padding-bottom", 132 | "padding-left", 133 | 134 | "table-layout", 135 | "empty-cells", 136 | "caption-side", 137 | "border-spacing", 138 | "border-collapse", 139 | "list-style", 140 | "list-style-position", 141 | "list-style-type", 142 | "list-style-image", 143 | 144 | "content", 145 | "quotes", 146 | "counter-reset", 147 | "counter-increment", 148 | "resize", 149 | "cursor", 150 | "-webkit-user-select", 151 | "-moz-user-select", 152 | "-ms-user-select", 153 | "user-select", 154 | "nav-index", 155 | "nav-up", 156 | "nav-right", 157 | "nav-down", 158 | "nav-left", 159 | "-webkit-transition", 160 | "-moz-transition", 161 | "-ms-transition", 162 | "-o-transition", 163 | "transition", 164 | "-webkit-transition-delay", 165 | "-moz-transition-delay", 166 | "-ms-transition-delay", 167 | "-o-transition-delay", 168 | "transition-delay", 169 | "-webkit-transition-timing-function", 170 | "-moz-transition-timing-function", 171 | "-ms-transition-timing-function", 172 | "-o-transition-timing-function", 173 | "transition-timing-function", 174 | "-webkit-transition-duration", 175 | "-moz-transition-duration", 176 | "-ms-transition-duration", 177 | "-o-transition-duration", 178 | "transition-duration", 179 | "-webkit-transition-property", 180 | "-moz-transition-property", 181 | "-ms-transition-property", 182 | "-o-transition-property", 183 | "transition-property", 184 | "-webkit-transform", 185 | "-moz-transform", 186 | "-ms-transform", 187 | "-o-transform", 188 | "transform", 189 | "-webkit-transform-origin", 190 | "-moz-transform-origin", 191 | "-ms-transform-origin", 192 | "-o-transform-origin", 193 | "transform-origin", 194 | "-webkit-animation", 195 | "-moz-animation", 196 | "-ms-animation", 197 | "-o-animation", 198 | "animation", 199 | "-webkit-animation-name", 200 | "-moz-animation-name", 201 | "-ms-animation-name", 202 | "-o-animation-name", 203 | "animation-name", 204 | "-webkit-animation-duration", 205 | "-moz-animation-duration", 206 | "-ms-animation-duration", 207 | "-o-animation-duration", 208 | "animation-duration", 209 | "-webkit-animation-play-state", 210 | "-moz-animation-play-state", 211 | "-ms-animation-play-state", 212 | "-o-animation-play-state", 213 | "animation-play-state", 214 | "-webkit-animation-timing-function", 215 | "-moz-animation-timing-function", 216 | "-ms-animation-timing-function", 217 | "-o-animation-timing-function", 218 | "animation-timing-function", 219 | "-webkit-animation-delay", 220 | "-moz-animation-delay", 221 | "-ms-animation-delay", 222 | "-o-animation-delay", 223 | "animation-delay", 224 | "-webkit-animation-iteration-count", 225 | "-moz-animation-iteration-count", 226 | "-ms-animation-iteration-count", 227 | "-o-animation-iteration-count", 228 | "animation-iteration-count", 229 | "-webkit-animation-direction", 230 | "-moz-animation-direction", 231 | "-ms-animation-direction", 232 | "-o-animation-direction", 233 | "animation-direction", 234 | "text-align", 235 | "-webkit-text-align-last", 236 | "-moz-text-align-last", 237 | "-ms-text-align-last", 238 | "text-align-last", 239 | "vertical-align", 240 | "white-space", 241 | "text-decoration", 242 | "text-emphasis", 243 | "text-emphasis-color", 244 | "text-emphasis-style", 245 | "text-emphasis-position", 246 | "text-indent", 247 | "-ms-text-justify", 248 | "text-justify", 249 | "letter-spacing", 250 | "word-spacing", 251 | "-ms-writing-mode", 252 | "text-outline", 253 | "text-transform", 254 | "text-wrap", 255 | "text-overflow", 256 | "-ms-text-overflow", 257 | "text-overflow-ellipsis", 258 | "text-overflow-mode", 259 | "-ms-word-wrap", 260 | "word-wrap", 261 | "word-break", 262 | "-ms-word-break", 263 | "-moz-tab-size", 264 | "-o-tab-size", 265 | "tab-size", 266 | "-webkit-hyphens", 267 | "-moz-hyphens", 268 | "hyphens", 269 | "pointer-events", 270 | 271 | "opacity", 272 | "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", 273 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", 274 | "-ms-interpolation-mode", 275 | "color", 276 | "border", 277 | "border-width", 278 | "border-style", 279 | "border-color", 280 | "border-top", 281 | "border-top-width", 282 | "border-top-style", 283 | "border-top-color", 284 | "border-right", 285 | "border-right-width", 286 | "border-right-style", 287 | "border-right-color", 288 | "border-bottom", 289 | "border-bottom-width", 290 | "border-bottom-style", 291 | "border-bottom-color", 292 | "border-left", 293 | "border-left-width", 294 | "border-left-style", 295 | "border-left-color", 296 | "-webkit-border-radius", 297 | "-moz-border-radius", 298 | "border-radius", 299 | "-webkit-border-top-left-radius", 300 | "-moz-border-radius-topleft", 301 | "border-top-left-radius", 302 | "-webkit-border-top-right-radius", 303 | "-moz-border-radius-topright", 304 | "border-top-right-radius", 305 | "-webkit-border-bottom-right-radius", 306 | "-moz-border-radius-bottomright", 307 | "border-bottom-right-radius", 308 | "-webkit-border-bottom-left-radius", 309 | "-moz-border-radius-bottomleft", 310 | "border-bottom-left-radius", 311 | "-webkit-border-image", 312 | "-moz-border-image", 313 | "-o-border-image", 314 | "border-image", 315 | "-webkit-border-image-source", 316 | "-moz-border-image-source", 317 | "-o-border-image-source", 318 | "border-image-source", 319 | "-webkit-border-image-slice", 320 | "-moz-border-image-slice", 321 | "-o-border-image-slice", 322 | "border-image-slice", 323 | "-webkit-border-image-width", 324 | "-moz-border-image-width", 325 | "-o-border-image-width", 326 | "border-image-width", 327 | "-webkit-border-image-outset", 328 | "-moz-border-image-outset", 329 | "-o-border-image-outset", 330 | "border-image-outset", 331 | "-webkit-border-image-repeat", 332 | "-moz-border-image-repeat", 333 | "-o-border-image-repeat", 334 | "border-image-repeat", 335 | "outline", 336 | "outline-width", 337 | "outline-style", 338 | "outline-color", 339 | "outline-offset", 340 | "background", 341 | "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", 342 | "background-color", 343 | "background-image", 344 | "background-repeat", 345 | "background-attachment", 346 | "background-position", 347 | "background-position-x", 348 | "-ms-background-position-x", 349 | "background-position-y", 350 | "-ms-background-position-y", 351 | "-webkit-background-clip", 352 | "-moz-background-clip", 353 | "background-clip", 354 | "background-origin", 355 | "-webkit-background-size", 356 | "-moz-background-size", 357 | "-o-background-size", 358 | "background-size", 359 | "box-decoration-break", 360 | "-webkit-box-shadow", 361 | "-moz-box-shadow", 362 | "box-shadow", 363 | "filter:progid:DXImageTransform.Microsoft.gradient", 364 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", 365 | "text-shadow" 366 | ], 367 | [ "$selector"], 368 | [ "$include media" ] 369 | ] 370 | } 371 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /.sass-lint.yml: -------------------------------------------------------------------------------- 1 | # sass-lint config generated by make-sass-lint-config v0.1.1 2 | # 3 | # The following scss-lint Linters are not yet supported by sass-lint: 4 | # PrivateNamingConvention 5 | # 6 | # The following settings/values are unsupported by sass-lint: 7 | # Linter Indentation, option "character" 8 | 9 | files: 10 | include: 'scss/**/*.scss' 11 | ignore: 12 | - 'bower_components/**/*.*' 13 | - 'coverage/**/*.*' 14 | - 'node-modules/**/*.*' 15 | options: 16 | formatter: stylish 17 | merge-default-rules: false 18 | rules: 19 | empty-line-between-blocks: 2 20 | property-units: 21 | - 2 22 | - 23 | global: ['px', '%', 'em', 'rem', 'vh', 'vw', 's', 'ms', 'min'] 24 | space-before-brace: 25 | - 2 26 | - 27 | include: true 28 | space-around-operator: 29 | - 2 30 | - 31 | include: true 32 | space-after-colon: 33 | - 2 34 | - 35 | include: true 36 | space-after-comma: 37 | - 2 38 | - 39 | include: true 40 | space-before-colon: 41 | - 2 42 | - 43 | include: false 44 | space-between-parens: 45 | - 2 46 | - 47 | include: false 48 | trailing-semicolon: 49 | - 2 50 | - 51 | include: true 52 | zero-unit: 53 | - 1 54 | - 55 | include: false 56 | bem-depth: 57 | - 2 58 | - 59 | max-depth: 3 60 | border-zero: 61 | - 1 62 | - 63 | convention: 'none' 64 | brace-style: 65 | - 1 66 | - 67 | allow-single-line: false 68 | class-name-format: 69 | - 1 70 | - 71 | convention: '^(?!js-).*' 72 | convention-explanation: 'should not be written in the form js-*' 73 | id-name-format: 74 | - 1 75 | - 76 | convention: hyphenatedbem 77 | extends-before-declarations: 0 78 | extends-before-mixins: 0 79 | function-name-format: 1 80 | indentation: 81 | - 2 82 | - 83 | size: 4 84 | leading-zero: 85 | - 2 86 | - include: true 87 | mixin-name-format: 1 88 | mixins-before-declarations: 1 89 | no-ids: 2 90 | no-invalid-hex: 2 91 | no-css-comments: 1 92 | no-extends: 0 93 | no-qualifying-elements: 0 94 | no-important: 1 95 | no-color-keywords: 2 96 | no-trailing-zero: 2 97 | no-transition-all: 1 98 | no-vendor-prefixes: 1 99 | no-duplicate-properties: 100 | - 2 101 | - 102 | exclude: 103 | - display 104 | no-empty-rulesets: 2 105 | one-declaration-per-line: 2 106 | placeholder-name-format: 107 | - 1 108 | - 109 | convention: hyphenatedlowercase 110 | property-sort-order: 0 111 | quotes: 112 | - 1 113 | - 114 | style: single 115 | variable-name-format: 1 116 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zlati Pehlivanov 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Radial Color Picker 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.1. 4 | 5 |

6 | screenshot 7 |

8 | 9 |

10 | 11 | Downloads 12 | 13 | 14 | Version 15 | 16 | 17 | License 18 | 19 |

20 | 21 | ## Introduction 22 | 23 | Great UX starts with two basic principles - ease of use and simplicity. Selecting a color should be as easy as moving a slider, clicking a checkbox or pressing a key just like other basic form elements behave. 24 | 25 | This is a flexible and elegant material-ish design color picker. Developed with mobile devices in mind. Key features: 26 | * Supports touch devices 27 | * Angular animations 28 | 29 | This component doesn't have feature parity with 30 | it's [predecessor](https://github.com/talamaska/angular-radial-color-picker) for Angular 1.x 31 | 32 | The component is developed so it can be used with template-driven or reactive form, or as a simple component. 33 | 34 | ## Quick Links 35 | 36 | * [Usage](#usage) 37 | * [Inputs](#inputs) 38 | * [Outputs](#outputs) 39 | * [Instance methods](#instance-methods) 40 | * [Lifecycle events](#lifecycle) 41 | 42 | * [FAQ](#questions) 43 | * [Contribute](#contributing) 44 | 45 | 46 | ## Usage 47 | 48 | Color Picker on [npm](https://www.npmjs.com/package/@radial-color-picker/angular-color-picker) 49 | ```bash 50 | npm install -S @radial-color-picker/angular-color-picker 51 | ``` 52 | 53 | In your app module or any module that will hold the components that are using the radial color picker: 54 | 55 | ```typescript 56 | import { RadialColorPickerModule } from 'radial-color-picker'; 57 | 58 | @NgModule({ 59 | declarations: [ 60 | AppComponent 61 | ], 62 | imports: [ 63 | BrowserModule, 64 | RadialColorPickerModule 65 | ], 66 | providers: [], 67 | bootstrap: [AppComponent] 68 | }) 69 | export class AppModule { } 70 | ``` 71 | 72 | and in your component template 73 | ```html 74 | 75 | ``` 76 | 77 | more examples can be found in the app in this repository 78 | 79 | ## Inputs 80 | `` component has several inputs, all of which are optional. 81 | 82 | | Options | Type | Default/Description | 83 | |------------|--------|---------| 84 | | `selectToChange` | Boolean | prevents propagation of on change on FormControl or NgModel on rotation. Defaults to false:
`false`. | 85 | | `collapsed` | Boolean | should the initial render be collapsed/open state of the color picker. Defaults to true:
`true`. | 86 | | `collapsible` | Boolean | Should color select trigger hiding of the color palette. Defaults to true:
`true`. | 87 | | `enterAnimation` | Boolean | flag to turn on/off intro animations. Defaults to true:
`true`. | 88 | | `exitAnimation` | Boolean | flag to turn on/off outro animations. Defaults to true:
`true`. | 89 | | `size` | String | pixel size of diameter. Defaults to 300:
`300`. | 90 | | `colorType` | String | format of color to be emitted by the component. Defaults to hex:
`hex`. | 91 | | `color` | String | hex, rgb or hsl code of your color. Defaults to red:
`#FF0000`. | 92 | 93 | [Back To Top](#user-content-quick-links) 94 | 95 | 96 | ## Outputs 97 | `` component has several outputs, all of which are optional. 98 | 99 | | Options | Type | Default/Description | 100 | |------------|--------|---------| 101 | | `selected` | Function | Callback which is triggered when a color is selected. | 102 | | `colorChange` | Function | Callback which is triggered when color is changed (i.e. on rotation). | 103 | | `lifecycle` | Function | Callback which is triggered when component state is changed. | 104 | 105 | [Back To Top](#user-content-quick-links) 106 | 107 | 108 | ## Lifecycle Events 109 | 110 | Emitted by the lifecycle Output 111 | 112 | | Name | Description | 113 | |------------|-------------| 114 | | `show` | Fires when the color picker is about to show and **before** any animation is started. | 115 | | `shown` | Fires when the color picker is shown and has finished animating. | 116 | | `selected` | Fires when a color is selected via the middle selector. Event is fired right before `hide`. | 117 | | `hide` | Fires when the color picker is about to hide and **before** any animation is started. | 118 | | `hidden` | Fires when the color picker is hidden and has finished animating. | 119 | 120 | [Back To Top](#user-content-quick-links) 121 | 122 | 123 | ## Instance methods 124 | | Name | Description | 125 | |------------|-------------| 126 | | `open` | Programatically opens the color picker if it's not already opened. | 127 | | `close` | Programatically closes the color picker if it's not already closed. | 128 | 129 | 130 | [Back To Top](#user-content-quick-links) 131 | 132 | 133 | ## First Asked Questions 134 | 135 |
136 | Color picker uses hex. How can I use other formats like rgb() or hsl? 137 |

The color picker will recognize the format of the input, but will still work internally with hex value of the provided color. You can change the output format by setting the colorType property to hex, rgb or hsl

138 |
139 | 140 |
141 | How to select other shades of the solid colors? 142 |

We suggest to add a custom slider for saturation and luminosity or use <input type="range">.

143 |
144 | 145 |
146 | How can I change the active color of the picker after initialization? 147 |

rcp-radial-color-picker component uses OnChanges lifecycle hook to detect changes of the color binding. When using 148 | <rcp-radial-color-picker [color]="color"></rcp-radial-color-picker> 149 |

150 |
151 | 152 | [Back To Top](#user-content-quick-links) 153 | 154 | ## Contribute 155 | If you're interested in the project you can help out with feature requests, bugfixes, documentation improvements or any other helpful contributions. You can use the issue list of this repo for bug reports and feature requests and as well as for questions and support. 156 | 157 | We are also particularly interested in projects you did with this plugin. If you have created something colorful and creative with the color picker and want to show it off send us a quick mail. 158 | 159 | The project is using an adapted version of [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-angular/convention.md) and commit messages should adhere to it. 160 | 161 | [Back To Top](#user-content-quick-links) 162 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-color-picker": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/angular-color-picker", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "aot": true, 26 | "assets": [ 27 | "src/favicon.ico", 28 | "src/assets" 29 | ], 30 | "styles": [ 31 | "src/styles.scss" 32 | ], 33 | "scripts": [] 34 | }, 35 | "configurations": { 36 | "production": { 37 | "fileReplacements": [ 38 | { 39 | "replace": "src/environments/environment.ts", 40 | "with": "src/environments/environment.prod.ts" 41 | } 42 | ], 43 | "optimization": true, 44 | "outputHashing": "all", 45 | "sourceMap": false, 46 | "extractCss": true, 47 | "namedChunks": false, 48 | "extractLicenses": true, 49 | "vendorChunk": false, 50 | "buildOptimizer": true, 51 | "budgets": [ 52 | { 53 | "type": "initial", 54 | "maximumWarning": "2mb", 55 | "maximumError": "5mb" 56 | }, 57 | { 58 | "type": "anyComponentStyle", 59 | "maximumWarning": "6kb", 60 | "maximumError": "10kb" 61 | } 62 | ] 63 | } 64 | } 65 | }, 66 | "serve": { 67 | "builder": "@angular-devkit/build-angular:dev-server", 68 | "options": { 69 | "browserTarget": "angular-color-picker:build" 70 | }, 71 | "configurations": { 72 | "production": { 73 | "aot": true, 74 | "browserTarget": "angular-color-picker:build:production" 75 | } 76 | } 77 | }, 78 | "extract-i18n": { 79 | "builder": "@angular-devkit/build-angular:extract-i18n", 80 | "options": { 81 | "browserTarget": "angular-color-picker:build" 82 | } 83 | }, 84 | "test": { 85 | "builder": "@angular-devkit/build-angular:karma", 86 | "options": { 87 | "main": "src/test.ts", 88 | "polyfills": "src/polyfills.ts", 89 | "tsConfig": "tsconfig.spec.json", 90 | "karmaConfig": "karma.conf.js", 91 | "assets": [ 92 | "src/favicon.ico", 93 | "src/assets" 94 | ], 95 | "styles": [ 96 | "src/styles.scss" 97 | ], 98 | "scripts": [] 99 | } 100 | }, 101 | "lint": { 102 | "builder": "@angular-devkit/build-angular:tslint", 103 | "options": { 104 | "tsConfig": [ 105 | "tsconfig.app.json", 106 | "tsconfig.spec.json", 107 | "e2e/tsconfig.json" 108 | ], 109 | "exclude": [ 110 | "**/node_modules/**" 111 | ] 112 | } 113 | }, 114 | "e2e": { 115 | "builder": "@angular-devkit/build-angular:protractor", 116 | "options": { 117 | "protractorConfig": "e2e/protractor.conf.js", 118 | "devServerTarget": "angular-color-picker:serve" 119 | }, 120 | "configurations": { 121 | "production": { 122 | "devServerTarget": "angular-color-picker:serve:production" 123 | } 124 | } 125 | }, 126 | "deploy": { 127 | "builder": "angular-cli-ghpages:deploy", 128 | "options": { 129 | "baseHref": "https://radial-color-picker.github.io/angular-color-picker/" 130 | } 131 | } 132 | } 133 | }, 134 | "radial-color-picker": { 135 | "projectType": "library", 136 | "root": "projects/radial-color-picker", 137 | "sourceRoot": "projects/radial-color-picker/src", 138 | "prefix": "rcp", 139 | "architect": { 140 | "build": { 141 | "builder": "@angular-devkit/build-ng-packagr:build", 142 | "options": { 143 | "tsConfig": "projects/radial-color-picker/tsconfig.lib.json", 144 | "project": "projects/radial-color-picker/ng-package.json" 145 | }, 146 | "configurations": { 147 | "production": { 148 | "tsConfig": "projects/radial-color-picker/tsconfig.lib.prod.json" 149 | } 150 | } 151 | }, 152 | "test": { 153 | "builder": "@angular-devkit/build-angular:karma", 154 | "options": { 155 | "main": "projects/radial-color-picker/src/test.ts", 156 | "tsConfig": "projects/radial-color-picker/tsconfig.spec.json", 157 | "karmaConfig": "projects/radial-color-picker/karma.conf.js" 158 | } 159 | }, 160 | "lint": { 161 | "builder": "@angular-devkit/build-angular:tslint", 162 | "options": { 163 | "tsConfig": [ 164 | "projects/radial-color-picker/tsconfig.lib.json", 165 | "projects/radial-color-picker/tsconfig.spec.json" 166 | ], 167 | "exclude": [ 168 | "**/node_modules/**" 169 | ] 170 | } 171 | } 172 | } 173 | } 174 | }, 175 | "defaultProject": "angular-color-picker", 176 | "cli": { 177 | "analytics": "d01eaac1-a998-474c-b6db-b244f49e807b" 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('angular-color-picker app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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/angular-color-picker'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 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 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-color-picker", 3 | "version": "0.0.3", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e", 11 | "build_lib": "ng build radial-color-picker --prod", 12 | "npm_pack": "cd dist/radial-color-picker && npm pack", 13 | "package": "npm run build_lib && npm run npm_pack" 14 | }, 15 | "private": false, 16 | "dependencies": { 17 | "@angular/animations": "~9.0.0", 18 | "@angular/cdk": "^9.0.0", 19 | "@angular/common": "~9.0.0", 20 | "@angular/compiler": "~9.0.0", 21 | "@angular/core": "~9.0.0", 22 | "@angular/forms": "~9.0.0", 23 | "@angular/material": "^9.0.0", 24 | "@angular/platform-browser": "~9.0.0", 25 | "@angular/platform-browser-dynamic": "~9.0.0", 26 | "@angular/router": "~9.0.0", 27 | "node-sass": "^4.13.1", 28 | "rxjs": "~6.5.4", 29 | "tslib": "^1.10.0", 30 | "zone.js": "~0.10.2" 31 | }, 32 | "devDependencies": { 33 | "@angular-devkit/build-angular": "~0.900.1", 34 | "@angular-devkit/build-ng-packagr": "~0.900.1", 35 | "@angular/cli": "~9.0.1", 36 | "@angular/compiler-cli": "~9.0.0", 37 | "@angular/language-service": "~9.0.0", 38 | "@types/jasmine": "~3.5.0", 39 | "@types/jasminewd2": "~2.0.3", 40 | "@types/node": "^12.11.1", 41 | "angular-cli-ghpages": "^0.6.2", 42 | "codelyzer": "^5.1.2", 43 | "jasmine-core": "~3.5.0", 44 | "jasmine-spec-reporter": "~4.2.1", 45 | "karma": "~4.3.0", 46 | "karma-chrome-launcher": "~3.1.0", 47 | "karma-coverage-istanbul-reporter": "~2.1.0", 48 | "karma-jasmine": "~2.0.1", 49 | "karma-jasmine-html-reporter": "^1.4.2", 50 | "ng-packagr": "^9.0.0", 51 | "protractor": "~5.4.3", 52 | "ts-node": "~8.3.0", 53 | "tsickle": "^0.38.0", 54 | "tslint": "~5.18.0", 55 | "typescript": "~3.7.5" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /projects/radial-color-picker/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zlati Pehlivanov 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/radial-color-picker/README.md: -------------------------------------------------------------------------------- 1 | # Angular Radial Color Picker 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.1. 4 | 5 |

6 | screenshot 7 |

8 | 9 |

10 | 11 | Downloads 12 | 13 | 14 | Version 15 | 16 | 17 | License 18 | 19 |

20 | 21 | ## Introduction 22 | 23 | Great UX starts with two basic principles - ease of use and simplicity. Selecting a color should be as easy as moving a slider, clicking a checkbox or pressing a key just like other basic form elements behave. 24 | 25 | This is a flexible and elegant material-ish design color picker. Developed with mobile devices in mind. Key features: 26 | * Supports touch devices 27 | * Angular animations 28 | 29 | This component doesn't have feature parity with 30 | it's [predecessor](https://github.com/talamaska/angular-radial-color-picker) for Angular 1.x 31 | 32 | The component is developed so it can be used with template-driven or reactive form, or as a simple component. 33 | 34 | ## Quick Links 35 | 36 | * [Usage](#usage) 37 | * [Inputs](#inputs) 38 | * [Outputs](#outputs) 39 | * [Instance methods](#instance-methods) 40 | * [Lifecycle events](#lifecycle) 41 | 42 | * [FAQ](#questions) 43 | * [Contribute](#contributing) 44 | 45 | 46 | ## Usage 47 | 48 | Color Picker on [npm](https://www.npmjs.com/package/@radial-color-picker/angular-color-picker) 49 | ```bash 50 | npm install -S @radial-color-picker/angular-color-picker 51 | ``` 52 | 53 | In your app module or any module that will hold the components that are using the radial color picker: 54 | 55 | ```typescript 56 | import { RadialColorPickerModule } from 'radial-color-picker'; 57 | 58 | @NgModule({ 59 | declarations: [ 60 | AppComponent 61 | ], 62 | imports: [ 63 | BrowserModule, 64 | RadialColorPickerModule 65 | ], 66 | providers: [], 67 | bootstrap: [AppComponent] 68 | }) 69 | export class AppModule { } 70 | ``` 71 | 72 | and in your component template 73 | ```html 74 | 75 | ``` 76 | 77 | more examples can be found in the app in this repository 78 | 79 | ## Inputs 80 | `` component has several inputs, all of which are optional. 81 | 82 | | Options | Type | Default/Description | 83 | |------------|--------|---------| 84 | | `selectToChange` | Boolean | prevents propagation of on change on FormControl or NgModel on rotation. Defaults to false:
`false`. | 85 | | `collapsed` | Boolean | should the initial render be collapsed/open state of the color picker. Defaults to true:
`true`. | 86 | | `collapsible` | Boolean | Should color select trigger hiding of the color palette. Defaults to true:
`true`. | 87 | | `enterAnimation` | Boolean | flag to turn on/off intro animations. Defaults to true:
`true`. | 88 | | `exitAnimation` | Boolean | flag to turn on/off outro animations. Defaults to true:
`true`. | 89 | | `size` | String | pixel size of diameter. Defaults to 300:
`300`. | 90 | | `colorType` | String | format of color to be emitted by the component. Defaults to hex:
`hex`. | 91 | | `color` | String | hex, rgb or hsl code of your color. Defaults to red:
`#FF0000`. | 92 | 93 | [Back To Top](#user-content-quick-links) 94 | 95 | 96 | ## Outputs 97 | `` component has several outputs, all of which are optional. 98 | 99 | | Options | Type | Default/Description | 100 | |------------|--------|---------| 101 | | `selected` | Function | Callback which is triggered when a color is selected. | 102 | | `colorChange` | Function | Callback which is triggered when color is changed (i.e. on rotation). | 103 | | `lifecycle` | Function | Callback which is triggered when component state is changed. | 104 | 105 | [Back To Top](#user-content-quick-links) 106 | 107 | 108 | ## Lifecycle Events 109 | 110 | Emitted by the lifecycle Output 111 | 112 | | Name | Description | 113 | |------------|-------------| 114 | | `show` | Fires when the color picker is about to show and **before** any animation is started. | 115 | | `shown` | Fires when the color picker is shown and has finished animating. | 116 | | `selected` | Fires when a color is selected via the middle selector. Event is fired right before `hide`. | 117 | | `hide` | Fires when the color picker is about to hide and **before** any animation is started. | 118 | | `hidden` | Fires when the color picker is hidden and has finished animating. | 119 | 120 | [Back To Top](#user-content-quick-links) 121 | 122 | 123 | ## Instance methods 124 | | Name | Description | 125 | |------------|-------------| 126 | | `open` | Programatically opens the color picker if it's not already opened. | 127 | | `close` | Programatically closes the color picker if it's not already closed. | 128 | 129 | 130 | [Back To Top](#user-content-quick-links) 131 | 132 | 133 | ## First Asked Questions 134 | 135 |
136 | Color picker uses hex. How can I use other formats like rgb() or hsl? 137 |

The color picker will recognize the format of the input, but will still work internally with hex value of the provided color. You can change the output format by setting the colorType property to hex, rgb or hsl

138 |
139 | 140 |
141 | How to select other shades of the solid colors? 142 |

We suggest to add a custom slider for saturation and luminosity or use <input type="range">.

143 |
144 | 145 |
146 | How can I change the active color of the picker after initialization? 147 |

rcp-radial-color-picker component uses OnChanges lifecycle hook to detect changes of the color binding. When using 148 | <rcp-radial-color-picker [color]="color"></rcp-radial-color-picker> 149 |

150 |
151 | 152 | [Back To Top](#user-content-quick-links) 153 | 154 | ## Contribute 155 | If you're interested in the project you can help out with feature requests, bugfixes, documentation improvements or any other helpful contributions. You can use the issue list of this repo for bug reports and feature requests and as well as for questions and support. 156 | 157 | We are also particularly interested in projects you did with this plugin. If you have created something colorful and creative with the color picker and want to show it off send us a quick mail. 158 | 159 | The project is using an adapted version of [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-angular/convention.md) and commit messages should adhere to it. 160 | 161 | [Back To Top](#user-content-quick-links) 162 | -------------------------------------------------------------------------------- /projects/radial-color-picker/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-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/radial-color-picker'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 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 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/radial-color-picker/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/radial-color-picker", 4 | "lib": { 5 | "entryFile": "src/public-api.ts", 6 | "styleIncludePaths": [ 7 | "src/lib/assets" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/radial-color-picker/ng-package.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/radial-color-picker", 4 | "lib": { 5 | "entryFile": "src/public_api.ts", 6 | "styleIncludePaths": ["src/lib/assets"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /projects/radial-color-picker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@radial-color-picker/angular-color-picker", 3 | "version": "0.0.3", 4 | "description": "Radial Color Picker - Angular", 5 | "author": "Zlati Pehlivanov (zlati.pehlivanov@gmail.com)", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/radial-color-picker/angular-color-picker.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/radial-color-picker/angular-color-picker/issues" 12 | }, 13 | "private": false, 14 | "homepage": "https://github.com/radial-color-picker/angular-color-picker", 15 | "keywords": [ 16 | "angular", 17 | "radial color picker", 18 | "color picker", 19 | "color-picker", 20 | "angular-color-picker", 21 | "hue picker" 22 | ], 23 | "license": "MIT", 24 | "peerDependencies": { 25 | "@angular/cdk": "^9.0.0", 26 | "@angular/common": "^9.0.0", 27 | "@angular/core": "^9.0.0", 28 | "@angular/animations": "^9.0.0", 29 | "@angular/platform-browser": "^9.0.0", 30 | "tslib": "^1.10.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/assets/lib.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | :host { 6 | box-sizing: border-box; 7 | 8 | * { 9 | box-sizing: border-box; 10 | } 11 | } 12 | 13 | 14 | $white: #ffffff; 15 | $black: #000000; 16 | $button-stable-border: #b2b2b2 !default; 17 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/color-preview/color-preview.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/color-preview/color-preview.component.scss: -------------------------------------------------------------------------------- 1 | @import 'lib'; 2 | 3 | :host { 4 | position: relative; 5 | z-index: 10; 6 | 7 | .color { 8 | position: absolute; 9 | z-index: 12; 10 | display: block; 11 | width: 100%; 12 | height: 100%; 13 | border: 6px solid $white; 14 | border-radius: 50%; 15 | box-shadow: 0 0 0 1px $button-stable-border; 16 | } 17 | 18 | .color-shadow { 19 | position: absolute; 20 | z-index: 11; 21 | width: 100%; 22 | height: 100%; 23 | border-width: 10px; 24 | border-style: solid; 25 | border-radius: 50%; 26 | } 27 | 28 | &.final-state-1 { 29 | transform: translate3d(0, 0, 0) scale3d(1, 1, 1); 30 | box-shadow: 0 0 1px 6px $white, 0 0 0 7px $button-stable-border; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/color-preview/color-preview.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ColorPreviewComponent } from './color-preview.component'; 4 | 5 | describe('ColorPreviewComponent', () => { 6 | let component: ColorPreviewComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ColorPreviewComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ColorPreviewComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/color-preview/color-preview.component.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line:max-line-length 2 | import { AfterViewInit, Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnChanges, OnInit, Output, OnDestroy } from '@angular/core'; 3 | import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; 4 | import { Animations } from '../../helpers/animations'; 5 | 6 | @Component({ 7 | selector: 'rcp-color-preview', 8 | templateUrl: './color-preview.component.html', 9 | styleUrls: ['./color-preview.component.scss'], 10 | animations: [ 11 | Animations.rippleAnimation, 12 | Animations.buttonAnimation 13 | ] 14 | }) 15 | export class ColorPreviewComponent implements OnInit, OnChanges, AfterViewInit, OnDestroy { 16 | public background: SafeStyle; 17 | 18 | public rippleState: any; 19 | public buttonState: any; 20 | 21 | public relation: number; 22 | 23 | @Input() coefficient: number; 24 | @Input() color: string; 25 | @Input() size: number; 26 | 27 | @Output() confirm = new EventEmitter(); 28 | 29 | @HostBinding('style.width') get width() { 30 | return (this.size && this.size < 200) ? '36px' : '70px'; 31 | } 32 | 33 | @HostBinding('style.height') get height() { 34 | return (this.size && this.size < 200) ? '36px' : '70px'; 35 | } 36 | 37 | @HostListener('click') onClick() { 38 | this.buttonState = { 39 | value: true, 40 | params: { 41 | scale: this.relation 42 | } 43 | }; 44 | this.rippleState = { 45 | value: true, 46 | params: { 47 | scale: this.relation 48 | } 49 | }; 50 | 51 | 52 | } 53 | 54 | constructor( 55 | private sanitizer: DomSanitizer, 56 | private el: ElementRef 57 | ) { 58 | 59 | 60 | } 61 | 62 | ngOnInit() { 63 | 64 | } 65 | 66 | ngAfterViewInit() { 67 | // console.log(this.size); 68 | const rect = this.el.nativeElement.getBoundingClientRect(); 69 | const innerCircle = this.size * this.coefficient; 70 | this.relation = innerCircle / rect.width; 71 | // console.log('relation', relation); 72 | } 73 | 74 | ngOnChanges(changes) { 75 | if (changes.color && changes.color.currentValue) { 76 | this.background = this.sanitizer.bypassSecurityTrustStyle(this.color); 77 | } 78 | } 79 | 80 | public rippleAnimationDone($event) { 81 | // console.log($event); 82 | if ($event.toState) { 83 | this.rippleState = false; 84 | this.confirm.emit({ 85 | color: this.color 86 | }); 87 | } 88 | } 89 | 90 | public buttonAnimationDone($event) { 91 | // console.log($event); 92 | if ($event.toState) { 93 | this.buttonState = false; 94 | } 95 | } 96 | 97 | ngOnDestroy() { 98 | // console.log('color preview destroy'); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/radial-color-picker/radial-color-picker.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 7 |
8 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/radial-color-picker/radial-color-picker.component.scss: -------------------------------------------------------------------------------- 1 | @import 'lib'; 2 | 3 | :host { 4 | display: block; 5 | overflow: hidden; 6 | border-radius: 50%; 7 | 8 | .wrapper { 9 | position: relative; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | width: 100%; 14 | height: 100%; 15 | border-radius: 50%; 16 | 17 | canvas { 18 | position: absolute; 19 | z-index: 1; 20 | top: 0; 21 | right: 0; 22 | bottom: 0; 23 | left: 0; 24 | opacity: 0; 25 | border-radius: 50%; 26 | box-shadow: 0 0 5px 0 rgba($black, 0.6); 27 | } 28 | 29 | .rotator { 30 | position: absolute; 31 | z-index: 2; 32 | display: flex; 33 | align-items: center; 34 | flex: 0 0 100%; 35 | flex-direction: column; 36 | justify-content: center; 37 | width: 100%; 38 | height: 100%; 39 | transform: translate3d(0,0,0); 40 | border-radius: 50%; 41 | backface-visibility: hidden; 42 | 43 | 44 | .knob { 45 | position: absolute; 46 | z-index: 11; 47 | width: 20px; 48 | height: 20px; 49 | pointer-events: none; 50 | opacity: 0; 51 | border-radius: 50%; 52 | background-color: $white; 53 | box-shadow: 0 0 3px 0 rgba($black, 0.6); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/radial-color-picker/radial-color-picker.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RadialColorPickerComponent } from './radial-color-picker.component'; 4 | 5 | describe('RadialColorPickerComponent', () => { 6 | let component: RadialColorPickerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ RadialColorPickerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(RadialColorPickerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/components/radial-color-picker/radial-color-picker.component.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line:max-line-length 2 | import { AnimationBuilder, AnimationPlayer } from '@angular/animations'; 3 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; 4 | import { 5 | AfterViewInit, 6 | Component, 7 | ElementRef, 8 | EventEmitter, 9 | forwardRef, 10 | HostBinding, 11 | Input, 12 | OnChanges, 13 | OnDestroy, 14 | OnInit, 15 | Output, 16 | Renderer2, 17 | SimpleChanges, 18 | ViewChild 19 | } from '@angular/core'; 20 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; 21 | import { AnimationsMeta } from '../../helpers/animations'; 22 | import { hexToRgb, hslToHex, rgbToHsl, extractRGB, rgbToHex, extractHSL } from '../../helpers/color-functions'; 23 | import { renderColorMap } from '../../helpers/color-gradient'; 24 | 25 | export const RADIAL_COLOR_PICKER_VALUE_ACCESSOR: any = { 26 | provide: NG_VALUE_ACCESSOR, 27 | useExisting: forwardRef(() => RadialColorPickerComponent), 28 | multi: true 29 | }; 30 | 31 | let nextUniqueId = 0; 32 | const rgbRegex = /rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/; 33 | const hslRegex = /hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/; 34 | enum RCPLifecycleEvents { 35 | show = 'show', 36 | shown = 'shown', 37 | selected = 'selected', 38 | hide = 'hide', 39 | hidden = 'hidden' 40 | } 41 | 42 | @Component({ 43 | selector: 'rcp-radial-color-picker', 44 | templateUrl: './radial-color-picker.component.html', 45 | styleUrls: ['./radial-color-picker.component.scss'], 46 | providers: [RADIAL_COLOR_PICKER_VALUE_ACCESSOR], 47 | }) 48 | export class RadialColorPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor { 49 | protected _uid = `rcp-${nextUniqueId++}`; 50 | 51 | public coefficient = 0.77; 52 | public hueValue = 266; 53 | public disabled = false; 54 | public active = false; 55 | 56 | public rect: ClientRect; 57 | 58 | public knobState = false; 59 | public gradientState = false; 60 | 61 | private _value = 'FF0000'; 62 | private defaultSize = 300; 63 | private gradientPlayer: AnimationPlayer; 64 | private knobPlayer: AnimationPlayer; 65 | 66 | @Input() public color: string; 67 | @Input() public colorType = 'hex'; 68 | @Input() public size: number; 69 | @Input() public enterAnimation = true; 70 | @Input() public exitAnimation = true; 71 | @Input() public selectToChange = false; 72 | @Input() public collapsed = true; 73 | @Input() public collapsible = true; 74 | 75 | @Output() public selected = new EventEmitter(); 76 | @Output() public colorChange = new EventEmitter(); 77 | @Output() public lifecycle = new EventEmitter(); 78 | 79 | public get isExplicit() { 80 | return coerceBooleanProperty(this.selectToChange); 81 | } 82 | 83 | public get hasEnterAnimation() { 84 | return coerceBooleanProperty(this.enterAnimation); 85 | } 86 | 87 | public get hasExitAnimation() { 88 | return coerceBooleanProperty(this.exitAnimation); 89 | } 90 | 91 | public get isCollapsible() { 92 | return coerceBooleanProperty(this.collapsible); 93 | } 94 | public get isCollapsed() { 95 | return coerceBooleanProperty(this.collapsed); 96 | } 97 | 98 | public get getSize() { 99 | return this.size ? this.size : this.defaultSize; 100 | } 101 | 102 | set value(value: any) { 103 | let val = value; 104 | if (value) { 105 | if (value instanceof Object) { 106 | val = value.value; 107 | } 108 | this._value = val; 109 | if (val.includes('#')) { 110 | this._value = val.substring(1); 111 | const rgb = hexToRgb(this._value); 112 | const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); 113 | this.hueValue = hsl.hue; 114 | } else if (val.includes('rgb')) { 115 | const color = extractRGB(val); 116 | const hsl = rgbToHsl(color.r, color.g, color.b); 117 | this._value = rgbToHex(color.r, color.g, color.b); 118 | this.hueValue = hsl.hue; 119 | } else if (val.includes('hsl')) { 120 | const color = extractHSL(val); 121 | this._value = hslToHex(color.h, 100, 50); 122 | this.hueValue = color.h; 123 | } 124 | 125 | // console.log('set value hue', this.hueValue); 126 | } 127 | 128 | if (!this.isExplicit) { 129 | this.notifyValueChange(); 130 | } 131 | } 132 | 133 | get value() { 134 | let color = this._value; 135 | 136 | color = '#' + this._value; 137 | return color; 138 | } 139 | 140 | @ViewChild('canvas', { static: false, read: ElementRef }) public canvas: ElementRef; 141 | @ViewChild('knob', { static: false, read: ElementRef }) public knob: ElementRef; 142 | @ViewChild('rotator', { static: false, read: ElementRef }) public rotator: ElementRef; 143 | 144 | @HostBinding('style.width.px') get width() { 145 | return this.size ? this.size : this.defaultSize; 146 | } 147 | 148 | @HostBinding('style.height.px') get height() { 149 | return this.size ? this.size : this.defaultSize; 150 | } 151 | 152 | onChange: (value) => {}; 153 | onTouched: () => {}; 154 | 155 | notifyValueChange() { 156 | if (this.onChange) { 157 | let color = this.value; 158 | const rgb = hexToRgb(this._value); 159 | const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); 160 | switch (this.colorType) { 161 | case 'hex': 162 | color = '#' + this._value; 163 | break; 164 | case 'rgb': 165 | color = `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`; 166 | break; 167 | case 'hsl': 168 | color = `hsl(${hsl.hue}, ${hsl.saturation}%, ${hsl.luminosity}%)`; 169 | break; 170 | default: 171 | color = '#' + this._value; 172 | } 173 | 174 | this.onChange(color); 175 | } 176 | } 177 | 178 | writeValue(obj: any): void { 179 | // console.log(obj); 180 | this.value = obj; 181 | } 182 | 183 | registerOnChange(fn: any): void { 184 | this.onChange = fn; 185 | } 186 | 187 | registerOnTouched(fn: any): void { 188 | this.onTouched = fn; 189 | } 190 | 191 | setDisabledState(isDisabled: boolean): void { 192 | this.disabled = isDisabled; 193 | } 194 | 195 | constructor( 196 | private el: ElementRef, 197 | private renderer: Renderer2, 198 | private animationBuilder: AnimationBuilder 199 | ) { 200 | 201 | } 202 | 203 | ngOnInit() { 204 | const rgb = hexToRgb(this._value); 205 | const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); 206 | this.hueValue = hsl.hue; 207 | } 208 | 209 | ngOnChanges(changes: SimpleChanges) { 210 | // console.log(changes); 211 | if (changes.color && changes.color.currentValue) { 212 | this.value = changes.color.currentValue; 213 | } 214 | if (changes.size && changes.size.currentValue) { 215 | this.recalculateKnobPosition(); 216 | } 217 | } 218 | 219 | ngAfterViewInit() { 220 | this.recalculateKnobPosition(); 221 | this.rect = this.el.nativeElement.getBoundingClientRect(); 222 | // console.log(this.rect); 223 | renderColorMap(this.canvas.nativeElement, this.getSize); 224 | // console.log(this.collapsed); 225 | if (this.isCollapsed) { 226 | this.introAnimation(); 227 | } 228 | } 229 | 230 | public open() { 231 | this.introAnimation(); 232 | } 233 | 234 | public close() { 235 | this.outroAnimation(); 236 | } 237 | 238 | public introAnimation() { 239 | this.lifecycle.emit(RCPLifecycleEvents.show); 240 | this.gradientState = true; 241 | this.createPlayerForGradient(this.hasEnterAnimation); 242 | this.gradientPlayer.onDone(() => { 243 | this.knobState = true; 244 | this.createPlayerForKnob(this.hasEnterAnimation); 245 | this.knobPlayer.onDone(() => { 246 | this.active = true; 247 | this.lifecycle.emit(RCPLifecycleEvents.shown); 248 | }); 249 | if (this.hasEnterAnimation) { 250 | this.knobPlayer.play(); 251 | } else { 252 | this.knobPlayer.finish(); 253 | } 254 | }); 255 | 256 | if (this.hasEnterAnimation) { 257 | this.gradientPlayer.play(); 258 | } else { 259 | this.gradientPlayer.finish(); 260 | } 261 | } 262 | 263 | public outroAnimation() { 264 | this.lifecycle.emit(RCPLifecycleEvents.hide); 265 | this.knobState = false; 266 | this.createPlayerForKnob(); 267 | this.knobPlayer.onDone(() => { 268 | this.gradientState = false; 269 | this.createPlayerForGradient(); 270 | this.gradientPlayer.onDone(() => { 271 | this.active = false; 272 | this.lifecycle.emit(RCPLifecycleEvents.hidden); 273 | }); 274 | if (this.hasExitAnimation) { 275 | this.gradientPlayer.play(); 276 | } else { 277 | this.gradientPlayer.finish(); 278 | } 279 | }); 280 | if (this.hasExitAnimation) { 281 | this.knobPlayer.play(); 282 | } else { 283 | this.knobPlayer.finish(); 284 | } 285 | } 286 | 287 | public onRotate(rotation) { 288 | const hex = hslToHex(this.angleToHue(rotation), 100, 50); 289 | this.value = hex; 290 | // console.log('on rotate', this.isExplicit); 291 | if (!this.isExplicit) { 292 | this.colorChange.emit(`#${hex}`); 293 | } 294 | } 295 | 296 | public angleToHue(rotation) { 297 | return rotation - 90; 298 | } 299 | 300 | public recalculateKnobPosition() { 301 | const radius = (this.getSize / 2); 302 | const innerCircle = radius * this.coefficient; 303 | const areaSize = radius - innerCircle; 304 | if (this.knob) { 305 | const knobRect = this.knob.nativeElement.getBoundingClientRect(); 306 | const knobPosition = radius - (areaSize / 2 + innerCircle) - knobRect.width / 2; 307 | this.renderer.setStyle(this.knob.nativeElement, 'top', knobPosition + 'px'); 308 | } 309 | } 310 | 311 | public confirmColor($event) { 312 | // console.log('confirm color', $event); 313 | if (!this.isCollapsible) { 314 | this.selected.emit($event.color); 315 | this.lifecycle.emit(RCPLifecycleEvents.selected); 316 | this.notifyValueChange(); 317 | return; 318 | } 319 | 320 | // is color picker collapsed 321 | if (this.knobState) { 322 | this.selected.emit($event.color); 323 | this.lifecycle.emit(RCPLifecycleEvents.selected); 324 | this.notifyValueChange(); 325 | this.outroAnimation(); 326 | } else { 327 | this.introAnimation(); 328 | } 329 | } 330 | 331 | public createPlayerForGradient(hasAnimation = true) { 332 | if (this.gradientPlayer) { 333 | this.gradientPlayer.destroy(); 334 | } 335 | let animationFactory; 336 | 337 | if (this.gradientState) { 338 | animationFactory = this.animationBuilder 339 | .build(AnimationsMeta.gradientAnimationEnter); 340 | } else { 341 | animationFactory = this.animationBuilder 342 | .build(AnimationsMeta.gradientAnimationExit); 343 | } 344 | 345 | this.gradientPlayer = animationFactory.create(this.canvas.nativeElement); 346 | } 347 | 348 | public createPlayerForKnob(hasAnimation = true) { 349 | if (this.knobPlayer) { 350 | this.knobPlayer.destroy(); 351 | } 352 | let animationFactory; 353 | 354 | if (this.knobState) { 355 | animationFactory = this.animationBuilder 356 | .build(AnimationsMeta.knobAnimationEnter); 357 | } else { 358 | animationFactory = this.animationBuilder 359 | .build(AnimationsMeta.knobAnimationExit); 360 | } 361 | 362 | this.knobPlayer = animationFactory.create(this.knob.nativeElement); 363 | } 364 | 365 | ngOnDestroy() { 366 | if (this.knobPlayer) { 367 | this.knobPlayer.destroy(); 368 | } 369 | if (this.gradientPlayer) { 370 | this.gradientPlayer.destroy(); 371 | } 372 | // console.log('color picker destroy'); 373 | } 374 | } 375 | 376 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/directives/rotatable.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { RotatableDirective } from './rotatable.directive'; 2 | 3 | describe('RotatableDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new RotatableDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/directives/rotatable.directive.ts: -------------------------------------------------------------------------------- 1 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; 2 | import { Platform } from '@angular/cdk/platform'; 3 | import { 4 | AfterViewInit, 5 | Directive, 6 | ElementRef, 7 | EventEmitter, 8 | Input, 9 | OnChanges, 10 | OnDestroy, 11 | OnInit, 12 | Output, 13 | Renderer2, 14 | SimpleChanges 15 | } from '@angular/core'; 16 | import { fromEvent, Observable, Subscription, merge } from 'rxjs'; 17 | import { filter, takeUntil, tap } from 'rxjs/operators'; 18 | import { calculateQuadrant, determineCSSRotationAngle } from '../helpers/helpers'; 19 | 20 | @Directive({ 21 | selector: '[rcpRotatable]' 22 | }) 23 | export class RotatableDirective implements OnInit, OnChanges, OnDestroy, AfterViewInit { 24 | public rotation: any; 25 | public dragging = false; 26 | public mouseDownSub: Subscription; 27 | public mouseMoveSub: Subscription; 28 | public mouseUpSub: Subscription; 29 | public rect: any; 30 | 31 | public cancelEv: string; 32 | public mouseDownEv: string; 33 | public mouseMoveEv: string; 34 | public mouseUpEv: string; 35 | 36 | @Input() angle: number; 37 | @Input() disable: boolean; 38 | @Input() active: boolean; 39 | 40 | get isDisabled() { 41 | return this.disable ? coerceBooleanProperty(this.disable) : false; 42 | } 43 | 44 | @Output() public rotateStart = new EventEmitter(); 45 | @Output() public rotating = new EventEmitter(); 46 | @Output() public rotateStop = new EventEmitter(); 47 | 48 | private point: any; 49 | private mouseUp$: Observable; 50 | private mouseOut$: Observable; 51 | 52 | constructor( 53 | private el: ElementRef, 54 | private renderer: Renderer2, 55 | private platform: Platform 56 | ) { 57 | if (this.platform.IOS || this.platform.ANDROID) { 58 | this.mouseDownEv = 'touchstart'; 59 | this.mouseUpEv = 'touchend'; 60 | this.mouseMoveEv = 'touchmove'; 61 | this.cancelEv = 'touchcancel'; 62 | } else { 63 | this.mouseDownEv = 'mousedown'; 64 | this.mouseUpEv = 'mouseup'; 65 | this.mouseMoveEv = 'mousemove'; 66 | this.cancelEv = 'mouseout'; 67 | } 68 | 69 | } 70 | 71 | ngOnInit() { 72 | 73 | } 74 | 75 | ngOnChanges(changes: SimpleChanges): void { 76 | if (changes.angle && changes.angle.currentValue) { 77 | // console.log(changes.angle.currentValue); 78 | const angle = changes.angle.currentValue + 90; 79 | this.renderer.setStyle(this.el.nativeElement, 'transform', `rotate(${angle}deg)`); 80 | } 81 | } 82 | 83 | ngAfterViewInit() { 84 | // console.log(this.isDisabled); 85 | requestAnimationFrame(this.initialRender.bind(this)); 86 | this.rect = this.el.nativeElement.getBoundingClientRect(); 87 | this.mouseUp$ = fromEvent(this.el.nativeElement, this.mouseUpEv, { passive: true }); 88 | this.mouseOut$ = fromEvent(this.el.nativeElement, this.cancelEv, { passive: true }); 89 | 90 | this.mouseDownSub = fromEvent(this.el.nativeElement, this.mouseDownEv, { passive: true }) 91 | .pipe( 92 | filter((val) => { 93 | return this.active && !this.isDisabled; 94 | }) 95 | ) 96 | .subscribe((downEvent) => { 97 | this.dragging = true; 98 | this.rect = this.el.nativeElement.getBoundingClientRect(); 99 | // console.log('mouse down', downEvent, this.rect); 100 | this.point = this.createPoint(downEvent); 101 | this.rotateStart.emit(this.point); 102 | this.applyRotation(); 103 | 104 | 105 | this.mouseMoveSub = fromEvent(this.el.nativeElement, this.mouseMoveEv).pipe( 106 | takeUntil(merge(this.mouseOut$, this.mouseUp$).pipe( 107 | tap((upEvent) => { 108 | this.rect = this.el.nativeElement.getBoundingClientRect(); 109 | // console.log('mouse up', upEvent, this.rect); 110 | this.dragging = false; 111 | this.mouseMoveSub.unsubscribe(); 112 | this.rotateStop.emit(this.point); 113 | }) 114 | )) 115 | ).subscribe((moveEvent: MouseEvent) => { 116 | this.rect = this.el.nativeElement.getBoundingClientRect(); 117 | // console.log('mouse move', moveEvent, this.rect); 118 | 119 | this.point = this.createPoint(moveEvent); 120 | // console.log(this.point); 121 | this.applyRotation(); 122 | }); 123 | }); 124 | } 125 | 126 | public initialRender() { 127 | const angle = this.angle + 90; 128 | this.renderer.setStyle(this.el.nativeElement, 'transform', `rotate(${angle}deg)`); 129 | } 130 | 131 | public rotationRender() { 132 | // console.log(this.rotation); 133 | this.renderer.setStyle(this.el.nativeElement, 'transform', `rotate(${this.rotation}deg)`); 134 | } 135 | 136 | ngOnDestroy() { 137 | if (this.mouseDownSub) { 138 | this.mouseDownSub.unsubscribe(); 139 | } 140 | if (this.mouseMoveSub) { 141 | this.mouseMoveSub.unsubscribe(); 142 | } 143 | if (this.mouseUpSub) { 144 | this.mouseUpSub.unsubscribe(); 145 | } 146 | // console.log('directive destroy'); 147 | } 148 | 149 | 150 | 151 | private applyRotation() { 152 | const quadrant = calculateQuadrant(this.point); 153 | const rotation = determineCSSRotationAngle(this.point, quadrant); 154 | // console.log(rotation); 155 | this.rotating.emit(rotation); 156 | this.rotation = rotation; 157 | requestAnimationFrame(this.rotationRender.bind(this)); 158 | 159 | } 160 | 161 | private createPoint(mouseEvent) { 162 | let point; 163 | if (mouseEvent.targetTouches) { 164 | point = { 165 | x: this._normalizeX(mouseEvent.targetTouches[0].clientX), 166 | y: this._normalizeY(mouseEvent.targetTouches[0].clientY) 167 | }; 168 | } else { 169 | point = { 170 | x: this._normalizeX(mouseEvent.clientX), 171 | y: this._normalizeY(mouseEvent.clientY) 172 | }; 173 | } 174 | // console.log('point', point); 175 | return point; 176 | } 177 | 178 | private _normalizeX(coordX) { 179 | return coordX - this.rect.left - this.rect.width / 2; 180 | } 181 | 182 | private _normalizeY(coordY) { 183 | return ((coordY - this.rect.top) * -1) + this.rect.height / 2; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/helpers/animations.ts: -------------------------------------------------------------------------------- 1 | import { animate, AnimationMetadata, AnimationTriggerMetadata, keyframes, query, state, style, transition, trigger } from '@angular/animations'; 2 | 3 | export const Animations: { 4 | buttonAnimation: AnimationTriggerMetadata; 5 | rippleAnimation: AnimationTriggerMetadata; 6 | } = { 7 | buttonAnimation: trigger('buttonAnimation', [ 8 | state('void', style({ transform: 'scale3d(1, 1, 1)' })), 9 | state('false', style({ transform: 'scale3d(1, 1, 1)' })), 10 | state('true', style({ transform: 'scale3d(1, 1, 1)' })), 11 | transition('* => 1', [ 12 | animate('400ms cubic-bezier(0.35, 0, 0.25, 1)', keyframes([ 13 | style({ transform: 'scale3d(1, 1, 1)', offset: 0 }), 14 | style({ transform: 'scale3d(0.8, 0.8, 1)', offset: 0.25 }), 15 | style({ transform: 'scale3d(1, 1, 1)', offset: 0.5 }), 16 | style({ transform: 'scale3d(1, 1, 1)', offset: 1.0 }), 17 | ])), 18 | ]) 19 | ]), 20 | rippleAnimation: trigger('rippleAnimation', [ 21 | state('void', style({ opacity: 0, transform: 'scale3d(0.8, 0.8, 1)' })), 22 | state('false', style({ opacity: 0, transform: 'scale3d(0.8, 0.8, 1)' })), 23 | state('true', style({ opacity: 0, transform: 'scale3d(0.8, 0.8, 1)' })), 24 | transition('* => 1', [ 25 | query(':self', 26 | animate('400ms cubic-bezier(0.35, 0, 0.25, 1)', keyframes([ 27 | style({ opacity: 0.2, transform: 'scale3d(0.8, 0.8, 1)', offset: 0 }), 28 | style({ opacity: 0.2, transform: 'scale3d(0.8, 0.8, 1)', offset: 0.20 }), 29 | style({ opacity: 0, borderWidth: 0, transform: 'scale3d({{scale}}, {{scale}}, 1)', offset: 1.0 }), 30 | ])), 31 | { 32 | params: { 33 | scale: '3.8' 34 | } 35 | } 36 | ) 37 | ]) 38 | ]) 39 | }; 40 | 41 | export const AnimationsMeta: { 42 | knobAnimationEnter: AnimationMetadata[]; 43 | knobAnimationExit: AnimationMetadata[]; 44 | gradientAnimationEnter: AnimationMetadata[]; 45 | gradientAnimationExit: AnimationMetadata[]; 46 | } = { 47 | knobAnimationEnter: [ 48 | style({ opacity: 1, transform: 'scale3d(1, 1, 1)' }), 49 | animate('150ms cubic-bezier(0.4, 0.0, 1, 1)', keyframes([ 50 | style({ opacity: 1, transform: 'scale3d(0, 0, 1)', offset: 0 }), 51 | style({ opacity: 1, transform: 'scale3d(1, 1, 1)', offset: 1.0 }), 52 | ])) 53 | ], 54 | knobAnimationExit: [ 55 | style({ opacity: 0, transform: 'scale3d(0, 0, 1)' }), 56 | animate('100ms cubic-bezier(0.0, 0.0, 0.2, 1)', keyframes([ 57 | style({ opacity: 1, transform: 'scale3d(1, 1, 1)', offset: 0 }), 58 | style({ opacity: 1, transform: 'scale3d(0, 0, 1)', offset: 1.0 }), 59 | ])) 60 | ], 61 | gradientAnimationEnter: [ 62 | 63 | style({ opacity: 1, transform: 'scale3d(1, 1, 1)' }), 64 | animate('200ms cubic-bezier(0.4, 0.0, 1, 1)', keyframes([ 65 | style({ opacity: 1, transform: 'scale3d(0, 0, 1)', offset: 0 }), 66 | style({ opacity: 1, transform: 'scale3d(1, 1, 1)', offset: 1.0 }), 67 | ])) 68 | ], 69 | gradientAnimationExit: [ 70 | style({ opacity: 0, transform: 'scale3d(0, 0, 1)' }), 71 | animate('150ms cubic-bezier(0.0, 0.0, 0.2, 1)', keyframes([ 72 | style({ opacity: 1, transform: 'scale3d(1, 1, 1)', offset: 0 }), 73 | style({ opacity: 1, transform: 'scale3d(0, 0, 1)', offset: 1.0 }), 74 | ])) 75 | ], 76 | 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/helpers/color-functions.ts: -------------------------------------------------------------------------------- 1 | export const hexToRgb = (hex) => { 2 | const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 3 | const r = parseInt(result[1], 16); 4 | const g = parseInt(result[2], 16); 5 | const b = parseInt(result[3], 16); 6 | 7 | return { r, g, b }; 8 | }; 9 | export const extractRGB = (rgb) => { 10 | const result = /^(?:rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\))$/i.exec(rgb); 11 | const r = parseInt(result[1], 10); 12 | const g = parseInt(result[2], 10); 13 | const b = parseInt(result[3], 10); 14 | 15 | return { r, g, b }; 16 | }; 17 | export const extractHSL = (hsl) => { 18 | const result = /^(?:hsl\((\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\))$/i.exec(hsl); 19 | const h = parseInt(result[1], 10); 20 | const s = parseInt(result[2], 10); 21 | const l = parseInt(result[3], 10); 22 | 23 | return { h, s, l }; 24 | }; 25 | 26 | 27 | 28 | /** 29 | * Converts RGB color model to hexadecimal string. 30 | * 31 | * @memberOf Utilities 32 | * 33 | * @param r Integer between 0 and 255 34 | * @param g Integer between 0 and 255 35 | * @param b Integer between 0 and 255 36 | * 37 | * @return 6 char long hex string 38 | */ 39 | export const rgbToHex = (r: number, g: number, b: number): string => { 40 | // tslint:disable-next-line:no-bitwise 41 | return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); 42 | }; 43 | 44 | /** 45 | * Converts RGB color model to HSL model. 46 | * 47 | * @memberOf Utilities 48 | * 49 | * @param r Integer between 0 and 255 50 | * @param g Integer between 0 and 255 51 | * @param Integer between 0 and 255 52 | * 53 | * @return The HSL representation containing the hue (in degrees), 54 | * saturation (in percentage) and luminosity (in percentage) fields. 55 | */ 56 | export const rgbToHsl = (r: number, g: number, b: number): { hue: number, saturation: number, luminosity: number } => { 57 | r = r / 255; 58 | g = g / 255; 59 | b = b / 255; 60 | 61 | let h, s; 62 | const max = Math.max(r, g, b); 63 | const min = Math.min(r, g, b); 64 | const l = (max + min) / 2; 65 | 66 | if (max === min) { 67 | h = s = 0; // achromatic 68 | } else { 69 | const d = max - min; 70 | 71 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 72 | 73 | if (max === r) { 74 | h = (g - b) / d + (g < b ? 6 : 0); 75 | } 76 | if (max === g) { 77 | h = (b - r) / d + 2; 78 | } 79 | if (max === b) { 80 | h = (r - g) / d + 4; 81 | } 82 | } 83 | 84 | return { 85 | hue: h * 60, 86 | saturation: s * 100, 87 | luminosity: l * 100 88 | }; 89 | }; 90 | 91 | /** 92 | * Converts HSL color model to hexademical string. 93 | * 94 | * @memberOf Utilities 95 | * 96 | * @param h Integer between 0 and 360 97 | * @param s Integer between 0 and 100 98 | * @param l Integer between 0 and 50 99 | * 100 | * @return 6 char long hex string 101 | */ 102 | export const hslToHex = (h: number, s: number, l: number): string => { 103 | const colorModel = hslToRgb(h, s, l); 104 | 105 | return rgbToHex(colorModel.red, colorModel.green, colorModel.blue); 106 | }; 107 | 108 | /** 109 | * Converts HSL color model to RGB model. 110 | * Shamelessly taken from http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c 111 | * 112 | * @memberOf Utilities 113 | * 114 | * @param h The hue. Number in the 0-360 range 115 | * @param s The saturation. Number in the 0-100 range 116 | * @param l The luminosity. Number in the 0-100 range 117 | * 118 | * @return The RGB representation containing the red, green and blue fields 119 | */ 120 | export const hslToRgb = (h, s, l): { red: number, green: number, blue: number } => { 121 | let r, g, b; 122 | 123 | h = h / 360; 124 | s = s / 100; 125 | l = l / 100; 126 | 127 | if (s === 0) { 128 | r = g = b = l; // achromatic 129 | } else { 130 | const q = l < 0.5 ? l * (1 + s) : l + s - l * s; 131 | const p = 2 * l - q; 132 | 133 | r = _hue2rgb(p, q, h + 1 / 3); 134 | g = _hue2rgb(p, q, h); 135 | b = _hue2rgb(p, q, h - 1 / 3); 136 | } 137 | 138 | return { 139 | red: Math.round(r * 255), 140 | green: Math.round(g * 255), 141 | blue: Math.round(b * 255) 142 | }; 143 | }; 144 | 145 | export const _hue2rgb = (p, q, t) => { 146 | if (t < 0) { 147 | t += 1; 148 | } 149 | if (t > 1) { 150 | t -= 1; 151 | } 152 | if (t < 1 / 6) { 153 | return p + (q - p) * 6 * t; 154 | } 155 | if (t < 1 / 2) { 156 | return q; 157 | } 158 | if (t < 2 / 3) { 159 | return p + (q - p) * (2 / 3 - t) * 6; 160 | } 161 | 162 | return p; 163 | }; 164 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/helpers/color-gradient.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Modified version of Lea Verou's 3 | * {@link https://github.com/leaverou/conic-gradient conic-gradient}. 4 | * 5 | * @example 6 | * paintColorWheelToCanvas(document.querySelector('#canvas'), 250); 7 | * 8 | * @param canvas Canvas to paint the color wheel 9 | * @param size Color wheel diameter in pixels 10 | * @returns canvas The passed canvas for easier chaining 11 | */ 12 | export const paintColorWheelToCanvas = (canvas: HTMLCanvasElement, size: number): HTMLCanvasElement => { 13 | const half = size / 2; 14 | const radius = Math.sqrt(2) * half; 15 | const deg = Math.PI / 180; 16 | const pi2 = Math.PI * 2; 17 | 18 | canvas.width = canvas.height = size; 19 | const ctx = canvas.getContext('2d'); 20 | 21 | // .02: To prevent empty blank line and corresponding moire 22 | // only non-alpha colors are cared now 23 | const thetaOffset = 0.5 * deg + 0.02; 24 | 25 | // Transform coordinate system so that angles start from the top left, like in CSS 26 | ctx.translate(half, half); 27 | ctx.rotate(-Math.PI / 2); 28 | ctx.translate(-half, -half); 29 | 30 | for (let i = 0; i < 360; i += 0.5) { 31 | ctx.fillStyle = `hsl(${i}, 100%, 50%)`; 32 | ctx.beginPath(); 33 | ctx.moveTo(half, half); 34 | 35 | const beginArg = i * deg; 36 | const endArg = Math.min(pi2, beginArg + thetaOffset); 37 | 38 | ctx.arc(half, half, radius, beginArg, endArg); 39 | 40 | ctx.closePath(); 41 | ctx.fill(); 42 | } 43 | 44 | return canvas; 45 | }; 46 | 47 | /** 48 | * 49 | * @param canvas Canvas to paint the color wheel 50 | * @param diameter Color wheel diameter in pixels 51 | * @param coefficient Relation between inner white circle outer border and color circle outer border, controls the width of the color gradient path 52 | * @returns canvas The passed canvas for easier chaining 53 | */ 54 | export const renderColorMap = (canvas: HTMLCanvasElement, diameter: number, coefficient: number = 0.77): HTMLCanvasElement => { 55 | canvas.width = canvas.height = diameter; 56 | const radius = diameter / 2; 57 | const toRad = (2 * Math.PI) / 360; 58 | const step = 0.2; 59 | const aliasing = 1; 60 | 61 | 62 | const ctx = canvas.getContext('2d'); 63 | ctx.clearRect(0, 0, diameter, diameter); 64 | for (let i = 0; i < 360; i += step) { 65 | const sRad = (i - aliasing) * toRad; 66 | const eRad = (i + step) * toRad; 67 | ctx.beginPath(); 68 | ctx.arc(radius, radius, radius / 2, sRad, eRad, false); 69 | ctx.strokeStyle = 'hsl(' + i + ', 100%, 50%)'; 70 | ctx.lineWidth = radius; 71 | ctx.closePath(); 72 | ctx.stroke(); 73 | } 74 | 75 | ctx.fillStyle = 'rgb(255, 255, 255)'; 76 | ctx.beginPath(); 77 | ctx.arc(radius, radius, radius * coefficient, 0, Math.PI * 2, true); 78 | ctx.closePath(); 79 | ctx.fill(); 80 | 81 | ctx.strokeStyle = 'rgb(255, 255, 255)'; 82 | ctx.lineWidth = 2; 83 | ctx.beginPath(); 84 | ctx.arc(radius, radius, radius, 0, 2 * Math.PI); 85 | ctx.stroke(); 86 | 87 | 88 | return canvas; 89 | }; 90 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/helpers/constants.ts: -------------------------------------------------------------------------------- 1 | export const Cache = { 2 | sin90: Math.sin(270 * Math.PI / 180), 3 | sin180: Math.sin(180 * Math.PI / 180), 4 | sin270: Math.sin(90 * Math.PI / 180), 5 | cos90: Math.cos(270 * Math.PI / 180), 6 | cos180: Math.cos(180 * Math.PI / 180), 7 | cos270: Math.cos(90 * Math.PI / 180) 8 | }; 9 | 10 | export const Quadrant = { 11 | I: 'q1', 12 | II: 'q2', 13 | III: 'q3', 14 | IV: 'q4' 15 | }; 16 | 17 | export const bezierCurves = { 18 | // Standard easing puts subtle attention at the end of an animation, 19 | // by giving more time to deceleration than acceleration.It is the most common form of easing. 20 | standard: 'cubic-bezier(0.4, 0.0, 0.2, 1)', 21 | // Elements exiting a screen use acceleration easing, where they start at rest and end at peak velocity. 22 | acc: 'cubic-bezier(0.4, 0.0, 1, 1)', 23 | // Incoming elements are animated using deceleration easing, 24 | // which starts a transition at peak velocity(the fastest point of an element’s movement) and ends at rest. 25 | dec: 'cubic-bezier(0.0, 0.0, 0.2, 1)' 26 | }; 27 | 28 | export const timings = { 29 | simpleMicro: '100ms', 30 | simpleEnter: '150ms', 31 | simpleExit: '75ms', 32 | complexEnter: '250ms', 33 | complexExit: '200ms', 34 | largeEnter: '300ms', 35 | largeExit: '250ms' 36 | }; 37 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/helpers/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Quadrant, Cache } from './constants'; 2 | 3 | /** 4 | * Calculates in which quadrant is the point, serves for calculating the right angle. 5 | * 6 | * @param point x,y coordinates of client's pointer position 7 | */ 8 | export const calculateQuadrant = (point: { x: number, y: number }): string => { 9 | if (point.x > 0) { 10 | if (point.y > 0) { 11 | return Quadrant.I; 12 | } else { 13 | return Quadrant.IV; 14 | } 15 | } else { 16 | if (point.y > 0) { 17 | return Quadrant.II; 18 | } else { 19 | return Quadrant.III; 20 | } 21 | } 22 | }; 23 | 24 | 25 | /** 26 | * Calculates the distance between two points. 27 | * 28 | * This variant expects separate x/y values for each point. If you already have 29 | * the points as array or object use the corresponding methods. 30 | * 31 | * @param x1 The X value of the first point. 32 | * @param y1 The Y value of the first point. 33 | * @param x2 The X value of the second point. 34 | * @param y2 The Y value of the second point. 35 | * @return The distance between the two points. 36 | */ 37 | export const distanceOfSegmentByXYValues = (x1: number, y1: number, x2: number, y2: number): number => { 38 | return Math.sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2))); 39 | }; 40 | 41 | /** 42 | * Calculates the angle of rotation 43 | * 44 | * @param point x,y coordinates of client's pointer position 45 | * @param quadrant one of four quarters of the coordinate plane 46 | */ 47 | export const determineCSSRotationAngle = (point, quadrant) => { 48 | let cx = point.x; 49 | let cy = point.y; 50 | let add = 0; 51 | switch (quadrant) { 52 | case Quadrant.II: 53 | add = 270; 54 | cx = ((point.x * Cache.cos90) - (point.y * Cache.sin90)); 55 | cy = ((point.x * Cache.sin90) + (point.y * Cache.cos90)); 56 | break; 57 | case Quadrant.III: 58 | add = 180; 59 | cx = ((point.x * Cache.cos180) - (point.y * Cache.sin180)); 60 | cy = ((point.x * Cache.sin180) + (point.y * Cache.cos180)); 61 | break; 62 | case Quadrant.IV: 63 | add = 90; 64 | cx = ((point.x * Cache.cos270) - (point.y * Cache.sin270)); 65 | cy = ((point.x * Cache.sin270) + (point.y * Cache.cos270)); 66 | break; 67 | } 68 | 69 | const rotation = Math.atan((distanceOfSegmentByXYValues(0, cy, cx, cy)) / (distanceOfSegmentByXYValues(0, cy, 0, 0))); 70 | 71 | return (rotation * (180 / Math.PI)) + add; 72 | }; 73 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/lib/radial-color-picker.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { BrowserModule } from '@angular/platform-browser'; 5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 6 | import { ColorPreviewComponent } from './components/color-preview/color-preview.component'; 7 | import { RadialColorPickerComponent } from './components/radial-color-picker/radial-color-picker.component'; 8 | import { RotatableDirective } from './directives/rotatable.directive'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | FormsModule, 14 | BrowserModule, 15 | BrowserAnimationsModule 16 | ], 17 | declarations: [ 18 | RotatableDirective, 19 | ColorPreviewComponent, 20 | RadialColorPickerComponent 21 | ], 22 | exports: [ 23 | RotatableDirective, 24 | RadialColorPickerComponent 25 | ] 26 | }) 27 | export class RadialColorPickerModule { } 28 | -------------------------------------------------------------------------------- /projects/radial-color-picker/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of radial-color-picker 3 | */ 4 | 5 | export * from './lib/helpers/color-functions'; 6 | export * from './lib/helpers/color-gradient'; 7 | export * from './lib/helpers/constants'; 8 | export * from './lib/helpers/helpers'; 9 | export * from './lib/directives/rotatable.directive'; 10 | export * from './lib/components/radial-color-picker/radial-color-picker.component'; 11 | export * from './lib/components/color-preview/color-preview.component'; 12 | export * from './lib/radial-color-picker.module'; 13 | -------------------------------------------------------------------------------- /projects/radial-color-picker/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'; 4 | import 'zone.js/dist/zone-testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | declare const require: { 12 | context(path: string, deep?: boolean, filter?: RegExp): { 13 | keys(): string[]; 14 | (id: string): T; 15 | }; 16 | }; 17 | 18 | // First, initialize the Angular testing environment. 19 | getTestBed().initTestEnvironment( 20 | BrowserDynamicTestingModule, 21 | platformBrowserDynamicTesting() 22 | ); 23 | // Then we find all the tests. 24 | const context = require.context('./', true, /\.spec\.ts$/); 25 | // And load the modules. 26 | context.keys().map(context); 27 | -------------------------------------------------------------------------------- /projects/radial-color-picker/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ], 13 | "paths": { 14 | "@angular/*": [ 15 | "../../node_modules/@angular/*" 16 | ] 17 | } 18 | }, 19 | "angularCompilerOptions": { 20 | "skipTemplateCodegen": true, 21 | "strictMetadataEmit": true, 22 | "enableResourceInlining": true, 23 | "enableIvy": false 24 | }, 25 | "exclude": [ 26 | "src/test.ts", 27 | "**/*.spec.ts" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /projects/radial-color-picker/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "angularCompilerOptions": { 4 | "enableIvy": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /projects/radial-color-picker/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/radial-color-picker/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "rcp", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "rcp", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /screenshots/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/screenshots/thumbnail.png -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { HomeComponent } from './components/home/home.component'; 4 | import { SimpleComponent } from './components/simple/simple.component'; 5 | import { NgmodelComponent } from './components/ngmodel/ngmodel.component'; 6 | import { FormcontrolComponent } from './components/formcontrol/formcontrol.component'; 7 | import { NoanimationsComponent } from './components/noanimations/noanimations.component'; 8 | import { ClosedComponent } from './components/closed/closed.component'; 9 | import { CollapsibleComponent } from './components/collapsible/collapsible.component'; 10 | import { PagenotfoundComponent } from './components/pagenotfound/pagenotfound.component'; 11 | 12 | const routes: Routes = [ 13 | { path: '', redirectTo: 'home', pathMatch: 'full' }, 14 | { path: 'home', component: HomeComponent }, 15 | { path: 'simple', component: SimpleComponent }, 16 | { path: 'ngmodel', component: NgmodelComponent }, 17 | { path: 'form-control', component: FormcontrolComponent }, 18 | { path: 'no-animations', component: NoanimationsComponent }, 19 | { path: 'closed', component: ClosedComponent }, 20 | { path: 'collapsible', component: CollapsibleComponent }, 21 | { path: '**', component: PagenotfoundComponent }, 22 | ]; 23 | 24 | @NgModule({ 25 | imports: [RouterModule.forRoot(routes)], 26 | exports: [RouterModule] 27 | }) 28 | export class AppRoutingModule {} 29 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 |

Angular Radial color picker Demo

7 |
8 | 9 | 11 | 12 | Home 13 | Simple Example 14 | Ng Model Example 15 | Reactive Forms Example 16 | No enter and exit animation example 17 | Inital state is closed 18 | Blocked collapsing toggle of the color picker 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | } 6 | 7 | .example-container { 8 | display: flex; 9 | flex-direction: column; 10 | position: absolute; 11 | top: 0; 12 | bottom: 0; 13 | left: 0; 14 | right: 0; 15 | } 16 | 17 | .example-is-mobile .example-toolbar { 18 | position: fixed; 19 | /* Make sure the toolbar will stay on top of the content as it scrolls past. */ 20 | z-index: 2; 21 | } 22 | 23 | h1.example-app-name { 24 | margin-left: 8px; 25 | } 26 | 27 | .example-sidenav-container { 28 | /* When the sidenav is not fixed, stretch the sidenav container to fill the available space. This 29 | causes `` to act as our scrolling element for desktop layouts. */ 30 | flex: 1; 31 | } 32 | 33 | .example-is-mobile .example-sidenav-container { 34 | /* When the sidenav is fixed, don't constrain the height of the sidenav container. This allows the 35 | `` to be our scrolling element for mobile layouts. */ 36 | flex: 1 0 auto; 37 | } 38 | -------------------------------------------------------------------------------- /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 angular-color-picker!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ChangeDetectorRef, OnDestroy } from '@angular/core'; 2 | import { MediaMatcher } from '@angular/cdk/layout'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'] 8 | }) 9 | export class AppComponent implements OnDestroy { 10 | title = 'Angular Radial color picker demo'; 11 | mobileQuery: MediaQueryList; 12 | 13 | private _mobileQueryListener: (this: MediaQueryList, ev: MediaQueryListEvent) => any; 14 | 15 | constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher) { 16 | this.mobileQuery = media.matchMedia('(max-width: 600px)'); 17 | this._mobileQueryListener = () => changeDetectorRef.detectChanges(); 18 | this.mobileQuery.addEventListener('change', this._mobileQueryListener); 19 | } 20 | 21 | ngOnDestroy(): void { 22 | this.mobileQuery.removeEventListener('change', this._mobileQueryListener); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | import { RadialColorPickerModule } from 'radial-color-picker'; 7 | import { HomeComponent } from './components/home/home.component'; 8 | import { SimpleComponent } from './components/simple/simple.component'; 9 | import { NgmodelComponent } from './components/ngmodel/ngmodel.component'; 10 | import { FormcontrolComponent } from './components/formcontrol/formcontrol.component'; 11 | import { NoanimationsComponent } from './components/noanimations/noanimations.component'; 12 | import { ClosedComponent } from './components/closed/closed.component'; 13 | import { CollapsibleComponent } from './components/collapsible/collapsible.component'; 14 | import { PagenotfoundComponent } from './components/pagenotfound/pagenotfound.component'; 15 | import { AppRoutingModule } from './app-routing.module'; 16 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 17 | import { MatSidenavModule } from '@angular/material/sidenav'; 18 | import { MatIconModule } from '@angular/material/icon'; 19 | import { MatToolbarModule } from '@angular/material/toolbar'; 20 | import { MatListModule } from '@angular/material/list'; 21 | import { MatDividerModule } from '@angular/material/divider'; 22 | import { MatMenuModule } from '@angular/material/menu'; 23 | import { MatButtonModule } from '@angular/material/button'; 24 | import { MatButtonToggleModule } from '@angular/material/button-toggle'; 25 | 26 | @NgModule({ 27 | declarations: [ 28 | AppComponent, 29 | HomeComponent, 30 | SimpleComponent, 31 | NgmodelComponent, 32 | FormcontrolComponent, 33 | NoanimationsComponent, 34 | ClosedComponent, 35 | CollapsibleComponent, 36 | PagenotfoundComponent 37 | ], 38 | imports: [ 39 | BrowserModule, 40 | FormsModule, 41 | ReactiveFormsModule, 42 | AppRoutingModule, 43 | RadialColorPickerModule, 44 | MatSidenavModule, 45 | MatIconModule, 46 | MatToolbarModule, 47 | MatListModule, 48 | MatMenuModule, 49 | MatDividerModule, 50 | MatButtonModule, 51 | MatButtonToggleModule 52 | ], 53 | providers: [], 54 | bootstrap: [AppComponent] 55 | }) 56 | export class AppModule { } 57 | -------------------------------------------------------------------------------- /src/app/components/closed/closed.component.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/app/components/closed/closed.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/closed/closed.component.scss -------------------------------------------------------------------------------- /src/app/components/closed/closed.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ClosedComponent } from './closed.component'; 4 | 5 | describe('ClosedComponent', () => { 6 | let component: ClosedComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ClosedComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ClosedComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/closed/closed.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-closed', 5 | templateUrl: './closed.component.html', 6 | styleUrls: ['./closed.component.scss'] 7 | }) 8 | export class ClosedComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | public selectColor(color) { 16 | console.log('select color', color); 17 | } 18 | 19 | public rtpHook($event) { 20 | console.log(`radial color picker lifecycle event: ${$event}`); 21 | } 22 | 23 | public colorChange($event) { 24 | console.log(`colorChange event: ${$event}`); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/app/components/collapsible/collapsible.component.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/app/components/collapsible/collapsible.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/collapsible/collapsible.component.scss -------------------------------------------------------------------------------- /src/app/components/collapsible/collapsible.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CollabsibleComponent } from './collabsible.component'; 4 | 5 | describe('CollabsibleComponent', () => { 6 | let component: CollabsibleComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CollabsibleComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CollabsibleComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/collapsible/collapsible.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-collapsible', 5 | templateUrl: './collapsible.component.html', 6 | styleUrls: ['./collapsible.component.scss'] 7 | }) 8 | export class CollapsibleComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | public selectColor(color) { 16 | console.log('select color', color); 17 | } 18 | 19 | public rtpHook($event) { 20 | console.log(`radial color picker lifecycle event: ${$event}`); 21 | } 22 | 23 | public colorChange($event) { 24 | console.log(`colorChange event: ${$event}`); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/components/formcontrol/formcontrol.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/components/formcontrol/formcontrol.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/formcontrol/formcontrol.component.scss -------------------------------------------------------------------------------- /src/app/components/formcontrol/formcontrol.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FormcontrolComponent } from './formcontrol.component'; 4 | 5 | describe('FormcontrolComponent', () => { 6 | let component: FormcontrolComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FormcontrolComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FormcontrolComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/formcontrol/formcontrol.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, OnDestroy } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { Subscription } from 'rxjs'; 4 | import { startWith } from 'rxjs/operators'; 5 | 6 | @Component({ 7 | selector: 'app-formcontrol', 8 | templateUrl: './formcontrol.component.html', 9 | styleUrls: ['./formcontrol.component.scss'] 10 | }) 11 | export class FormcontrolComponent implements OnInit, OnDestroy { 12 | colorCtrl: FormControl; 13 | colorSub: Subscription; 14 | constructor() { 15 | this.colorCtrl = new FormControl('#ff0000'); 16 | } 17 | 18 | ngOnInit() { 19 | this.colorSub = this.colorCtrl.valueChanges.pipe(startWith(null)).subscribe((value) => { 20 | console.log(value); 21 | }); 22 | 23 | } 24 | 25 | ngOnDestroy() { 26 | this.colorSub.unsubscribe(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/home/home.component.scss -------------------------------------------------------------------------------- /src/app/components/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.scss'] 7 | }) 8 | export class HomeComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | public rtpHook($event) { 16 | console.log(`radial color picker lifecycle event: ${$event}`); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/components/ngmodel/ngmodel.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/components/ngmodel/ngmodel.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/ngmodel/ngmodel.component.scss -------------------------------------------------------------------------------- /src/app/components/ngmodel/ngmodel.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NgmodelComponent } from './ngmodel.component'; 4 | 5 | describe('NgmodelComponent', () => { 6 | let component: NgmodelComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NgmodelComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NgmodelComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/ngmodel/ngmodel.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-ngmodel', 5 | templateUrl: './ngmodel.component.html', 6 | styleUrls: ['./ngmodel.component.scss'] 7 | }) 8 | export class NgmodelComponent implements OnInit { 9 | color = '#ff0000'; 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | modelChange($event) { 16 | console.log('model change', $event); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/app/components/noanimations/noanimations.component.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/app/components/noanimations/noanimations.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/noanimations/noanimations.component.scss -------------------------------------------------------------------------------- /src/app/components/noanimations/noanimations.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NoanimationsComponent } from './noanimations.component'; 4 | 5 | describe('NoanimationsComponent', () => { 6 | let component: NoanimationsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NoanimationsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NoanimationsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/noanimations/noanimations.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-noanimations', 5 | templateUrl: './noanimations.component.html', 6 | styleUrls: ['./noanimations.component.scss'] 7 | }) 8 | export class NoanimationsComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | 16 | public selectColor(color) { 17 | console.log('select color', color); 18 | } 19 | 20 | public rtpHook($event) { 21 | console.log(`radial color picker lifecycle event: ${$event}`); 22 | } 23 | 24 | public colorChange($event) { 25 | console.log(`colorChange event: ${$event}`); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/components/pagenotfound/pagenotfound.component.html: -------------------------------------------------------------------------------- 1 |

2 | pagenotfound works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/components/pagenotfound/pagenotfound.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/pagenotfound/pagenotfound.component.scss -------------------------------------------------------------------------------- /src/app/components/pagenotfound/pagenotfound.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PagenotfoundComponent } from './pagenotfound.component'; 4 | 5 | describe('PagenotfoundComponent', () => { 6 | let component: PagenotfoundComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PagenotfoundComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PagenotfoundComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/pagenotfound/pagenotfound.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-pagenotfound', 5 | templateUrl: './pagenotfound.component.html', 6 | styleUrls: ['./pagenotfound.component.scss'] 7 | }) 8 | export class PagenotfoundComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/components/simple/simple.component.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/app/components/simple/simple.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/app/components/simple/simple.component.scss -------------------------------------------------------------------------------- /src/app/components/simple/simple.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SimpleComponent } from './simple.component'; 4 | 5 | describe('SimpleComponent', () => { 6 | let component: SimpleComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SimpleComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SimpleComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/simple/simple.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-simple', 5 | templateUrl: './simple.component.html', 6 | styleUrls: ['./simple.component.scss'] 7 | }) 8 | export class SimpleComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | public selectColor(color) { 16 | console.log('select color', color); 17 | } 18 | 19 | public rtpHook($event) { 20 | console.log(`radial color picker lifecycle event: ${$event}`); 21 | } 22 | 23 | public colorChange($event) { 24 | console.log(`colorChange event: ${$event}`); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/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 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radial-color-picker/angular-color-picker/cab6bab3a13a5bce80c8ac6244545475f5021cdf/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AngularColorPicker 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/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 | }; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; 3 | 4 | body { 5 | font-family: Roboto, Arial, sans-serif; 6 | margin: 0; 7 | 8 | } 9 | .basic-container { 10 | padding: 30px; 11 | } 12 | .version-info { 13 | font-size: 8pt; 14 | float: right; 15 | margin: 8px; 16 | } 17 | -------------------------------------------------------------------------------- /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: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "types": [] 7 | }, 8 | "exclude": [ 9 | "src/test.ts", 10 | "**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/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 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ], 21 | "paths": { 22 | "radial-color-picker": [ 23 | "dist/radial-color-picker/radial-color-picker", 24 | "dist/radial-color-picker" 25 | ] 26 | } 27 | }, 28 | "angularCompilerOptions": { 29 | "fullTemplateTypeCheck": true, 30 | "strictInjectionParameters": true 31 | } 32 | } -------------------------------------------------------------------------------- /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 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 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-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-var-requires": false, 64 | "object-literal-key-quotes": [ 65 | true, 66 | "as-needed" 67 | ], 68 | "object-literal-sort-keys": false, 69 | "ordered-imports": false, 70 | "quotemark": [ 71 | true, 72 | "single" 73 | ], 74 | "trailing-comma": false, 75 | "no-conflicting-lifecycle": true, 76 | "no-host-metadata-property": true, 77 | "no-input-rename": true, 78 | "no-inputs-metadata-property": true, 79 | "no-output-native": true, 80 | "no-output-on-prefix": true, 81 | "no-output-rename": true, 82 | "no-outputs-metadata-property": true, 83 | "template-banana-in-box": true, 84 | "template-no-negated-async": true, 85 | "use-lifecycle-interface": true, 86 | "use-pipe-transform-interface": true 87 | }, 88 | "rulesDirectory": [ 89 | "codelyzer" 90 | ] 91 | } --------------------------------------------------------------------------------