├── favicon.ico
├── apple-touch-icon.png
├── favicon-icons
├── favicon-16x16.png
├── favicon-32x32.png
├── large-og-image.png
├── mstile-150x150.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── browserconfig.xml
└── safari-pinned-tab.svg
├── assets
├── dropdown-icon.svg
├── check-icon.svg
├── height-icon.svg
├── width-icon.svg
├── y-icon.svg
├── x-icon.svg
├── minus-icon.svg
├── spread-icon.svg
├── icon.svg
├── copy-code-icon.svg
├── add-icon.svg
├── github-icon.svg
└── blur-icon.svg
├── LICENSE
├── readme.md
├── index.html
├── style.css
└── main.js
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/favicon.ico
--------------------------------------------------------------------------------
/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/apple-touch-icon.png
--------------------------------------------------------------------------------
/favicon-icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/favicon-icons/favicon-16x16.png
--------------------------------------------------------------------------------
/favicon-icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/favicon-icons/favicon-32x32.png
--------------------------------------------------------------------------------
/favicon-icons/large-og-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/favicon-icons/large-og-image.png
--------------------------------------------------------------------------------
/favicon-icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/favicon-icons/mstile-150x150.png
--------------------------------------------------------------------------------
/favicon-icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/favicon-icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/favicon-icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zshaian/boxshadowcraft/HEAD/favicon-icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/assets/dropdown-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/check-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/height-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/width-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/y-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/favicon-icons/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #00aba9
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/assets/x-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/minus-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/spread-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icon.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/copy-code-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/add-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/favicon-icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [2024] [I Bex]
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 |
2 |

