├── .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 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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 |
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 |
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 | }
--------------------------------------------------------------------------------