├── 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 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/check-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/height-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/width-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/y-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /favicon-icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #00aba9 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/x-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/minus-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/spread-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/copy-code-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/add-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /favicon-icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 14 | 19 | 20 | 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 | box shadow icon 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 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/blur-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 |
39 |
40 | 41 | github icon 42 | 43 |
44 |
45 | 46 |
47 |
    48 |
  • 49 |
    50 | 51 |
    52 |
    53 | height 60 | 69 | 79 |
    80 |
  • 81 |
  • 82 |
    83 | 84 |
    85 |
    86 | width 93 | 102 | 112 |
    113 |
  • 114 |
  • 115 | 123 | #000000 129 |
  • 130 |
  • 131 | 139 | #FFFFFF 144 |
  • 145 |
146 |
147 |
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 | --------------------------------------------------------------------------------