3 |
4 |
5 | # Box Shadow Craft
6 |
7 | Box Shadow Craft is a lightweight web application that allows you to dynamically adjust the box shadow properties of an element in real-time. It provides a user-friendly interface to customize box shadow effects and see the changes instantly. With Shadow Craft, you can easily generate CSS code snippets for the configured shadow effects.
8 |
9 | ## Features
10 |
11 | - **Live Shadow Editing:** Change box shadow properties and instantly see the results.
12 | - **Code Snippet Generation:** Automatically generate CSS code for the configured shadow effects.
13 | - **Interactive Shadow List:** Manage and visualize multiple box shadow effects simultaneously.
14 | - **Easy to Use:** Intuitive interface for effortless customization of shadow properties.
15 |
16 | ## Usage
17 |
18 | 1. **Adjust Properties:** Modify the horizontal offset, vertical offset, blur radius, spread radius, and color of the shadow.
19 | 2. **Preview:** See the live preview of the shadow effect on the selected element.
20 | 3. **Copy Code:** Copy the generated CSS code snippet and use it in your projects.
--------------------------------------------------------------------------------
/assets/github-icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/blur-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | BoxShadowCraft | Live Box Shadow Editor
35 |
36 |
37 |
38 |
148 |
149 |
150 |
151 |
152 |
shadow list
153 |
166 |
167 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
190 |
191 |
192 |
193 | .box-element {
194 | height:200px;
195 | width:200px;
196 | background-color:#000000;
197 | box-shadow: ;
198 | }
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* colors */
3 | --clr-primary: #222831;
4 | --clr-secondary: #393e46;
5 | --clr-accent: #00adb5;
6 | --clr-white: #eeeeee;
7 | --clr-gray:rgb(74, 74, 74);
8 | /* extra colors */
9 | --clr-code-bg: #1e1e1e;
10 | --clr-active-shadow-list: #10161f;
11 | --clr-inactive-shadow-list: #1c2026;
12 | /* font family */
13 | --main-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
14 | "Helvetica Neue", Arial, sans-serif;
15 | /* font sizes */
16 | --fs-small: 1rem;
17 | --fs-medium: 1.3rem;
18 | --fs-large: 1.5rem;
19 | /* font weights */
20 | --fw-normal: 400;
21 | --fw-bold: 600;
22 | --fw-extra-bold: 900;
23 | /* paddings */
24 | --pd-normal: 1em;
25 | --pd-large: 2em;
26 | }
27 |
28 | *,
29 | html,
30 | body {
31 | margin: 0;
32 | padding: 0;
33 | box-sizing: border-box;
34 | }
35 |
36 | @-moz-document url-prefix(){
37 | * {
38 | scrollbar-width:thin;
39 | scrollbar-color:var(--clr-accent) transparent;
40 | }
41 | }
42 |
43 | @media screen and (-webkit-min-device-pixel-ratio:0) {
44 | *::-webkit-scrollbar {
45 | -webkit-appearance: none;
46 | -moz-appearance: none;
47 | appearance: none;
48 | background-color:transparent;
49 | height:0.3em;
50 | width:0.3em;
51 | }
52 |
53 | *::-webkit-scrollbar-corner {
54 | -webkit-appearance: none;
55 | -moz-appearance: none;
56 | appearance:none;
57 | }
58 |
59 | *::-webkit-scrollbar-thumb {
60 | -webkit-appearance: none;
61 | -moz-appearance: none;
62 | appearance: none;
63 | border-radius:5px;
64 | background-color:var(--clr-accent);
65 | }
66 | }
67 |
68 |
69 | button,
70 | select,
71 | input[type="color"]{
72 | cursor:pointer;
73 | }
74 | button:active {
75 | background-color:var(--clr-gray);
76 | }
77 |
78 | body {
79 | font-family: var(--main-font);
80 | background-color:var(--clr-code-bg);
81 | }
82 |
83 | .github-project-link {
84 | display:inline-block;
85 | position: absolute;
86 | top:5px;
87 | right:0px;
88 | }
89 |
90 | .main-container {
91 | display:flex;
92 | flex-direction: column;
93 | }
94 |
95 | .main-container__canvas {
96 | display: flex;
97 | flex-direction: column;
98 | border-bottom: 5px solid var(--clr-primary);
99 | }
100 |
101 | .canvas__box {
102 | height:33.3vh;
103 | max-width:100%;
104 | overflow:scroll;
105 | background-color:var(--clr-white);
106 | position:relative;
107 | }
108 |
109 | .canvas__box__element {
110 | height: 200px;
111 | width: 200px;
112 | margin:2.5em auto 2em auto;
113 | border-radius: 5px;
114 | background-color:#000000;
115 | transition:all 0.1s linear;
116 | }
117 |
118 | .canvas__design-inputs__list {
119 | padding: var(--pd-normal);
120 | list-style-type: none;
121 | display: flex;
122 | flex-wrap: wrap;
123 | justify-content: center;
124 | gap: 0.3em;
125 | background-color: var(--clr-secondary);
126 | }
127 |
128 | .canvas__design-inputs__property {
129 | padding: 0.3em;
130 | color: var(--clr-white);
131 | text-transform:uppercase;
132 | display: flex;
133 | flex-direction:column;
134 | align-items:center;
135 | justify-content: center;
136 | gap: 0.3em;
137 | border-radius: 5px;
138 | border: 2px solid var(--clr-primary);
139 | }
140 | .canvas__design-inputs__property__range {
141 | display:flex;
142 | align-items:flex-start;
143 | justify-content: flex-start;
144 | }
145 |
146 | .canvas__design-range-inputs {
147 | -webkit-appearance: none;
148 | appearance:none;
149 | height:12px;
150 | width:100%;
151 | border-radius:5px;
152 | background-color:var(--clr-primary);
153 | position:relative;
154 | overflow:hidden;
155 | }
156 |
157 | .canvas__design-range-inputs::-webkit-slider-thumb {
158 | -webkit-appearance: none;
159 | appearance:none;
160 | height:12px;
161 | width:12px;
162 | background-color:var(--clr-primary);
163 | border:2px solid var(--clr-accent);
164 | border-radius:50%;
165 | box-shadow:-405px 2px 0px 400px var(--clr-accent);
166 | z-index:2;
167 | }
168 |
169 | .canvas__design-range-inputs::-moz-range-thumb {
170 | -webkit-appearance: none;
171 | appearance:none;
172 | height:12px;
173 | width:12px;
174 | background-color:var(--clr-primary);
175 | border:2px solid var(--clr-accent);
176 | border-radius:50%;
177 | box-shadow:-405px 2px 0px 400px var(--clr-accent);
178 | z-index:2;
179 | }
180 |
181 | .canvas__design-inputs__value-text {
182 | color: var(--clr-white);
183 | width: 4ch;
184 | font-size: var(--fs-small);
185 | border: none;
186 | background-color: transparent;
187 | }
188 |
189 | .canvas__design-inputs__value-number {
190 | -moz-appearance: textfield;
191 | appearance: textfield;
192 | padding:0.3em;
193 | color: var(--clr-white);
194 | width: 4ch;
195 | font-size: var(--fs-small);
196 | border: none;
197 | background-color: transparent;
198 | }
199 |
200 | .canvas__design-inputs__value-number::-webkit-outer-spin-button,
201 | .canvas__design-inputs__value-number::-webkit-inner-spin-button {
202 | -webkit-appearance: none;
203 | appearance: none;
204 | }
205 |
206 | .canvas__design-inputs__unit {
207 | padding: 0.3em;
208 | color: var(--clr-white);
209 | text-transform: uppercase;
210 | border-radius: 2px;
211 | border: none;
212 | background-color: var(--clr-primary);
213 | }
214 |
215 | .canvas__design-inputs__value-color {
216 | -moz-appearance: none;
217 | -webkit-appearance: none;
218 | appearance: none;
219 | padding: 0;
220 | width:100%;
221 | border: none;
222 | background-color: transparent;
223 | }
224 |
225 | .canvas__design-inputs__value-color::-webkit-color-swatch-wrapper {
226 | margin: 0;
227 | padding: 0;
228 | }
229 |
230 | .canvas__design-inputs__value-color::-webkit-color-swatch {
231 | margin: 0;
232 | padding: 0;
233 | border: none;
234 | border-radius:2px;
235 | }
236 |
237 | .shadow-list__head {
238 | padding: var(--pd-normal);
239 | display: flex;
240 | justify-content: space-between;
241 | border-bottom: 5px solid var(--clr-primary);
242 | background-color: var(--clr-secondary);
243 | }
244 |
245 | .shadow-list__head__title-dropdown {
246 | display: flex;
247 | gap: 0em 1em;
248 | }
249 |
250 | .shadow-list__head__title {
251 | color: var(--clr-white);
252 | font-size: var(--fs-medium);
253 | text-transform: uppercase;
254 | }
255 |
256 | .shadow-list__head__dropdown-btn {
257 | height: 30px;
258 | width: 30px;
259 | display: flex;
260 | align-items: center;
261 | justify-content: center;
262 | border-radius: 5px;
263 | border: none;
264 | background-color: var(--clr-primary);
265 | transform: rotate(180deg);
266 | }
267 |
268 | .shadow-list__head__dropdown-btn--active {
269 | transform:rotate(0deg);
270 | }
271 |
272 | .shadow-list__add-shadow-btn {
273 | height: 30px;
274 | width: 30px;
275 | display: flex;
276 | align-items: center;
277 | justify-content: center;
278 | border-radius: 5px;
279 | border: none;
280 | background-color: var(--clr-primary);
281 | }
282 |
283 | .shadow-list__shadows {
284 | display: none;
285 | }
286 |
287 | .shadow-list__shadows--active {
288 | display: flex;
289 | flex-direction: column;
290 | list-style-type: none;
291 | }
292 |
293 | .shadows__info {
294 | background-color: var(--clr-inactive-shadow-list);
295 | list-style-type:none;
296 | }
297 |
298 | .shadows__info--active {
299 | background-color: var(--clr-active-shadow-list);
300 | border-bottom:2px solid var(--clr-accent);
301 | }
302 |
303 | .shadows__info__name-options {
304 | padding: var(--pd-normal);
305 | display: flex;
306 | gap: 0em 1em;
307 | }
308 |
309 | .shadows__info__name {
310 | color: var(--clr-white);
311 | font-size: var(--fs-small);
312 | font-weight: var(--fw-normal);
313 | text-transform: capitalize;
314 | }
315 |
316 |
317 | .shadows__info__options__expand {
318 | margin-left:auto;
319 | }
320 |
321 |
322 | .shadows__info__options__expand,
323 | .shadows__info__options__remove {
324 | height: 25px;
325 | width: 25px;
326 | display: flex;
327 | align-items: center;
328 | justify-content: center;
329 | border: none;
330 | border-radius: 2px;
331 | background-color: var(--clr-primary);
332 | transform: rotate(180deg);
333 | }
334 |
335 | .options__expand__dropdown-icon,
336 | .options__remove__minus-icon {
337 | pointer-events:none;
338 | }
339 |
340 | .shadows__info__options__expand--active {
341 | transform:rotate(0deg);
342 | }
343 |
344 | .shadows__property-inputs {
345 | display: none;
346 | }
347 |
348 | .shadows__property-inputs--active {
349 | padding: var(--pd-normal);
350 | display:grid;
351 | grid-template-columns:repeat(3,1fr);
352 | grid-template-rows:repeat(2,1fr);
353 | grid-template-areas:
354 | "x blur color"
355 | "y spread check";
356 | gap:0.3em;
357 | list-style-type: none;
358 | }
359 |
360 | .shadows__property-inputs__info {
361 | padding: 0.5em;
362 | color: var(--clr-white);
363 | text-transform:uppercase;
364 | display: flex;
365 | flex-direction:column;
366 | justify-content: center;
367 | gap:0.8em;
368 | border-radius: 5px;
369 | border: 2px solid var(--clr-secondary);
370 | }
371 | .box-shadow-property-range-inputs-wrapper {
372 | display:flex;
373 | align-items:flex-start;
374 | justify-content: flex-start;
375 | }
376 | .shadows__property-inputs__info--x {
377 | grid-area:x;
378 | }
379 |
380 | .shadows__property-inputs__info--y {
381 | grid-area:y;
382 | }
383 |
384 | .shadows__property-inputs__info--blur {
385 | grid-area:blur;
386 | }
387 |
388 | .shadows__property-inputs__info--spread {
389 | grid-area:spread;
390 | }
391 |
392 | .shadows__property-inputs__info--color {
393 | grid-area:color;
394 | flex-direction: row;
395 | align-items: center;
396 | }
397 |
398 | .shadows__property-inputs__info--inset {
399 | grid-area:check;
400 | flex-direction: row;
401 | align-items: center;
402 | }
403 |
404 | .box-shadow-inputs {
405 | display:flex;
406 | gap:0.3em;
407 | align-items:center;
408 | justify-content: center;
409 | }
410 |
411 | .shadows__property-inputs__value {
412 | -moz-appearance: textfield;
413 | appearance: textfield;
414 | padding:0.3em;
415 | width: 4ch;
416 | color: var(--clr-white);
417 | background-color: transparent;
418 | border: none;
419 | }
420 |
421 | .shadows__property-inputs__value::-webkit-outer-spin-button,
422 | .shadows__property-inputs__value::-webkit-inner-spin-button {
423 | -webkit-appearance: none;
424 | appearance: none;
425 | }
426 |
427 | .shadows__property-inputs-range__value {
428 | -webkit-appearance: none;
429 | appearance:none;
430 | height:12px;
431 | width:100%;
432 | border-radius:5px;
433 | background-color:var(--clr-secondary);
434 | position:relative;
435 | overflow:hidden;
436 | }
437 |
438 | .shadows__property-inputs-range__value::-webkit-slider-thumb {
439 | -webkit-appearance: none;
440 | appearance:none;
441 | height:12px;
442 | width:12px;
443 | background-color:var(--clr-primary);
444 | border:2px solid var(--clr-accent);
445 | border-radius:50%;
446 | box-shadow:-405px 2px 0px 400px var(--clr-accent);
447 | z-index:2;
448 | }
449 |
450 | .shadows__property-inputs-range__value::-moz-range-thumb {
451 | -webkit-appearance: none;
452 | appearance:none;
453 | height:12px;
454 | width:12px;
455 | background-color:var(--clr-primary);
456 | border:2px solid var(--clr-accent);
457 | border-radius:50%;
458 | box-shadow:-405px 2px 0px 400px var(--clr-accent);
459 | z-index:2;
460 | }
461 |
462 | .shadows__property-inputs__unit {
463 | padding: 0.3em;
464 | color: var(--clr-white);
465 | text-transform: uppercase;
466 | border-radius: 2px;
467 | border: none;
468 | background-color: var(--clr-primary);
469 | }
470 |
471 | .shadows__property-inputs__color-icon {
472 | height: 15px;
473 | width: 15px;
474 | background-color: var(--clr-accent);
475 | }
476 | .shadows__property-inputs__colors-value {
477 | -moz-appearance: none;
478 | -webkit-appearance: none;
479 | appearance: none;
480 | padding: 0;
481 | height: 15px;
482 | width: 15px;
483 | border: none;
484 | border-radius: 50%;
485 | background-color: transparent;
486 | }
487 |
488 | .shadows__property-inputs__colors-value::-webkit-color-swatch-wrapper {
489 | margin: 0;
490 | padding: 0;
491 | border-radius: 50%;
492 | }
493 |
494 | .shadows__property-inputs__colors-value::-webkit-color-swatch {
495 | margin: 0;
496 | padding: 0;
497 | border: none;
498 | border-radius: 50%;
499 | }
500 |
501 | .shadows__property-inputs__checkbox {
502 | -webkit-appearance: none;
503 | -moz-appearance: none;
504 | appearance: none;
505 | height: 15px;
506 | width: 15px;
507 | border-radius: 2px;
508 | background-color: var(--clr-secondary);
509 | position: relative;
510 | }
511 |
512 | .shadows__property-inputs__checkbox-label {
513 | font-size: var(--fs-small);
514 | text-transform: uppercase;
515 | }
516 |
517 | .shadows__property-inputs__checkbox::before {
518 | content: "";
519 | height: 100%;
520 | width: 100%;
521 | background-color: var(--clr-primary);
522 | background-image: url("./assets/check-icon.svg");
523 | background-size: 10px 7px;
524 | background-position: center;
525 | background-repeat: no-repeat;
526 | border-radius: inherit;
527 | position: absolute;
528 | display: none;
529 | }
530 |
531 | .shadows__property-inputs__checkbox:checked::before {
532 | display: block;
533 | }
534 |
535 | .main-container__code {
536 | padding:var(--pd-normal);
537 | background-color:var(--clr-code-bg);
538 | display:flex;
539 | flex-direction:column;
540 | overflow:scroll;
541 | }
542 |
543 | .code__copy-code-btn {
544 | max-width:max-content;
545 | color:var(--clr-white);
546 | padding:0.5em 1em;
547 | margin-bottom:1em;
548 | font-size:var(--fs-small);
549 | text-transform:uppercase;
550 | display: flex;
551 | align-items: center;
552 | justify-content: center;
553 | gap:0.5em;
554 | border:none;
555 | border-radius:5px;
556 | background-color:var(--clr-primary);
557 | }
558 |
559 | .code__text {
560 | color:var(--clr-white);
561 | line-height:1.3rem;
562 | font-size:var(--fs-small);
563 | }
564 |
565 | .code-property__list-container {
566 | padding-left:var(--pd-large);
567 | }
568 |
569 | .code-selector {
570 | color: #d3b382;
571 | }
572 |
573 | .code-property {
574 | color:#3FC6F1;
575 | }
576 |
577 | .code-unit {
578 | color:#E5AF5E;
579 | }
580 |
581 | .code-value {
582 | color:#E5AF5E;
583 | }
584 |
585 | .code-value--color-value {
586 | color:#E57333;
587 | }
588 |
589 | @media screen and (min-width:1024px) {
590 | .main-container {
591 | height:100vh;
592 | width:100vw;
593 | min-height:100vh;
594 | min-width:100vw;
595 | display:grid;
596 | grid-template-columns:repeat(3,1fr);
597 | grid-template-rows:repeat(3,1fr);
598 | grid-template-areas:
599 | "shadowList canvas canvas"
600 | "shadowList canvas canvas"
601 | "shadowList code code"
602 | ;
603 | }
604 |
605 | .main-container__canvas {
606 | grid-area:canvas;
607 | border-bottom:none;
608 | }
609 | .canvas__box {
610 | max-height: 100%;
611 | max-width:100%;
612 | flex:1;
613 | overflow:scroll;
614 | }
615 |
616 | .main-container__shadow-list{
617 | grid-area:shadowList;
618 | background-color:var(--clr-secondary);
619 | display:flex;
620 | flex-direction: column;
621 | }
622 | .shadow-list__head {
623 | border-bottom:none;
624 | }
625 | .shadow-list__head__dropdown-btn {
626 | display:none;
627 | }
628 | .shadow-list__shadows {
629 | max-height:100%;
630 | overflow-y:scroll;
631 | display: flex;
632 | flex-direction: column;
633 | flex:1;
634 | list-style-type: none;
635 | }
636 |
637 | .main-container__code {
638 | grid-area:code;
639 | }
640 | }
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | // This class represents a Properties object that stores key-value pairs of properties.
2 | // The constructor initializes the Properties object with an initial set of properties.
3 | class Properties {
4 | constructor(initialProperties) {
5 | // Initialize the properties object with the initialProperties passed, or an empty object if none provided.
6 | this.properties = initialProperties || {};
7 | }
8 |
9 | // Method to change the value of a property given its name.
10 | changeValue(propertyName, newValue) {
11 | // Check if the property with the provided name exists in the properties object.
12 | if (this.properties[propertyName]) {
13 | // If the property exists, update its value with the new value provided.
14 | this.properties[propertyName].value = newValue;
15 | }
16 | }
17 |
18 | // Method to change the unit of a property given its name.
19 | changeUnit(propertyName, newUnit) {
20 | // Check if the property with the provided name exists in the properties object.
21 | if (this.properties[propertyName]) {
22 | // If the property exists, update its unit with the new unit provided.
23 | this.properties[propertyName].unit = newUnit;
24 | }
25 | }
26 |
27 | // Method to apply the stored property changes to an HTML element's style.
28 | applyChangesToElement(element) {
29 | // Iterate over each property in the properties object.
30 | for (const prop in this.properties) {
31 | // Destructure the value and unit of the property.
32 | const { value, unit } = this.properties[prop];
33 | // Apply the property changes to the element's style.
34 | element.style[prop] = `${value}${unit || ""}`;
35 | }
36 | }
37 |
38 | // Method to apply the property changes to a code element's displayed value.
39 | applyChangesToCodeValue(propertyName, newValue, type) {
40 | // If newValue is falsy, default to 0.
41 | const codeValue = newValue || 0;
42 | // Find the target code element using the provided type and property name.
43 | const targetCodeElement = document.querySelector(
44 | `.code-${type}[data-property-name="${propertyName}"]`
45 | );
46 | // Update the text content of the target code element with the codeValue.
47 | targetCodeElement.textContent = codeValue;
48 | }
49 | }
50 |
51 | // Define a class for setting properties of a box element
52 | class BoxProperties extends Properties {
53 | constructor() {
54 | // Call the constructor of the parent class with initial properties
55 | super({
56 | height: { value: "200", unit: "px" },
57 | width: { value: "200", unit: "px" },
58 | backgroundColor: { value: "#000000", unit: null },
59 | });
60 | }
61 | }
62 |
63 | // Define a class for setting properties of a canvas element
64 | class CanvasProperties extends Properties {
65 | constructor() {
66 | // Call the constructor of the parent class with initial properties
67 | super({
68 | height: { value: "auto", unit: null },
69 | width: { value: "auto", unit: null },
70 | backgroundColor: { value: "#FFFFFF", unit: null },
71 | });
72 | }
73 | }
74 |
75 | // Get the box element from the DOM
76 | const boxElement = document.getElementById("box-element");
77 |
78 | // Create an object to manage box properties
79 | const boxShadowElementObject = new BoxProperties();
80 |
81 | // Function to update box properties based on user input
82 | const updateBox = (event) => {
83 | const newValue = event.target.value;
84 | const propertyName = event.target.dataset.propertyName;
85 |
86 | // Update the property value in the object
87 | boxShadowElementObject.changeValue(propertyName, newValue);
88 |
89 | // Apply the changes to the actual DOM element
90 | boxShadowElementObject.applyChangesToElement(boxElement);
91 |
92 | // Update the corresponding code value displayed on the page
93 | boxShadowElementObject.applyChangesToCodeValue(propertyName, newValue, "value");
94 | };
95 |
96 | // Function to update box color property
97 | const updateBoxColor = (event) => {
98 | // Call the generic updateBox function to handle common updates
99 | updateBox(event);
100 |
101 | // Update the displayed color value on the page
102 | const shadowColorTextElement = document.getElementById("element-color-text-value");
103 | shadowColorTextElement.textContent = event.target.value;
104 | };
105 |
106 | // Adding event listener to all elements with class "box-property-input" and calling updateBox function
107 | document
108 | .querySelectorAll(".box-property-input")
109 | .forEach((input) => input.addEventListener("input", updateBox));
110 |
111 | // Adding event listener to element with id "element-background-color-input" and calling updateBoxColor function
112 | document
113 | .getElementById("element-background-color-input")
114 | .addEventListener("input", updateBoxColor);
115 |
116 | // Function to update box unit properties
117 | const updateBoxUnit = (event) => {
118 | const newValue = event.target.value;
119 | const propertyName = event.target.dataset.propertyName;
120 | boxShadowElementObject.changeUnit(propertyName, newValue);
121 | boxShadowElementObject.applyChangesToElement(boxElement);
122 | boxShadowElementObject.applyChangesToCodeValue(propertyName, newValue, "unit");
123 | };
124 |
125 | // Adding event listener to all elements with class "box-property-unit" and calling updateBoxUnit function
126 | document
127 | .querySelectorAll(".box-property-unit")
128 | .forEach((select) => select.addEventListener("change", updateBoxUnit));
129 |
130 | // Function to update box text input value and update corresponding properties
131 | const updateBoxTextInputValue = (event) => {
132 | updateBox(event);
133 | const textInputValueProperty = event.target.parentElement.nextElementSibling.children[1];
134 | textInputValueProperty.value = event.target.value;
135 | }
136 |
137 | // Adding event listener to all elements with class "canvas__design-range-inputs" and calling updateBoxTextInputValue function
138 | document.querySelectorAll(".canvas__design-range-inputs")
139 | .forEach(rangeInput => rangeInput.addEventListener("input",updateBoxTextInputValue));
140 |
141 | // Creating canvas element and object for CanvasProperties class
142 | const canvasElement = document.getElementById("box-canvas");
143 | const canvasElementObject = new CanvasProperties();
144 |
145 | // Function to update canvas text element color
146 | const updateCanvasTextElementColor = (value) =>
147 | (document.getElementById("canvas-color-text-value").textContent = value);
148 |
149 | // Function to update canvas properties
150 | const updateCanvas = (event) => {
151 | const newValue = event.target.value;
152 | const propertyName = event.target.dataset.propertyName;
153 | canvasElementObject.changeValue(propertyName, newValue);
154 | canvasElementObject.applyChangesToElement(canvasElement);
155 | updateCanvasTextElementColor(newValue);
156 | };
157 |
158 | // Adding event listener to element with id "canvas-background-color" and calling updateCanvas function
159 | document
160 | .getElementById("canvas-background-color")
161 | .addEventListener("input", updateCanvas);
162 |
163 | // Class definition for BoxShadowsPropertiesList
164 | class BoxShadowsPropertiesList {
165 | constructor() {
166 | this.boxShadowList = [];
167 | this.boxShadowId = 1;
168 | }
169 |
170 | // Function to add a new shadow with default properties
171 | addShadow() {
172 | const boxShadowDefaultPropertiesAndValues = {
173 | id: {
174 | value: this.boxShadowId,
175 | unit: null,
176 | },
177 | x: {
178 | value: "2",
179 | unit: "px",
180 | },
181 | y: {
182 | value: "2",
183 | unit: "px",
184 | },
185 | blur: {
186 | value: "2",
187 | unit: "px",
188 | },
189 | spread: {
190 | value: "0",
191 | unit: "px",
192 | },
193 | color: {
194 | value: "#000000",
195 | unit: null,
196 | },
197 | inset: {
198 | value: false,
199 | unit: null,
200 | },
201 | };
202 | this.boxShadowList.push(boxShadowDefaultPropertiesAndValues);
203 | this.incrementBoxShadowId();
204 | return boxShadowDefaultPropertiesAndValues;
205 | }
206 |
207 | editBoxShadowPropertiesUnitValue(
208 | boxShadowId,
209 | propertyName,
210 | newValue,
211 | unitOrValue
212 | ) {
213 | const targetShadow = this.boxShadowList.filter(
214 | (shadow) => shadow.id.value === boxShadowId
215 | )[0];
216 | targetShadow[propertyName][unitOrValue] = newValue;
217 | return this;
218 | }
219 |
220 | deleteBoxShadow(boxShadowId) {
221 | const updatedBoxShadow = this.boxShadowList.filter(
222 | (shadow) => shadow.id.value !== boxShadowId
223 | );
224 | this.boxShadowList = updatedBoxShadow;
225 | return this;
226 | }
227 |
228 | getBoxShadowList() {
229 | return this.boxShadowList;
230 | }
231 |
232 | incrementBoxShadowId() {
233 | this.boxShadowId++;
234 | return this;
235 | }
236 | }
237 |
238 | // Instantiate a new BoxShadowsPropertiesList object
239 | const boxShadowsPropertiesList = new BoxShadowsPropertiesList();
240 |
241 | // Function to create a styled element with specified attributes
242 | const createStyledElement = (elementName, attributes = {}) => {
243 | const element = document.createElement(elementName);
244 | // Loop through each attribute and set it on the element
245 | for (const attributeName in attributes) {
246 | element.setAttribute(attributeName, attributes[attributeName]);
247 | }
248 | return element; // Return the created element
249 | };
250 |
251 | // Function to render different properties based on their type
252 | const renderPropertiesElement = (properties, propertiesContainer) => {
253 | // Define different types of properties
254 | const inputUnitProperty = ["x", "y", "blur", "spread"];
255 | const colorInputProperty = ["color"];
256 | const checkInputProperty = ["inset"];
257 |
258 | // Loop through each property in the provided properties object
259 | for (const shadowProperty in properties) {
260 | // Check the type of property and create corresponding input element
261 | if (inputUnitProperty.includes(shadowProperty)) {
262 | const inputValueUnitElement = createBoxShadowInputValueUnit(
263 | properties,
264 | shadowProperty
265 | );
266 | // Append the created input element to the properties container
267 | propertiesContainer.append(inputValueUnitElement);
268 | } else if (colorInputProperty.includes(shadowProperty)) {
269 | const inputColorValueElement = createBoxShadowInputColorValue(
270 | properties,
271 | shadowProperty
272 | );
273 | propertiesContainer.append(inputColorValueElement);
274 | } else if (checkInputProperty.includes(shadowProperty)) {
275 | const inputCheckValueElement = createBoxShadowInputCheckValue(
276 | properties,
277 | shadowProperty
278 | );
279 | propertiesContainer.append(inputCheckValueElement);
280 | }
281 | }
282 | };
283 |
284 | // This function creates input elements for a specific box shadow property
285 | const createBoxShadowInputValueUnit = (properties, shadowProperty) => {
286 | // Get the current box shadow property value
287 | const currentBoxShadowProperty = properties[shadowProperty];
288 |
289 | // List of units for the box shadow property
290 | const unitList = ["px", "em", "rem"];
291 |
292 | // Create elements for the input field
293 | const shadowPropertyInputsInfo = createStyledElement("li", {
294 | class: `shadows__property-inputs__info shadows__property-inputs__info--${shadowProperty}`,
295 | });
296 | const propertyInputWrapper = createStyledElement("div", {
297 | class: "box-shadow-property-inputs-wrapper",
298 | });
299 | const propertyInputs = createStyledElement("div", {
300 | class:"box-shadow-inputs",
301 | })
302 | const shadowPropertyRangeInputWrapper = createStyledElement("div",{
303 | class:"box-shadow-property-range-inputs-wrapper",
304 | });
305 |
306 | // Create a range input element for the box shadow property
307 | const shadowPropertyRangeInput = createStyledElement("input", {
308 | type: "range",
309 | class: "shadows__property-inputs-range__value",
310 | id: `box-shadow${properties.id.value}-${shadowProperty}-value`,
311 | min:"-100",
312 | max:"100",
313 | value:`${properties[shadowProperty].value}`,
314 | "aria-label": `box shadow ${shadowProperty} value`,
315 | "data-input-function": "change-box-shadow-property",
316 | "data-box-shadow-id-reference": `${properties.id.value}`,
317 | "data-box-shadow-property-name": `${shadowProperty}`,
318 | });
319 |
320 | // Create an icon element for the box shadow property
321 | const shadowPropertyInputsIcon = createStyledElement("img", {
322 | class: "shadows__property-inputs__icon",
323 | src: `./assets/${shadowProperty}-icon.svg`,
324 | alt: `box shadow ${shadowProperty} icon`,
325 | height: "15",
326 | width: "15",
327 | });
328 |
329 | // Create an input field for the box shadow property value
330 | const shadowPropertyInput = createStyledElement("input", {
331 | class: "shadows__property-inputs__value",
332 | type: "number",
333 | id: `box-shadow-${properties.id.value}-${shadowProperty}-value`,
334 | value: `${currentBoxShadowProperty.value}`,
335 | "aria-label": `box shadow ${shadowProperty} value`,
336 | "data-input-function": "change-box-shadow-property",
337 | "data-box-shadow-id-reference": `${properties.id.value}`,
338 | "data-box-shadow-property-name": `${shadowProperty}`,
339 | });
340 |
341 | // Create a select element for choosing the unit of the box shadow property
342 | const shadowPropertyUnit = createStyledElement("select", {
343 | class: "shadows__property-inputs__unit",
344 | id: `box-shadow-${properties.id.value}-${shadowProperty}-unit`,
345 | "aria-label": `box shadow ${shadowProperty} unit`,
346 | "data-select-function": "change-box-shadow-property",
347 | "data-box-shadow-id-reference": `${properties.id.value}`,
348 | "data-box-shadow-property-name": `${shadowProperty}`,
349 | });
350 |
351 | // Loop through the unit list and create options for the select element
352 | for (const unit of unitList) {
353 | const optionElement = createStyledElement("option", { value: unit });
354 | optionElement.textContent = unit;
355 | shadowPropertyUnit.add(optionElement);
356 | }
357 |
358 | // Append elements to the parent elements
359 | propertyInputs.append(
360 | shadowPropertyInputsIcon,
361 | shadowPropertyInput,
362 | shadowPropertyUnit
363 | );
364 |
365 | shadowPropertyRangeInputWrapper.append(shadowPropertyRangeInput);
366 | propertyInputWrapper.append(propertyInputs);
367 |
368 | shadowPropertyInputsInfo.append(shadowPropertyRangeInputWrapper, propertyInputWrapper);
369 |
370 | // Return the created input elements for the box shadow property
371 | return shadowPropertyInputsInfo;
372 | };
373 |
374 | // This function creates input elements for the box shadow color value based on the provided properties and shadow property
375 | const createBoxShadowInputColorValue = (properties, shadowProperty) => {
376 | // Get the current box shadow property value from the provided properties
377 | const currentBoxShadowProperty = properties[shadowProperty];
378 |
379 | // Create a list element to hold the box shadow property info
380 | const boxShadowsPropertyInfo = createStyledElement("li", {
381 | class: `shadows__property-inputs__info shadows__property-inputs__info--${shadowProperty}`,
382 | });
383 |
384 | // Create a span element to display the box shadow property color value
385 | const boxShadowPropertyInputsColorValue = createStyledElement("span", {
386 | class: "shadows__property-inputs__value-colors",
387 | });
388 |
389 | // Create an input element to allow users to change the box shadow color value
390 | const boxShadowPropertyInfoInputColor = createStyledElement("input", {
391 | class: "shadows__property-inputs__colors-value",
392 | type: "color",
393 | "aria-label": `box shadow ${shadowProperty} value`,
394 | "data-input-function": "change-box-shadow-property",
395 | "data-box-shadow-id-reference": `${properties.id.value}`,
396 | "data-box-shadow-property-name": `${shadowProperty}`,
397 | });
398 |
399 | // Set the text content of the color value span to the current box shadow color value
400 | boxShadowPropertyInputsColorValue.textContent = currentBoxShadowProperty.value;
401 |
402 | // Set the value of the color input to the current box shadow color value
403 | boxShadowPropertyInfoInputColor.value = currentBoxShadowProperty.value;
404 |
405 | // Append the color input and color value span to the box shadow property info element
406 | boxShadowsPropertyInfo.append(
407 | boxShadowPropertyInfoInputColor,
408 | boxShadowPropertyInputsColorValue
409 | );
410 |
411 | // Return the box shadow property info element
412 | return boxShadowsPropertyInfo;
413 | };
414 |
415 | // Function to create input checkbox for box-shadow property
416 | const createBoxShadowInputCheckValue = (properties, shadowProperty) => {
417 | // Get the current box shadow property value
418 | const currentBoxShadowProperty = properties[shadowProperty];
419 |
420 | // Create a list element for box shadow property info with specific class
421 | const boxShadowsPropertyInfo = createStyledElement("li", {
422 | class: `shadows__property-inputs__info shadows__property-inputs__info--${shadowProperty}`,
423 | });
424 |
425 | // Create an input checkbox element for the box shadow property
426 | const shadowPropertyInputCheckBox = createStyledElement("input", {
427 | type: "checkbox",
428 | class: "shadows__property-inputs__checkbox",
429 | id: `box-shadow-${properties.id.value}-inset-value`,
430 | "data-input-function": "change-box-shadow-property",
431 | "data-box-shadow-id-reference": `${properties.id.value}`,
432 | "data-box-shadow-property-name": `${shadowProperty}`,
433 | });
434 |
435 | // Create a label for the checkbox
436 | const shadowPropertyInputCheckBoxLabel = createStyledElement("label", {
437 | class: "shadows__property-inputs__checkbox-label",
438 | for: `box-shadow-${properties.id.value}-inset-value`,
439 | });
440 |
441 | // Set the checkbox checked status based on current box shadow property value
442 | shadowPropertyInputCheckBox.checked = currentBoxShadowProperty.value;
443 |
444 | // Set the label text content to "inset"
445 | shadowPropertyInputCheckBoxLabel.textContent = "inset";
446 |
447 | // Append checkbox and label to the box shadow property info element
448 | boxShadowsPropertyInfo.append(
449 | shadowPropertyInputCheckBox,
450 | shadowPropertyInputCheckBoxLabel
451 | );
452 |
453 | // Return the box shadow property info element
454 | return boxShadowsPropertyInfo;
455 | };
456 |
457 | // Function to create elements for box shadow information
458 | const createBoxShadowInfoElement = (properties) => {
459 | // Generate the shadow title based on the property id
460 | const shadowTitle = `shadow ${properties.id.value}`;
461 |
462 | // Create a list element for shadows info
463 | const shadowsInfoElement = createStyledElement("li", {
464 | class: "shadows__info",
465 | });
466 |
467 | // Create elements for shadow name, dropdown options, and remove option
468 | const shadowNameOptions = createStyledElement("div", {
469 | class: "shadows__info__name-options",
470 | });
471 | const shadowNameElement = createStyledElement("h3", {
472 | class: "shadows__info__name",
473 | });
474 | const shadowDropDownOptionsBtn = createStyledElement("button", {
475 | type: "button",
476 | class: "shadows__info__options__expand",
477 | "aria-label": "show shadow property list",
478 | "data-button-function": "expand-shadow-options",
479 | });
480 | const shadowDropDownOptionIcon = createStyledElement("img", {
481 | src: "./assets/dropdown-icon.svg",
482 | alt: "dropdown icon",
483 | height: "10",
484 | width: "15",
485 | class: "options__expand__dropdown-icon",
486 | });
487 | const shadowRemoveOptionBtn = createStyledElement("button", {
488 | type: "button",
489 | class: "shadows__info__options__remove",
490 | "aria-label": "delete-shadow",
491 | "data-button-function": "delete-shadow",
492 | "data-shadow-id-reference": `${properties.id.value}`,
493 | });
494 | const shadowsRemoveOptionIcon = createStyledElement("img", {
495 | src: "./assets/minus-icon.svg",
496 | alt: "minus icon",
497 | height: "4",
498 | width: "15",
499 | class: "options__remove__minus-icon",
500 | });
501 |
502 | // Create a list element for shadow property inputs
503 | const shadowPropertyInputs = createStyledElement("ul", {
504 | class: "shadows__property-inputs",
505 | });
506 |
507 | // Set the text content of the shadow name element to the generated shadow title
508 | shadowNameElement.textContent = shadowTitle;
509 |
510 | // Render the properties element for the shadow property inputs
511 | renderPropertiesElement(properties, shadowPropertyInputs);
512 |
513 | // Append icons to the dropdown and remove buttons
514 | shadowDropDownOptionsBtn.append(shadowDropDownOptionIcon);
515 | shadowRemoveOptionBtn.append(shadowsRemoveOptionIcon);
516 |
517 | // Append elements to the shadow name options container
518 | shadowNameOptions.append(
519 | shadowNameElement,
520 | shadowDropDownOptionsBtn,
521 | shadowRemoveOptionBtn
522 | );
523 |
524 | // Append name options and property inputs to the shadows info element
525 | shadowsInfoElement.append(shadowNameOptions, shadowPropertyInputs);
526 |
527 | // Return the shadows info element
528 | return shadowsInfoElement;
529 | };
530 |
531 | // Function to create a new shadow list element
532 | const createShadowListElement = () => {
533 | // Get the container where the shadow list will be displayed
534 | const shadowListContainer = document.getElementById("shadow-list-container");
535 | // Get the properties for the new shadow
536 | const shadowProperties = boxShadowsPropertiesList.addShadow();
537 | // Create an info element for the shadow
538 | const shadowInfoElement = createBoxShadowInfoElement(shadowProperties);
539 | // Append the shadow info element to the container
540 | shadowListContainer.append(shadowInfoElement);
541 | // Render the box shadow code
542 | renderBoxShadowCode();
543 | // Render the box shadow style
544 | renderBoxShadowStyle();
545 | };
546 |
547 | // Event listener for adding a new shadow when the button is clicked
548 | document
549 | .getElementById("add-shadow-btn")
550 | .addEventListener("click", createShadowListElement);
551 |
552 | // Get the button to expand the shadow list
553 | const expandShadowListBtn = document.getElementById("dropdown-shadow-list");
554 |
555 | // Function to expand the shadow list when the button is clicked
556 | const expandShadowList = (event) => {
557 | // Get the shadow list container
558 | const shadowListContainer = document.getElementById("shadow-list-container");
559 | // Toggle the class to show/hide the shadows
560 | shadowListContainer.classList.toggle("shadow-list__shadows--active");
561 | // Toggle the class of the expand button
562 | expandShadowListBtn.classList.toggle(
563 | "shadow-list__head__dropdown-btn--active"
564 | );
565 | };
566 |
567 | // Event listener for expanding the shadow list when the button is clicked
568 | expandShadowListBtn.addEventListener("click", expandShadowList);
569 |
570 | // Function to handle click events in the shadow list
571 | const handleClickEventInShadowList = (event) => {
572 | // Get the element that triggered the event
573 | const targetElement = event.target;
574 |
575 | // Function to expand the shadow options when the button is clicked
576 | const expandShadowOptions = () => {
577 | // Get the options container for the shadow
578 | const shadowListOptions = targetElement.parentElement.nextElementSibling;
579 | // Get the shadow list container
580 | const shadowListContainer = targetElement.parentElement.parentElement;
581 | // Toggle the class to show/hide the options
582 | shadowListOptions.classList.toggle("shadows__property-inputs--active");
583 | // Toggle the class of the expand button
584 | targetElement.classList.toggle("shadows__info__options__expand--active");
585 | // Toggle the class of the shadow list container
586 | shadowListContainer.classList.toggle("shadows__info--active");
587 | };
588 |
589 | // Function to delete a shadow when the delete button is clicked
590 | const deleteShadow = () => {
591 | // Get the parent container of the shadow list
592 | const shadowListParentContainer = targetElement.parentElement.parentElement.parentElement;
593 | // Get the shadow list element
594 | const shadowList = targetElement.parentElement.parentElement;
595 | // Remove the shadow list element from the parent container
596 | shadowListParentContainer.removeChild(shadowList);
597 | // Delete the shadow from the properties list by its id
598 | boxShadowsPropertiesList.deleteBoxShadow(
599 | Number(targetElement.dataset.shadowIdReference)
600 | );
601 | // Render the updated box shadow code
602 | renderBoxShadowCode();
603 | // Render the updated box shadow style
604 | renderBoxShadowStyle();
605 | };
606 |
607 | // Check if the clicked element is a button and has a specific data attribute to expand shadow options
608 | if (targetElement.type === "button" &&
609 | event.type === "click" &&
610 | targetElement.dataset.buttonFunction === "expand-shadow-options") {
611 | expandShadowOptions();
612 | }
613 |
614 | // Check if the clicked element is a button and has a specific data attribute to delete a shadow
615 | if (targetElement.type === "button" &&
616 | event.type === "click" &&
617 | targetElement.dataset.buttonFunction === "delete-shadow") {
618 | deleteShadow();
619 | }
620 | };
621 |
622 | // Function to handle input events in the shadow list
623 | const handleInputEventInShadowList = (event) => {
624 | const targetElement = event.target;
625 |
626 | // Function to change the property value of box shadow
627 | const changePropertyValueUnitOfBoxShadow = (newValue, valueOrUnit) => {
628 | // Accessing the relevant data attributes from the target element
629 | const targetElementShadowIdReference = Number(targetElement.dataset.boxShadowIdReference);
630 | const targetElementShadowPropertyNameReference = targetElement.dataset.boxShadowPropertyName;
631 |
632 | // Editing the box shadow properties using a specific method
633 | boxShadowsPropertiesList.editBoxShadowPropertiesUnitValue(
634 | targetElementShadowIdReference,
635 | targetElementShadowPropertyNameReference,
636 | newValue,
637 | valueOrUnit
638 | );
639 |
640 | // Rendering the updated box shadow code and style
641 | renderBoxShadowCode();
642 | renderBoxShadowStyle();
643 | };
644 |
645 | // Function to update the color text value element
646 | const updateColorTextValueElement = () => {
647 | targetElement.nextElementSibling.textContent = targetElement.value;
648 | };
649 |
650 | // Function to update the property input value
651 | const updatePropertyInputValue = () => {
652 | const inputPropertyElement = targetElement.parentElement.nextElementSibling.children[0].children[1];
653 | inputPropertyElement.value = targetElement.value;
654 | }
655 |
656 | // Conditional statements to handle different types of input events and elements
657 | if (targetElement.type === "range" && event.type === "input" && targetElement.dataset.inputFunction === "change-box-shadow-property") {
658 | changePropertyValueUnitOfBoxShadow(targetElement.value, "value");
659 | updatePropertyInputValue();
660 | }
661 |
662 | if (targetElement.type === "number" && event.type === "input" && targetElement.dataset.inputFunction === "change-box-shadow-property") {
663 | changePropertyValueUnitOfBoxShadow(targetElement.value || 0, "value");
664 | }
665 |
666 | if (targetElement.type === "color" && event.type === "input" && targetElement.dataset.inputFunction === "change-box-shadow-property") {
667 | changePropertyValueUnitOfBoxShadow(targetElement.value, "value");
668 | updateColorTextValueElement();
669 | }
670 |
671 | if (targetElement.type === "checkbox" && event.type === "click" && targetElement.dataset.inputFunction === "change-box-shadow-property") {
672 | changePropertyValueUnitOfBoxShadow(targetElement.checked, "value");
673 | }
674 |
675 | if (targetElement.type === "select-one" && event.type === "change" && targetElement.dataset.selectFunction === "change-box-shadow-property") {
676 | changePropertyValueUnitOfBoxShadow(targetElement.value, "unit");
677 | }
678 | };
679 |
680 | // Function to handle delegation of events
681 | const handleDelegateEvent = (event) => {
682 | handleClickEventInShadowList(event);
683 | handleInputEventInShadowList(event);
684 | };
685 |
686 | // Adding event listeners to the shadow list container for different event types
687 | document.getElementById("shadow-list-container").addEventListener("click", handleDelegateEvent);
688 | document.getElementById("shadow-list-container").addEventListener("input", handleDelegateEvent);
689 | document.getElementById("shadow-list-container").addEventListener("change", handleDelegateEvent);
690 |
691 | // Function to render the box shadow code based on the updated properties
692 | const renderBoxShadowCode = () => {
693 | const boxShadowList = boxShadowsPropertiesList.getBoxShadowList();
694 | const boxShadowListCodeContainer = document.getElementById("shadow-code-element-container");
695 | const boxShadowListProperty = ["x", "y", "blur", "spread", "color", "inset"];
696 |
697 | // Clearing any existing elements in the code container
698 | while (boxShadowListCodeContainer.firstChild) {
699 | boxShadowListCodeContainer.removeChild(boxShadowListCodeContainer.firstChild);
700 | }
701 |
702 | // Iterating over each box shadow and rendering the code
703 | boxShadowList.forEach((shadow) => {
704 | const shadowCodeElement = document.createElement("p");
705 |
706 | // Creating code elements for each box shadow property
707 | boxShadowListProperty.forEach((boxShadowProperty) => {
708 | let shadowCodePropertyValue = shadow[boxShadowProperty].value;
709 | const shadowCodePropertyUnit = shadow[boxShadowProperty].unit !== null ? shadow[boxShadowProperty].unit : "";
710 | const shadowCodeValueElement = createStyledElement("span", { class: "code-value" });
711 |
712 | // Handling boolean values for 'inset' property
713 | if (typeof shadowCodePropertyValue === "boolean" && shadowCodePropertyValue === true) {
714 | shadowCodePropertyValue = "inset,";
715 | } else if (typeof shadowCodePropertyValue === "boolean" && shadowCodePropertyValue === false) {
716 | shadowCodePropertyValue = ",";
717 | }
718 |
719 | // Setting text content for the code value element
720 | shadowCodeValueElement.textContent = `${shadowCodePropertyValue}${shadowCodePropertyUnit} `;
721 | shadowCodeElement.append(shadowCodeValueElement);
722 | });
723 |
724 | // Appending the code element to the container
725 | boxShadowListCodeContainer.append(shadowCodeElement);
726 | });
727 |
728 | // Handling special case for the last 'inset' property
729 | if (boxShadowListCodeContainer?.lastChild?.lastChild.textContent === "inset, ") {
730 | const lastBoxShadowProperty = boxShadowListCodeContainer?.lastChild?.lastChild;
731 | lastBoxShadowProperty.textContent = "inset";
732 | return;
733 | }
734 |
735 | // Removing unnecessary comma for the last property
736 | boxShadowListCodeContainer.lastChild?.removeChild(boxShadowListCodeContainer?.lastChild?.lastChild);
737 | };
738 |
739 | // Function to copy the box shadow code to the clipboard
740 | const copyBoxShadowCode = (event) => {
741 | // Get the box shadow code text to be copied
742 | const codeToCopy = document.getElementById("box-shadow-list-code").innerText;
743 | // Get the button element for displaying the copy status
744 | const copyCodeButtonText = document.getElementById("copy-code-text");
745 |
746 | // Copy the code to the clipboard
747 | navigator.clipboard
748 | .writeText(codeToCopy)
749 | .then(() => {
750 | // Update the button text to indicate successful copying
751 | copyCodeButtonText.textContent = "copied !";
752 | // Reset the button text after 2 seconds
753 | setTimeout(() => (copyCodeButtonText.textContent = "copy code"), 2000);
754 | })
755 | .catch((err) => {
756 | // Log any errors to the console
757 | console.log(err);
758 | });
759 | };
760 |
761 | // Add click event listener to the copy code button
762 | document
763 | .getElementById("copy-code-btn")
764 | .addEventListener("click", copyBoxShadowCode);
765 |
766 | // Function to render the box shadow style based on the properties list
767 | const renderBoxShadowStyle = () => {
768 | // Get the list of box shadow properties
769 | const boxShadowList = boxShadowsPropertiesList.getBoxShadowList();
770 | // Generate the box shadow style value based on the properties
771 | const boxShadowListStyleValue = boxShadowList.map((shadow) => {
772 | const { x, y, blur, spread, color, inset } = shadow;
773 | // Generate the individual box shadow value
774 | const insetValue = inset.value ? "inset" : "";
775 | return `${x.value}${x.unit} ${y.value}${y.unit} ${blur.value}${blur.unit} ${spread.value}${spread.unit} ${color.value} ${insetValue}`;
776 | });
777 | // Set the box shadow style for the box element
778 | boxElement.style.boxShadow = boxShadowListStyleValue.join(",");
779 | };
780 |
--------------------------------------------------------------------------------