├── .gitignore ├── CSS ├── 1-Grilles │ ├── README.md │ └── solution │ │ ├── README.md │ │ ├── app.css │ │ └── index.html └── 2-Mosaic │ ├── README.md │ ├── base │ ├── app.css │ └── index.html │ └── screenshot.jpg └── JS ├── 1-Spanify └── README.md ├── 2-Impot.fr ├── README.md ├── Solution │ ├── app.js │ ├── app.test.js │ ├── babel.config.js │ ├── index.html │ ├── jest.config.js │ ├── package.json │ ├── ui.js │ └── yarn.lock ├── TheCaliban │ ├── assets │ │ ├── css │ │ │ └── style.css │ │ └── js │ │ │ └── app.js │ └── index.html └── rgermain │ ├── calcul.js │ ├── index.html │ ├── init.js │ ├── main.js │ └── style.css ├── 3-DockMacOS ├── .gitignore ├── README.md ├── favicon.svg ├── index.html ├── package.json ├── public │ └── images │ │ ├── appstore.png │ │ ├── finder.png │ │ ├── mail.png │ │ ├── messages.png │ │ ├── systempreferences.png │ │ ├── terminal.png │ │ └── trashbin.png ├── src │ ├── main.ts │ ├── style.css │ └── vite-env.d.ts ├── tsconfig.json └── yarn.lock ├── 4-2FAInput ├── .gitignore ├── README.md ├── cypress.config.js ├── cypress │ ├── component │ │ └── CodeInput.cy.js │ └── support │ │ ├── component-index.html │ │ └── component.js ├── example.html ├── index.html ├── package.json ├── pnpm-lock.yaml ├── screenshot.png ├── src │ ├── example.js │ └── source.js ├── style.css └── vite.config.js └── 5-Calendar ├── .gitignore ├── README.md ├── bun.lockb ├── example.html ├── index.html ├── package.json ├── screenshot.jpg ├── src ├── data.js ├── example │ ├── date.js │ ├── main.js │ └── style.css ├── main.js ├── reset.css ├── style.css └── vite-env.d.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /CSS/1-Grilles/README.md: -------------------------------------------------------------------------------- 1 | # Utilisation des Grilles & Flexbox 2 | 3 | L'objectif de cet exercice est de réfléchir sur la structure à adopter pour créer un formulaire en gardant une structure simple et facilement extensible. 4 | 5 | 6 | ## Scénario 7 | 8 | Le designer vous a fourni une maquette pour un formulaire sur le logiciel Figma : https://www.figma.com/file/CgKcMZLE29Nzqx4vNhlbnL/ChallengeForm?node-id=0%3A1 . Le designer n'a en revanche pas fourni de direction pour la partie responsive et vous devez improviser l'affichage sur les différents périphériques. 9 | 10 | - On se concentre ici sur la structure du formulaire plus que sur le style des éléments. 11 | - L'intégration doit être responsive. 12 | - L'utilisation de framework est autorisée. 13 | 14 | ## Pour participer 15 | 16 | Vous pouvez éditer ce fichier directement sur github en mettant votre pseudo suivi d'un lien codesandbox/jsfiddle ou autre permettant de voir cotre réponse 17 | 18 | ## Vos solutions 19 | 20 | - Pseudo - https://grafikart.fr/exemple-de-lien 21 | 22 | ### Grille 23 | 24 | - Dim - https://jsfiddle.net/dimitri_bouteille/awzvq6uh/ ⭐ 25 | - L4rks - https://codesandbox.io/s/divine-monad-l32vi ⭐ 26 | - MalikH - https://webdevproformation.github.io/grafikart-challenges/flex.html ⭐ (autofill) 27 | - Bowser65 - https://codesandbox.io/s/grafikart-challenge-1-whp3t 28 | - Roman R - https://codepen.io/romanryckebusch/pen/abvjJxw?editors=1100 (autofill, A11Y) 29 | - Bokad - https://jsfiddle.net/veq0Lf6y/ 30 | - BaptouFou - https://repl.it/repls/WorthwhileSeashellIrc 31 | - Yann - https://codepen.io/yann-marec/pen/VwvBbYB 32 | - HugoWit - https://jsfiddle.net/Hugowit/6qud1yrp/8/ 33 | - Choulss (grid+flex) - https://codesandbox.io/s/heuristic-cherry-4t3xk?file=/style.css (bonne gestion du format tablette) 34 | - PhiSyX - https://jsfiddle.net/PhiSyX/Lrzyvepd/ (bonne gestion du format tablette) 35 | - Gwen.L - https://codepen.io/gwenlacosse/pen/oNjMbzW (template area) 36 | - Papa Mouhamadou DIOP - https://codesandbox.io/s/bold-water-xvj6i 37 | - stephcache - https://jsfiddle.net/stephcache/6rasvc5e/24/ 38 | - JeanBon - https://codepen.io/felixdorn/pen/QWjBgqQ 39 | - Guillaume ASTIER - https://codesandbox.io/embed/spooky-challenge-grafikart-css-1-kp3pi?fontsize=14&hidenavigation=1&theme=dark&view=preview 40 | - Voltra - https://codesandbox.io/s/grille-flex-pzpy0?file=/main.css 41 | - Glioburd - https://codesandbox.io/s/grid-form-ufi2z (grid-area, fullscreen : https://ufi2z.csb.app/) 42 | - Chewbathra - https://codesandbox.io/s/wonderful-panini-f1clh ⭐ 43 | - Taro - https://codesandbox.io/s/vigorous-williamson-mlzwi?file=/style.css 44 | - akbak - https://codesandbox.io/s/xenodochial-cerf-3f0z0 45 | - Florian - https://jsfiddle.net/7sbvw8an/2/ 46 | - Matteo - https://codesandbox.io/s/funny-morning-pkq3i (template area) 47 | - itsadeki - https://codesandbox.io/s/challenge-css-grille-gnic0 48 | - AnttonDev - https://codesandbox.io/s/grafikartchallenge-by-anttondev-yi8ss 49 | - Ugo.P - https://codepen.io/Ugo_P/pen/BaoOoJW 50 | - georges - https://jsfiddle.net/dan_georges/opds19wm/ (template area) 51 | - Tatal - https://jsfiddle.net/ametthey/gL7qy5us/68/ (template area) 52 | - avallete - https://codesandbox.io/s/grafikart-css-challenge-01-bppic 53 | - gturpin - https://codepen.io/ThPrGanesha/pen/VwvGYMW?editors=1100 (grid + flex) 54 | 55 | ### Flex 56 | 57 | - Norem - https://codepen.io/Norem/pen/GRpGbVO 58 | - Vscool - https://codesandbox.io/s/fragrant-brook-8evnd?file=/index.html 59 | - Couapy - https://codesandbox.io/s/nervous-shannon-vih2g?file=/style.css 60 | - Jayson - https://codepen.io/burke9/pen/pojZyoy 61 | - Jordan L. - https://codesandbox.io/s/challenge-grafikart-formulaire-responsive-wsk8v 62 | - whiskey92 - https://codepen.io/jl92000/pen/pojZZzW 63 | - m-vann - https://codesandbox.io/s/m-vann-u6rcu?file=/index.html 64 | - ARNOLD - https://codepen.io/arnold2020/pen/gOajazR 65 | - Jayson Mourier - https://codepen.io/burke9/pen/pojZyoy 66 | - Roger Carter - https://codesandbox.io/s/eloquent-fog-wl3ji?file=/index.html 67 | - Jam83 - https://codesandbox.io/s/agitated-dew-8gnl4?file=/index.html (avec position absolute) 68 | - celtak - https://codepen.io/Celtak/pen/WNQKQrZ 69 | - Darth_Judge - https://codesandbox.io/s/loving-star-mpnsd 70 | - jam83 - https://codesandbox.io/s/agitated-dew-8gnl4 71 | - stevar - https://jsfiddle.net/stevar/ck3sq2Lw/2/#&togetherjs=6fJUkr3Ulj (pb d'alignement de champs & tabulation) 72 | - Nicolas41 - https://codesandbox.io/s/tender-pine-1thci 73 | - Jam8310 - https://codesandbox.io/s/agitated-dew-8gnl4 74 | - cissoko23 - https://jsfiddle.net/cissoko23/xsLb6v83/1/ 75 | 76 | ## Float 77 | 78 | - Farouk - https://jsfiddle.net/Roukfa/t8epz9o0/8/ 79 | 80 | ### Framework CSS 81 | 82 | - rherault - https://codepen.io/romaixn/pen/vYNrPag (tailwind) 83 | - Amiega - https://codepen.io/fdfd/pen/KKdeEvB 84 | - Art29 - https://codesandbox.io/s/challengeform-fr1v9 (bootstrap) 85 | - olivier49 - https://codepen.io/olivier49/pen/WNQKjwv 86 | - JulienKit - https://codesandbox.io/s/julienkit-grafikart-challenge-css-mai-2020-b4tiq (bootstrap) 87 | - Maxime - https://codesandbox.io/s/morning-mountain-qcvdx?file=/index.html (bootstrap) 88 | - Quentin Geeraert - https://codesandbox.io/s/grafikart-challenge-b84xt?file=/index.html (bootstrap) 89 | -------------------------------------------------------------------------------- /CSS/1-Grilles/solution/README.md: -------------------------------------------------------------------------------- 1 | ## A propos de la solution 2 | 3 | Pour cet exercice on a plusieurs solutions possibles mais les critères importants sont : 4 | 5 | - Le code HTMLuniux doit rester simple et extensible (pouvoir rajouter des champs facilement plus tard par exemple, et éviter une structure trop en lien avec le design). 6 | - Faire en sorte que la tabulation sélectionne les champs dans le bon ordre. 7 | - Faire en sorte que le responsive gère un maximum de cas (ne pas oublier les tablettes). 8 | - Gérer convenablement les espaces entre les champs quelquesoit la résolution. 9 | 10 | Les quelques points clefs de la solution proposée : 11 | 12 | - `grid-template-columns: repeat(auto-fill, minmax(297px, 1fr));` permet de faire en sorte que le navigateur crée automatiquement les colonnes ce qui permet de simplifier le responsive sans avoir à créer des breakpoint. 13 | - `align-content`, `align-self`, `align-items` permettent de gérer convenablement les alignements, surtout si un champs à un message en dessous 14 | - Pour que le champs textarea prenne toute la hauteur on utilise `align-content: stretch;` 15 | -------------------------------------------------------------------------------- /CSS/1-Grilles/solution/app.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | color: #374151; 7 | margin: 0; 8 | font-family: sans-serif; 9 | background-color: #E5E5E5; 10 | } 11 | 12 | .page { 13 | display: flex; 14 | width: 100%; 15 | align-items: center; 16 | min-height: 100vh; 17 | } 18 | 19 | .card { 20 | border-radius: 4px; 21 | width: 1028px; 22 | max-width: calc(100% - 20px); 23 | margin-left: auto; 24 | margin-right: auto; 25 | padding: 40px; 26 | background-color: #FFF; 27 | box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.1); 28 | } 29 | 30 | .card-title { 31 | font-size: 24px; 32 | font-weight: bold; 33 | color: #000; 34 | padding-bottom: 8px; 35 | border-bottom: 1px solid #D7DBDF; 36 | margin-bottom: 30px; 37 | } 38 | 39 | /** 40 | * Flex 41 | */ 42 | .flex { 43 | display: flex; 44 | } 45 | .flex > * + * { 46 | margin-left: 8px; 47 | } 48 | .flex-end { 49 | justify-content: flex-end; 50 | } 51 | 52 | /** 53 | * Grille 54 | */ 55 | .grid { 56 | display: grid; 57 | grid-template-columns: repeat(auto-fill, minmax(297px, 1fr)); 58 | grid-gap: 24px; 59 | align-items: stretch; 60 | } 61 | 62 | @media only screen and (max-width: 600px) { 63 | .grid { 64 | grid-template-columns: 1fr; 65 | } 66 | .card { 67 | padding: 24px; 68 | } 69 | } 70 | 71 | .grid .full { 72 | grid-column:1 / -1; 73 | } 74 | 75 | .grid .form-group-textarea { 76 | grid-row-end: span 2; 77 | height: 100%; 78 | align-content: stretch; 79 | } 80 | 81 | .grid .btn-primary { 82 | justify-self: flex-end; 83 | grid-column-end: -1; 84 | } 85 | 86 | /** 87 | * Style générique des formulaires 88 | */ 89 | .form-group { 90 | display: grid; 91 | grid-template-columns: 1fr; 92 | align-content: flex-start; 93 | grid-template-rows: min-content; 94 | grid-gap: 8px; 95 | } 96 | 97 | .form-control { 98 | display: block; 99 | width: 100%; 100 | border: 1px solid #D2D6DB; 101 | border-radius: 6px; 102 | font-size: 16px; 103 | padding: 14px 10px; 104 | } 105 | 106 | .form-check { 107 | cursor: pointer; 108 | display: flex; 109 | align-items: center; 110 | } 111 | 112 | .form-check-label { 113 | cursor: pointer; 114 | } 115 | 116 | .form-check-input { 117 | margin-right: 8px; 118 | } 119 | 120 | .btn { 121 | border: none; 122 | color: #FFF; 123 | font-weight: 500; 124 | padding: 15px 25px; 125 | border-radius: 6px; 126 | font-size: 16px; 127 | cursor: pointer; 128 | } 129 | 130 | .btn-primary { 131 | background-color: #5850EB; 132 | } 133 | 134 | .btn-secondary { 135 | background-color: grey; 136 | } 137 | 138 | .form-checkboxes { 139 | display: flex; 140 | flex-direction: column; 141 | margin-left: -16px; 142 | margin-top: -16px; 143 | } 144 | 145 | .form-checkboxes > * { 146 | margin-top: 16px; 147 | margin-left: 16px; 148 | } 149 | 150 | @media only screen and (min-width: 760px) { 151 | .form-checkboxes { 152 | flex-direction: row; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /CSS/1-Grilles/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Exercice 7 | 8 | 9 | 10 |
11 |
12 |
Modifier mon profil
13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 |
46 |
47 |
48 | 49 | 50 |
51 |
52 | 53 | 54 |
55 |
56 |
57 | 58 | 59 |
60 |
61 |
62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /CSS/2-Mosaic/README.md: -------------------------------------------------------------------------------- 1 | # Utilisation des Grilles & Flexbox 2 | 3 | L'objectif de cet exercice est de réfléchir à la structure à adopter pour créer un layout particulier en minimisant le nombre de règles à créer. 4 | 5 | ## Scénario 6 | 7 | On cherche à créer une structure originale pour nos articles. Une personne a déjà travaillé le style général des cartes et a besoin de votre aide pour construire la structure en fonction des différentes résolutions. 8 | 9 | ![Résultat attendu](./screenshot.jpg) 10 | 11 | **Quelques infos :** 12 | - Les cartes sont espacées de 16px 13 | - Le bouton est espacé de 24px par rapport aux cartes 14 | - Sur grand écran la carte centrale a été mis en avant et a une largeur de 419px (cette valeur doit pouvoir être changé facilement via une variable CSS) 15 | - Une class `.is-expanded` est ajouté au container lors du clic sur le bouton. 16 | 17 | ## Règles 18 | 19 | - Vous ne pouvez éditer que le fichier app.css (pas de JavaScript en plus). 20 | - Vous ne pouvez pas modifier le fichier HTML. 21 | - Vous devez créer le fichier CSS le plus petit possible (on comparera le nombre de règles et la taille du fichier gzippé). Inutile de tout mettre sur une seule ligne par exemple. 22 | 23 | -------------------------------------------------------------------------------- /CSS/2-Mosaic/base/app.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Ajoutez vos règles ici 3 | **/ 4 | -------------------------------------------------------------------------------- /CSS/2-Mosaic/base/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Title 5 | 6 | 74 | 75 | 76 |
77 |
78 | 79 |

Titre de la carte

80 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores blanditiis commodi enim esse eveniet facilis 81 | fugit id impedit iste itaque, laboriosam laudantium, maiores nobis optio temporibus ullam velit veniam vero!

82 | 87 |
88 |
89 | 90 |

Titre de la carte

91 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores blanditiis commodi enim esse eveniet facilis 92 | fugit id impedit iste itaque, laboriosam laudantium, maiores nobis optio temporibus ullam velit veniam vero!

93 | 98 |
99 |
100 | 101 |

Titre de la carte

102 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores blanditiis commodi enim esse eveniet facilis 103 | fugit id impedit iste itaque, laboriosam laudantium, maiores nobis optio temporibus ullam velit veniam vero!

104 | 109 |
110 |
111 | 112 |

Titre de la carte

113 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores blanditiis commodi enim esse eveniet facilis 114 | fugit id impedit iste itaque, laboriosam laudantium, maiores nobis optio temporibus ullam velit veniam vero!

115 | 120 |
121 |
122 | 123 |

Titre de la carte

124 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores blanditiis commodi enim esse eveniet facilis 125 | fugit id impedit iste itaque, laboriosam laudantium, maiores nobis optio temporibus ullam velit veniam vero!

126 | 131 |
132 |
133 | 134 |

Titre de la carte

135 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores blanditiis commodi enim esse eveniet facilis 136 | fugit id impedit iste itaque, laboriosam laudantium, maiores nobis optio temporibus ullam velit veniam vero!

137 | 142 |
143 | 144 | 145 | 146 |
147 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /CSS/2-Mosaic/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/CSS/2-Mosaic/screenshot.jpg -------------------------------------------------------------------------------- /JS/1-Spanify/README.md: -------------------------------------------------------------------------------- 1 | # Manipulation du DOM et récursivité 2 | 3 | L'objectif est ici de créer une fonction capable d'entourer chaque mot d'un élément `span` 4 | 5 | 6 | ## Scénario 7 | 8 | J'aimerais créer un effet d'animation mot par mot et il me faut donc convertir la structure HTML de mon titre 9 | 10 | ```html 11 |

Ceci est un titre

12 | ``` 13 | 14 | En entourant chaque mot d'une 15 | 16 | ```html 17 |

Ceci est un titre

18 | ``` 19 | 20 | Vous pouvez utiliser ce template CodeSandbox pour tester votre code (actualiser dans la partie de droite pour tester votre solution). 21 | Vous pouvez soumettre votre solution ici (un seul message par personne et la solution peut être un lien jsfiddle / codesandbox / github...) 22 | 23 | ## Les solutions 24 | 25 | Présentation des solutions : https://www.youtube.com/watch?v=4BB7KjRqS2Y 26 | 27 | ### Solutions basées sur le traitement du texte 28 | 29 | - @Hachemarre : https://codesandbox.io/s/challenge-spanify-0hl99?fontsize=14&hidenavigation=1&theme=dark Machine à état pour trouver les espaces 30 | - @Alex : https://codesandbox.io/s/challenge-spanify-h2lxd (split par espace) 31 | - @roulianne : https://codesandbox.io/s/challenge-spanify-xbrbv?file=/src/index.js (split par espace) 32 | - @Pierre MINIGGIO : https://codesandbox.io/s/challenge-spanify-n7sm5?file=/src/index.js (split par espace) 33 | - @dukizwee : https://codesandbox.io/s/challenge-spanify-z703k?file=/src/index.js (construit l'html puis innerHTML) 34 | - @Thilladon : https://codesandbox.io/s/challenge-spanify-uxsp6 (split par espace) 35 | - @[P5]Assane : https://codesandbox.io/s/challenge-spanify-q09bf?file=/src/index.js 36 | - @M0rg1tOu : https://codesandbox.io/s/challenge-spanify-cech4?file=/src/index.js (remplace mot par mot) 37 | - @Gm Fmi : https://codesandbox.io/s/challenge-spanify-sqbs2?file=/src/index.js 38 | - @Papa Mouhamadou DIOP : https://codesandbox.io/s/challenge-spanify-pd0hq?file=/src/index.js 39 | - @Julien Jamet : https://codesandbox.io/s/challenge-spanify-xbrbv?file=/src/index.js (split par ' ') 40 | - @Luc Varoqui : https://codesandbox.io/s/challenge-spanify-dn3b9 41 | - @KR.FM : https://codesandbox.io/s/challenge-spanify-uib08?file=/src/index.js 42 | - @Yoyo Bu : https://codesandbox.io/s/challenge-spanify-i2tb3?file=/src/index.js remplacement mot par mot 43 | - @Nekena Ratafita Haritsimba : https://codesandbox.io/s/challenge-spanify-00b18 (mot par mot) 44 | - @Mael Genevrais : https://codesandbox.io/s/challenge-spanify-e7dgm?file=/src/index.js 45 | - @One3yeDriss : https://codesandbox.io/s/challenge-spanify-eu9wx?file=/src/index.js:383-556 46 | - @Arthir Varennes : https://codesandbox.io/s/challenge-spanify-xhy4m?file=/src/index.js 47 | - @Marcel Gnac : https://codesandbox.io/s/challenge-spanify-z26vr?file=/src/index.js 48 | - @Alain Brunel : https://codesandbox.io/s/challenge-spanify-7k34d?file=/src/index.js 49 | - @Mike Mirosz : https://codesandbox.io/s/challenge-spanify-uif9q?file=/src/index.js 50 | - @Maxime Girard : https://codesandbox.io/s/challenge-spanify-dxdcn?file=/src/index.js:298-421 51 | 52 | 53 | ### Solution basées sur une expressions régulière 54 | 55 | - @Arnich : https://codesandbox.io/s/challenge-spanify-2lfik 56 | - @Gux : https://codesandbox.io/s/challenge-spanify-c1tdb?file=/src/index.js (très court) 57 | - @Julian : https://codesandbox.io/s/challenge-spanify-d1cqb?file=/src/index.js:84-88 (split basé sur une regex) 58 | - @Negwael : https://codesandbox.io/s/challenge-spanify-m5z20 59 | - @Senoyoru : https://codesandbox.io/s/challenge-spanify-zdxzr (replace) 60 | - @Radonirina : https://codesandbox.io/s/challenge-spanify-4jxij 61 | - @D0rian : https://codesandbox.io/s/challenge-spanify-gjd28?file=/src/index.js 62 | - @Sylvain : https://codesandbox.io/s/challenge-spanify-tx9rx?file=/src/index.js 63 | - @Jojopata : https://codesandbox.io/s/challenge-spanify-v8ool (oneliner) 64 | - @Max : https://codesandbox.io/s/challenge-spanify-d3phi?file=/src/index.js (oneliner) 65 | - @betaWeb : https://codesandbox.io/s/challenge-spanify-uxsp6 (oneliner) 66 | - @Maxime Chêne : https://codesandbox.io/s/challenge-spanify-d3phi?file=/src/index.js 67 | 68 | ### Solutions basées sur la manipulation du DOM 69 | 70 | - @RomainLanz : https://codesandbox.io/s/challenge-spanify-wskw2?file=/src/index.js (petites fonctions) 71 | - @Grafikart : https://codesandbox.io/s/challenge-spanify-answer-nm8i2?file=/src/index.js 72 | - @bernard-ng : https://codesandbox.io/s/challenge-spanify-jjq2w?file=/src/index.js:738-748 (une seule fonction) 73 | - @Wassim : https://codesandbox.io/s/challenge-spanify-u6q7h?file=/src/index.js 74 | - @nuks : https://codesandbox.io/s/challenge-spanify-lpcol 75 | - @Clément : https://codesandbox.io/s/challenge-spanify-i2ix7?file=/src/index.js 76 | - @Voltra : https://codesandbox.io/s/challenge-spanify-3ttir?file=/src/index.js (dom + regex pour les espaces) 77 | - @HyOsh : https://codesandbox.io/s/challenge-spanify-w7g94?file=/src/index.js (TreeWalker) 78 | - @Akanoa : https://codesandbox.io/s/challenge-spanify-4gdzf 79 | - @elkolotfi : https://codesandbox.io/s/challenge-spanify-gskd6?file=/src/index.js 80 | - @Callan : https://codesandbox.io/s/challenge-spanify-zfcq5 81 | - @Mathieu : https://codesandbox.io/s/challenge-spanify-f600w?file=/src/index.js 82 | - @magicshark : https://codesandbox.io/s/challenge-spanify-ipj51?file=/src/index.js 83 | - @François : https://codesandbox.io/embed/challenge-spanify-vtlhv ([].forEach.call()) 84 | - @Nioub : https://codesandbox.io/s/challenge-spanify-s8vp4?file=/src/index.js (récursivité + regex) 85 | - @Apox : https://codesandbox.io/s/challenge-spanify-x1tte?file=/src/index.js 86 | - @Jérémie Sellam : https://codesandbox.io/s/challenge-spanify-br3du?file=/src/index.js (attention à utiliser des constantes) 87 | - @Fx : https://codesandbox.io/s/challenge-spanify-uindv (code propre / simple) 88 | - @fred : https://codesandbox.io/s/challenge-spanify-ssx8x?file=/src/index.js 89 | - @Barnard Ngandu : https://codesandbox.io/s/challenge-spanify-jjq2w?file=/src/index.js 90 | - @Log Wagler : https://codepen.io/YggLife/pen/RwWVWRo?editors=0010 91 | - @Wassim Bechouel : https://codesandbox.io/s/challenge-spanify-u6q7h?file=/src/index.js (logique un peu complexe) 92 | - @Vincent Diezel https://codesandbox.io/s/challenge-spanify-snj0t?file=/src/index.js 93 | - @Deltackro : https://codesandbox.io/s/challenge-spanify-93jnz?file=/src/index.js (bien commenté) 94 | - @InfrazFull : https://codesandbox.io/s/challenge-spanify-9q1zc 95 | - @Mr. Kèive : https://codesandbox.io/s/challenge-spanify-rw0yw?file=/src/index.js 96 | - @ridrum ridrum : https://codesandbox.io/s/challenge-spanify-w94n8 97 | - @Vasco Compain : https://codesandbox.io/s/challenge-spanify-g7zm1?file=/src/index.js (utilise des constante ;) 98 | - @IoDream : https://codesandbox.io/s/challenge-spanify-s8vp4?file=/src/index.js 99 | - @Epic Kiwi : https://codesandbox.io/s/challenge-spanify-czm1d?file=/src/index.js 100 | - @Jérémie Sellam : https://codesandbox.io/s/challenge-spanify-br3du?file=/src/index.js:95-111 101 | - @Darcy Dar'c https://codesandbox.io/s/challenge-spanify-z703k?file=/src/index.js:97-104 (ne gère pas plus d'1 niveau de profondeur) 102 | - @Cedric Cholley https://codesandbox.io/s/challenge-spanify-l5fdg?file=/src/index.js:696-768 103 | 104 | ### Solutions trop spécifique au problème, presque de la triche ;) 105 | 106 | - @Julien G : https://codesandbox.io/s/challenge-spanify-3s4mn?file=/src/index.js 107 | - @Hugo : https://codesandbox.io/s/challenge-spanify-64dvm?file=/src/index.js 108 | 109 | 110 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/README.md: -------------------------------------------------------------------------------- 1 | # Calculateur d'impôt sur le revenu 2 | 3 | On souhaite créer un formulaire permettant de calculer l'impôt que l'on va payer pour l'année courante. 4 | L'objectif de cet exercice est de tester votre capacité à interpréter une consigne et traduire une explication en algorithme. 5 | 6 | Le gouvernement a créer une fiche explicative permettant de comprendre le calcul du taux d'imposition mais ce calcul s'avère relativement complexe et on souhaite créer une interface simplifier pour calculer les choses. 7 | 8 | https://www.economie.gouv.fr/particuliers/tranches-imposition-impot-revenu#etapescalculir 9 | 10 | ## Niveau 1 11 | 12 | Pour commencer on souhaite créer un formulaire dans lequel on va entrée la somme que l'on compte déclarer (valeur net) et le système doit automatiquement calculer l'impôt que l'on va avoir à payer ainsi que la somme qu'il nous restera (On prendra le cas d'un célibataire pour commencer). 13 | 14 | ## Niveau 2 15 | 16 | On ajoutera une case à cocher pour préciser si on est en couple et un autre champs nous permettra de préciser le nombre d'enfant. En fonction de ces nouvelles données le système adaptera le taux. 17 | 18 | ## Niveau 3 19 | 20 | On souhaite avoir un détail du montant que l'on paye par tranche. Le système affichera donc un tableau pour préciser le montant que l'on doit payer pour chaque tranche. 21 | 22 | ## Boss final 23 | 24 | Pour l'exercice final on prendra le problème en sens inverse et on permettra à l'utilisateur d'entrer la somme désiré (après impôt) et le système calculera les revenus net à avoir pour obtenir cette somme après l'imposition. 25 | 26 | # Solutions : 27 | 28 | ## Vanilla 29 | 30 | - ⭐@JOTSR https://github.com/JOTSR/ChallengesGrafikart/tree/master/JS/2-impot ([Demo](https://julienoculi.com/challenge_grafikart/impot/index.html)) 31 | - @meschac38700 : https://codesandbox.io/s/purple-frost-b4c8g?file=/app.js 32 | - @Arnich : https://nervous-mclean-74e21c.netlify.app - [Github](https://github.com/Oipnet/calculateur-impots) 33 | - @lsarrazi : https://playcode.io/659406 34 | - @mcdostone : https://codesandbox.io/s/angry-cloud-z8n08 35 | - @mdsv41 : https://codesandbox.io/s/gallant-torvalds-rem0p 36 | - @Dedouduck : https://codesandbox.io/s/wispy-pond-lwrlz 37 | 38 | ## React 39 | 40 | - ⭐@Mania#6276 : https://codesandbox.io/s/github/MathisBarre/grafikart-challenge-impots ([Demo](https://sl8sp.csb.app/)) 41 | - @LemaireJean-Baptiste : [CodeSandbox](https://codesandbox.io/s/grafikart-challenge-tax-calculator-jbl-dr8zo) 42 | - @jordanmonier : https://codesandbox.io/s/elated-wright-wgt9i (Typescript) 43 | - @playeurzero : https://ovmzu.csb.app/ 44 | - @Anass-Rhazal : https://codesandbox.io/s/sparkling-framework-d54g8 45 | - @samibettayeb : https://github.com/dchallenges/tax-calculator ([Demo](https://dchallenges.github.io/taxca/)) 46 | - @mojinet : https://codepen.io/mojinet/pen/jOqBRBQ 47 | - @zacoDev : https://github.com/zacoDev/grafikart-challenge ([Demo](http://www.calculimpotfrance.tk/)) 48 | - @jeremyradenne : https://codesandbox.io/s/heoqi Typescript ([Demo](https://heoqi.csb.app/)) 49 | - @abourtnik : https://github.com/abourtnik/simulator-taxes ([Demo](https://abourtnik.github.io/simulator-taxes)) 50 | 51 | ## VueJS 52 | 53 | - ⭐@eroak : https://codesandbox.io/s/coding-challenge-js-impot-pb7c0 ([Demo](https://pb7c0.csb.app/)) 54 | - @Da-max : https://codesandbox.io/s/calcul-impot-3dkxp niveau 3 (avec vue et vuetify) 55 | - @aschelch : https://jsfiddle.net/aschelch/a9emwd1t/9/ 56 | - @Khivar : https://khivar.github.io/taxes-calculator/ ([Code](https://github.com/Khivar/taxes-calculator)) 57 | 58 | ## Svelte 59 | 60 | - @PeufOne : https://codesandbox.io/s/wandering-morning-g7ow0 61 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/Solution/app.js: -------------------------------------------------------------------------------- 1 | export const RATES_2021 = [ 2 | { 3 | max: 10084, 4 | rate: 0 5 | }, { 6 | max: 25710, 7 | rate: .11 8 | }, { 9 | max: 73516, 10 | rate: .30 11 | }, { 12 | max: 158122, 13 | rate: .41 14 | }, { 15 | max: Infinity, 16 | rate: .45 17 | } 18 | ] 19 | 20 | export const RATES_2020 = [ 21 | { 22 | max: 10064, 23 | rate: 0 24 | }, { 25 | max: 25659, 26 | rate: .11 27 | }, { 28 | max: 73369, 29 | rate: .30 30 | }, { 31 | max: 157806, 32 | rate: .41 33 | }, { 34 | max: Infinity, 35 | rate: .45 36 | } 37 | ] 38 | 39 | export const RATES_2019 = [ 40 | { 41 | max: 10064, 42 | rate: 0 43 | }, { 44 | max: 27794, 45 | rate: .14 46 | }, { 47 | max: 74517, 48 | rate: .30 49 | }, { 50 | max: 157806, 51 | rate: .41 52 | }, { 53 | max: Infinity, 54 | rate: .45 55 | } 56 | ] 57 | 58 | /** 59 | * Arrondit un nombre 60 | * 61 | * @param n 62 | * @param decimal 63 | * @returns {number} 64 | */ 65 | function round(n, decimal) { 66 | return Math.round(n * (10 ** decimal)) / (10 ** decimal) 67 | } 68 | 69 | /** 70 | * Calcul la quantité d'imposition par rapport au tranche 71 | * 72 | * @param {number} revenue 73 | * @param {array} rates 74 | * @returns {number} 75 | */ 76 | export function impot(revenue, rates = RATES_2021) { 77 | const tranches = impotWithTranches(revenue, rates) 78 | return tranches.reduce((acc, tranche) => acc + tranche, 0) 79 | } 80 | 81 | 82 | /** 83 | * Calcul la quantité d'imposition par rapport au tranche 84 | * 85 | * @param {number} revenue 86 | * @param {array} rates 87 | * @returns {number[]} 88 | */ 89 | export function impotWithTranches(revenue, rates = RATES_2021) { 90 | const tranches = [] 91 | for (const index in rates) { 92 | const rate = rates[index] 93 | const min = rates[index - 1] ? rates[index - 1].max + 1 : 0 94 | if (revenue > rate.max) { 95 | tranches.push(round((rate.max - min) * rate.rate, 2)) 96 | } else { 97 | tranches.push(round((revenue - min) * rate.rate, 2)) 98 | break 99 | } 100 | } 101 | return [...tranches, ...new Array(rates.length - tranches.length).fill(0)] 102 | } 103 | 104 | 105 | /** 106 | * Calcul le nombre de part d'un couple 107 | * 108 | * @param isMarried 109 | * @param children 110 | */ 111 | export function getParts(isMarried = false, children = 0) { 112 | return (isMarried ? 2 : 1) + (children * .5) 113 | } 114 | 115 | /** 116 | * Calcul l'impot avec les part 117 | * 118 | * @param {number} revenues 119 | * @param {number} part 120 | * @param {array} rates 121 | */ 122 | export function impotWithPart(revenues, part, rates = RATES_2021) { 123 | return Math.round(part * impot(revenues / part, rates)) 124 | } 125 | 126 | /** 127 | * Calcul l'inversion de l'impot (trouve le revenu avant impot) 128 | * 129 | * @param {number} value 130 | * @param {array} rates 131 | */ 132 | export function impotReversed(value, parts = 1, rates = RATES_2021) { 133 | // On calcul l'impot associé au max de chaque branche 134 | const reversedTranches = rates.map(rate => { 135 | const due = impot(rate.max, rates) 136 | return { 137 | ...rate, 138 | impot: due, 139 | afterImpotMax: rate.max - due 140 | } 141 | }) 142 | // On trouve dans quel tranche va se trouver l'utilisateur 143 | let index = reversedTranches.findIndex(tranche => value / parts < tranche.afterImpotMax) 144 | // On récupère les infos de la tranche courante et précédente 145 | const tranche = reversedTranches[index] 146 | let previousTranche = reversedTranches[index - 1]; 147 | const previousTrancheImpot = previousTranche ? previousTranche.impot : 0 148 | const min = previousTranche ? previousTranche.max + 1 : 0 149 | return round( (value + previousTrancheImpot - min * tranche.rate) / (1 - tranche.rate), 2) 150 | } 151 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/Solution/app.test.js: -------------------------------------------------------------------------------- 1 | import {impot, impotReversed, impotWithPart, impotWithTranches, RATES_2019, RATES_2020, RATES_2021} from './app' 2 | 3 | describe('Imposition', () => { 4 | 5 | describe('impot()', () => { 6 | 7 | test('Célibataire avec 32 000 € en 2020', () => { 8 | expect(impot(32000, RATES_2020)).toBe(3617.34) 9 | }) 10 | 11 | test('Célibataire avec 18 650 € en 2020', () => { 12 | expect(impot(18650, RATES_2020)).toBe(944.35) 13 | }) 14 | 15 | test('Célibataire avec 32 000 € en 2019', () => { 16 | expect(impot(32000, RATES_2019)).toBe(3743.56) 17 | }) 18 | 19 | test('Célibataire avec 18 650 € en 2019', () => { 20 | expect(impot(18650, RATES_2019)).toBe(1201.9) 21 | }) 22 | 23 | test('Célibataire avec 32 000 € en 2021', () => { 24 | expect(impot(32000, RATES_2021)).toBe(3605.45) 25 | }) 26 | 27 | }) 28 | 29 | describe('impotWithPart()', () => { 30 | 31 | test('Célibataire avec 32 000 €', () => { 32 | expect(impotWithPart(32000, 1, RATES_2020)).toBe(3617) 33 | }) 34 | 35 | test('Couple + 2 mineurs avec 55 950 €', () => { 36 | expect(impotWithPart(55950, 3, RATES_2020)).toBe(2833) 37 | }) 38 | 39 | test('Célibataire avec 32 000 € en 2019', () => { 40 | expect(impotWithPart(32000, 1, RATES_2019)).toBe(3744) 41 | }) 42 | 43 | test('Célibataire avec 55 950 € en 2019', () => { 44 | expect(impotWithPart(55950, 3, RATES_2019)).toBe(3606) 45 | }) 46 | 47 | }) 48 | 49 | describe('impotWithTranches()', () => { 50 | 51 | test('Célibataire avec 32 000 €', () => { 52 | expect(impotWithTranches(32000, RATES_2020)).toEqual([0, 1715.34, 1902, 0, 0]) 53 | }) 54 | 55 | test('Célibataire avec 18 650 €', () => { 56 | expect(impotWithTranches(18650, RATES_2020)).toEqual([0, 944.35, 0, 0, 0]) 57 | }) 58 | 59 | test('Célibataire avec 32 000 € en 2019', () => { 60 | expect(impotWithTranches(32000, RATES_2019)).toEqual([0, 2482.06, 1261.5, 0, 0]) 61 | }) 62 | 63 | test('Célibataire avec 18 650 € en 2019', () => { 64 | expect(impotWithTranches(18650, RATES_2019)).toEqual([0, 1201.9, 0, 0, 0]) 65 | }) 66 | 67 | }) 68 | 69 | test.each([ 70 | [32000], 71 | [10000], 72 | [18650], 73 | [50000], 74 | [100000], 75 | [150000], 76 | ])('impotReversed(%p)', (target) => { 77 | expect(impotReversed(target - impot(target), RATES_2020)).toBe(target) 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/Solution/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/Solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 |

Calculateur d'impôt

11 |
12 | Informations 13 |

14 | 15 | 16 |

17 |

18 | 19 | 20 |

21 |

22 | 23 | 24 |

25 |
26 |
27 | Résultats 28 |

29 | Impôts :
30 | Taux : 31 |

32 |

33 | 34 | (le calcul inversé ne prend pas en compte les parts) 35 |

36 |
37 |
38 | Tranches 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
PallierTauxImpot
49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/Solution/jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | 8 | // Stop running tests after `n` failures 9 | // bail: 0, 10 | 11 | // The directory where Jest should store its cached dependency information 12 | // cacheDirectory: "/tmp/jest_rs", 13 | 14 | // Automatically clear mock calls and instances between every test 15 | clearMocks: true, 16 | 17 | // Indicates whether the coverage information should be collected while executing the test 18 | // collectCoverage: false, 19 | 20 | // An array of glob patterns indicating a set of files for which coverage information should be collected 21 | // collectCoverageFrom: undefined, 22 | 23 | // The directory where Jest should output its coverage files 24 | // coverageDirectory: undefined, 25 | 26 | // An array of regexp pattern strings used to skip coverage collection 27 | // coveragePathIgnorePatterns: [ 28 | // "/node_modules/" 29 | // ], 30 | 31 | // Indicates which provider should be used to instrument code for coverage 32 | coverageProvider: "v8", 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: undefined, 44 | 45 | // A path to a custom dependency extractor 46 | // dependencyExtractor: undefined, 47 | 48 | // Make calling deprecated APIs throw helpful error messages 49 | // errorOnDeprecated: false, 50 | 51 | // Force coverage collection from ignored files using an array of glob patterns 52 | // forceCoverageMatch: [], 53 | 54 | // A path to a module which exports an async function that is triggered once before all test suites 55 | // globalSetup: undefined, 56 | 57 | // A path to a module which exports an async function that is triggered once after all test suites 58 | // globalTeardown: undefined, 59 | 60 | // A set of global variables that need to be available in all test environments 61 | // globals: {}, 62 | 63 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 64 | // maxWorkers: "50%", 65 | 66 | // An array of directory names to be searched recursively up from the requiring module's location 67 | // moduleDirectories: [ 68 | // "node_modules" 69 | // ], 70 | 71 | // An array of file extensions your modules use 72 | // moduleFileExtensions: [ 73 | // "js", 74 | // "json", 75 | // "jsx", 76 | // "ts", 77 | // "tsx", 78 | // "node" 79 | // ], 80 | 81 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 82 | // moduleNameMapper: {}, 83 | 84 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 85 | // modulePathIgnorePatterns: [], 86 | 87 | // Activates notifications for test results 88 | // notify: false, 89 | 90 | // An enum that specifies notification mode. Requires { notify: true } 91 | // notifyMode: "failure-change", 92 | 93 | // A preset that is used as a base for Jest's configuration 94 | // preset: undefined, 95 | 96 | // Run tests from one or more projects 97 | // projects: undefined, 98 | 99 | // Use this configuration option to add custom reporters to Jest 100 | // reporters: undefined, 101 | 102 | // Automatically reset mock state between every test 103 | // resetMocks: false, 104 | 105 | // Reset the module registry before running each individual test 106 | // resetModules: false, 107 | 108 | // A path to a custom resolver 109 | // resolver: undefined, 110 | 111 | // Automatically restore mock state between every test 112 | // restoreMocks: false, 113 | 114 | // The root directory that Jest should scan for tests and modules within 115 | // rootDir: undefined, 116 | 117 | // A list of paths to directories that Jest should use to search for files in 118 | // roots: [ 119 | // "" 120 | // ], 121 | 122 | // Allows you to use a custom runner instead of Jest's default test runner 123 | // runner: "jest-runner", 124 | 125 | // The paths to modules that run some code to configure or set up the testing environment before each test 126 | // setupFiles: [], 127 | 128 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 129 | // setupFilesAfterEnv: [], 130 | 131 | // The number of seconds after which a test is considered as slow and reported as such in the results. 132 | // slowTestThreshold: 5, 133 | 134 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 135 | // snapshotSerializers: [], 136 | 137 | // The test environment that will be used for testing 138 | // testEnvironment: "jest-environment-jsdom", 139 | 140 | // Options that will be passed to the testEnvironment 141 | // testEnvironmentOptions: {}, 142 | 143 | // Adds a location field to test results 144 | // testLocationInResults: false, 145 | 146 | // The glob patterns Jest uses to detect test files 147 | // testMatch: [ 148 | // "**/__tests__/**/*.[jt]s?(x)", 149 | // "**/?(*.)+(spec|test).[tj]s?(x)" 150 | // ], 151 | 152 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 153 | // testPathIgnorePatterns: [ 154 | // "/node_modules/" 155 | // ], 156 | 157 | // The regexp pattern or array of patterns that Jest uses to detect test files 158 | // testRegex: [], 159 | 160 | // This option allows the use of a custom results processor 161 | // testResultsProcessor: undefined, 162 | 163 | // This option allows use of a custom test runner 164 | // testRunner: "jasmine2", 165 | 166 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 167 | // testURL: "http://localhost", 168 | 169 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 170 | // timers: "real", 171 | 172 | // A map from regular expressions to paths to transformers 173 | // transform: undefined, 174 | 175 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 176 | // transformIgnorePatterns: [ 177 | // "/node_modules/", 178 | // "\\.pnp\\.[^\\/]+$" 179 | // ], 180 | 181 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 182 | // unmockedModulePathPatterns: undefined, 183 | 184 | // Indicates whether each individual test should be reported during the run 185 | // verbose: undefined, 186 | 187 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 188 | // watchPathIgnorePatterns: [], 189 | 190 | // Whether to use watchman for file crawling 191 | // watchman: true, 192 | }; 193 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/Solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Solution", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "@babel/core": "^7.11.6", 8 | "@babel/preset-env": "^7.11.5", 9 | "@types/jest": "^26.0.13", 10 | "babel-jest": "^26.3.0", 11 | "browser-sync": "^2.26.12", 12 | "jest": "^26.4.2" 13 | }, 14 | "scripts": { 15 | "test": "jest", 16 | "dev":"browser-sync -s -w **/*.js" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/Solution/ui.js: -------------------------------------------------------------------------------- 1 | import { getParts, impot, impotWithPart, impotWithTranches, RATES_2021, impotReversed } from './app.js' 2 | 3 | const revenusInput = document.getElementById('revenues') 4 | const impotText = document.getElementById('impot') 5 | const resultInput = document.getElementById('result') 6 | const rateText = document.getElementById('rate') 7 | const coupleCheckbox = document.getElementById('couple') 8 | const childrenInput = document.getElementById('children') 9 | const tableBody = document.getElementById('tbody') 10 | const trancheCells = []; 11 | let direction = 1; // Direction du calcul 12 | 13 | const currencyFormatter = new Intl.NumberFormat('fr-FR', { 14 | currency: 'EUR', 15 | style: 'currency', 16 | minimumFractionDigits: 0, 17 | maximumFractionDigits: 0 18 | }) 19 | const numberFormat = new Intl.NumberFormat('fr-FR') 20 | const percentFormat = new Intl.NumberFormat('fr-FR', { style: 'percent' }) 21 | 22 | function handleChange(e = null) { 23 | // On définit la direction du calcul 24 | if (e && e.target === resultInput) { 25 | direction = -1 26 | } else if (e === null || e.target === revenusInput) { 27 | direction = 1 28 | } 29 | const sourceInput = direction === 1 ? revenusInput : resultInput 30 | const targetInput = direction === 1 ? resultInput : revenusInput 31 | let value = parseFloat(sourceInput.value) 32 | if (Number.isNaN(value)) { 33 | return; 34 | } 35 | // On calcul le nbre de part 36 | const parts = getParts(coupleCheckbox.checked, parseInt(childrenInput.value, 10)) 37 | let impotValue 38 | if (direction === 1) { 39 | impotValue = impotWithPart(value, parts) 40 | targetInput.value = value - impotValue 41 | } else { 42 | const revenusBeforeImpot = impotReversed(value, parts) 43 | targetInput.value = revenusBeforeImpot 44 | impotValue = revenusBeforeImpot - value 45 | value = revenusBeforeImpot 46 | } 47 | 48 | // On met à jour les texte avec le taux / et les chiffres 49 | impotText.innerText = currencyFormatter.format(impotValue) 50 | rateText.innerText = percentFormat.format(impotValue / value) 51 | resultInput.value = value - impotValue 52 | 53 | // On met à jour la valeur des tranches 54 | const tranches = impotWithTranches(value / parts) 55 | for (let index in tranches) { 56 | trancheCells[index].innerText = currencyFormatter.format(tranches[index]) 57 | } 58 | } 59 | 60 | function buildTable() { 61 | const rates = RATES_2021 62 | for (const rate of rates) { 63 | const tr = document.createElement('tr') 64 | tr.innerHTML = ` 65 | ${rate.max} 66 | ${percentFormat.format(rate.rate)} 67 | 0 € 68 | ` 69 | trancheCells.push(tr.lastElementChild) 70 | tableBody.appendChild(tr) 71 | } 72 | } 73 | 74 | revenusInput.addEventListener('input', handleChange) 75 | coupleCheckbox.addEventListener('change', handleChange) 76 | childrenInput.addEventListener('change', handleChange) 77 | resultInput.addEventListener('input', handleChange) 78 | 79 | buildTable() 80 | handleChange() 81 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/TheCaliban/assets/css/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | font-size: 1.2rem; 4 | height: 100%; 5 | position: relative; 6 | } 7 | 8 | input[type="text"] { 9 | height: 100%; 10 | min-height: 25px; 11 | border: 1px solid #b3b3b3; 12 | border-radius: 15px; 13 | padding: 5px; 14 | font-size: 0.8rem; 15 | outline: none; 16 | } 17 | 18 | .header { 19 | background-color: #808080; 20 | color: #ffffff; 21 | padding: 15px 10px; 22 | } 23 | 24 | .header h1 { 25 | font-size: 2rem; 26 | margin: 0; 27 | } 28 | 29 | .content { 30 | text-align:center; 31 | padding: 15px; 32 | } 33 | 34 | /* Form */ 35 | 36 | .tax-form { 37 | width: auto; 38 | margin: auto; 39 | } 40 | 41 | .tax-form div:nth-child(2) { 42 | margin: 15px 0; 43 | } 44 | 45 | .tax-input-div { 46 | display: flex; 47 | flex-direction: column; 48 | } 49 | 50 | #tax-input-child { 51 | width: 20px; 52 | border-radius: 25%; 53 | text-align: center; 54 | } 55 | 56 | .tax-output-div { 57 | margin-top: 20px; 58 | } 59 | 60 | #no-tax-msg { 61 | align-content: center; 62 | text-decoration: underline; 63 | } 64 | 65 | /* Table */ 66 | 67 | .table-div { 68 | margin-top: 30px; 69 | display:none; 70 | } 71 | 72 | .table-tax-pourcent { 73 | font-weight: bold; 74 | } 75 | 76 | #table-step-tax { 77 | margin: auto; 78 | border-collapse: collapse; 79 | } 80 | 81 | #table-step-tax td { 82 | border: 1px solid black; 83 | padding: 10px; 84 | } 85 | 86 | .footer { 87 | position: absolute; 88 | display: flex; 89 | justify-content: center; 90 | bottom: 0; 91 | padding: 15px 0; 92 | width: 100%; 93 | font-size: 20px; 94 | } 95 | 96 | /* Media queries */ 97 | 98 | @media (max-width: 500px) { 99 | .tax-form { 100 | max-width: 100%; 101 | margin: 0; 102 | } 103 | 104 | } 105 | 106 | @media (max-width: 600px) { 107 | .header { 108 | text-align: center; 109 | } 110 | } -------------------------------------------------------------------------------- /JS/2-Impot.fr/TheCaliban/assets/js/app.js: -------------------------------------------------------------------------------- 1 | $('#tax-input-income, #tax-input-status, #tax-input-child').on('input', function () { 2 | let taxStep = { 3 | 45: { 4 | max: Infinity, 5 | min: 157807 6 | }, 7 | 41: { 8 | max: 157806, 9 | min: 73370 10 | }, 11 | 30: { 12 | max: 73369, 13 | min: 25660 14 | }, 15 | 11: { 16 | max: 25659, 17 | min: 10065 18 | } 19 | } 20 | 21 | let taxDuByStep = { 22 | 45: 0, 23 | 41: 0, 24 | 30: 0, 25 | 11: 0, 26 | 0: 0 27 | } 28 | 29 | let brutIncome = ($('#tax-input-income').val() != '') ? parseInt($('#tax-input-income').val()) : 0; 30 | let status = (!$('#tax-input-status').prop('checked')) ? 1 : 2; 31 | let nbChild = ($('#tax-input-child').val() != '') ? parseInt($('#tax-input-child').val()) : 0; 32 | if (nbChild > 2) { 33 | nbChild = 1 + (nbChild - 2) 34 | } 35 | let quotientFam = status + nbChild; 36 | 37 | let qfBrutIncome = brutIncome / quotientFam; 38 | let netIncome = 0; 39 | let incrementTax = 0; 40 | let duEachStep = 0; 41 | 42 | 43 | $.each(taxStep, (pourcent, limit) => { 44 | if (qfBrutIncome > limit.min) { 45 | if (qfBrutIncome > limit.max) { 46 | duEachStep = (limit.max - limit.min) * (pourcent / 100); 47 | } 48 | else { 49 | duEachStep = (qfBrutIncome - limit.min) * (pourcent / 100); 50 | } 51 | taxDuByStep[pourcent] = duEachStep; 52 | incrementTax += duEachStep; 53 | } 54 | }); 55 | 56 | let $table = $('#table-step-tax tbody'); 57 | $table.html(''); 58 | 59 | $.each(taxDuByStep, (pourcent, amount) => { 60 | 61 | if (amount > 0) { 62 | $table.append(` 63 | ${pourcent} 64 | ${amount.toFixed(2)} 65 | ${taxStep[pourcent].min} - ${taxStep[pourcent].max} 66 | `); 67 | } 68 | }); 69 | 70 | if (incrementTax > 0) { 71 | $('.table-div').css('display', 'block'); 72 | $('#no-tax-msg').css('display', 'none'); 73 | } 74 | else { 75 | 76 | $('.table-div').css('display', 'none'); 77 | $('#no-tax-msg').css('display', 'block'); 78 | } 79 | 80 | netIncome = brutIncome - incrementTax; 81 | 82 | $('#tax-span-amount').html(`${incrementTax.toFixed(2)} €`); 83 | $('#tax-span-rest').html(`${netIncome} €`); 84 | }); -------------------------------------------------------------------------------- /JS/2-Impot.fr/TheCaliban/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Calculateur d'impôt 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

Évaluer le montant de vos impôts

17 |
18 | 19 |
20 |
21 |
22 |
23 | 24 | € 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 |
37 |
38 | Impôt dû : 39 | 0€ 40 |
41 |
42 | Revenus net : 43 | 0€ 44 |
45 |
46 | 47 |
48 | 49 | Vous n'êtes pas concernés par les impôts 50 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
Votre côtisation détaillée
%MontantTranche
67 |
68 |
69 | 70 | 71 | 74 | 75 | 76 | 77 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/rgermain/calcul.js: -------------------------------------------------------------------------------- 1 | const DEBUG = true 2 | 3 | function debug(text, value) { 4 | if (DEBUG) { 5 | console.log("debug:\n\t" + text + " = ", value) 6 | } 7 | } 8 | 9 | /* list des paliers d'impositions 2020 */ 10 | const part2020 = { 11 | "10064": 11, 12 | "25659": 30, 13 | "73369": 41, 14 | "157806": 45, 15 | } 16 | 17 | /* list des paliers d'impositions 2019 */ 18 | const part2019 = { 19 | "10064": 14, 20 | "27794": 30, 21 | "74517": 41, 22 | "157806": 45, 23 | } 24 | 25 | /* 26 | ** sorry pour les fautes :D 27 | */ 28 | 29 | 30 | /* 31 | function pour calculer le revenue le taux d'imposiition part rapport au revenue 32 | */ 33 | function calculImpot(part, revenue, quotient) { 34 | 35 | /* on divise le revenue par le quotient */ 36 | revenue /= quotient 37 | debug('revenue par le quotient', revenue) 38 | 39 | let total = 0 // sera le total a payer 40 | let tranch = [] // sera une liste des tranches avec la valeurs a payer 41 | 42 | const keys = Object.keys(part) 43 | 44 | for (let i = 0; i <= keys.length; i++) { 45 | 46 | let older = 0 47 | let coef = 0 48 | // check le premier element du tableu et remplace par 0 49 | if (i != 0) { 50 | // on ajout 1 a au palier precedent 51 | older = parseInt(keys[i - 1]) + 1 52 | coef = part[keys[i - 1]] 53 | } 54 | 55 | let diff 56 | if (i != keys.length) { 57 | // on fait la diff entre le pallier precedent et le palier actuelle 58 | diff = Math.abs(older - parseInt(keys[i])) 59 | // on soustrait le revenue - le palier precedent, en prenant au maximun le 0 60 | // si il vaut 0 c'est que le revenue est trop bas pour ce pallier 61 | diff = Math.min(Math.max(revenue - older, 0), diff) 62 | } else { 63 | // cas pour le dernier 64 | diff = Math.max(revenue - older, 0) 65 | } 66 | 67 | // on multiplie la diff par le coef 68 | const impot = Math.round(diff * coef / 100) 69 | 70 | // min = palier min 71 | // impot = l'impot a payer pour cette tranche 72 | // coef = le coefitient d'imposiiton 73 | tranch.push({min: older, impot, coef}) 74 | 75 | total += impot 76 | } 77 | // on mutiplie par le quotient, et on arondie 78 | total = Math.round(total * quotient) 79 | 80 | return [total, tranch] 81 | } 82 | 83 | 84 | /* 85 | function pour calculer le revenue a declarer part rapport a l'impot 86 | */ 87 | function calculeReverse(part, impot, quotient) { 88 | impot /= quotient 89 | debug('impot par le quotient', impot) 90 | 91 | let total = 0 // sera le total a avoir 92 | let tranch = [] // sera une liste des tranches avec la valeurs a payer 93 | let finish = false 94 | 95 | const keys = Object.keys(part) 96 | 97 | for (let i = 0; i <= keys.length; i++) { 98 | 99 | let older = 0 100 | let coef = 0 101 | // check le premier element du tableu et remplace par 0 102 | if (i != 0) { 103 | older = parseInt(keys[i - 1]) + 1 104 | coef = part[keys[i - 1]] 105 | } 106 | 107 | if (finish) { 108 | // uniquent pour le front 109 | tranch.push({min: older, impot: 0, coef}) 110 | } 111 | else if (i == keys.length) { 112 | total = (impot * 100 / coef) + older 113 | tranch.push({min: older, impot: Math.round(impot) * quotient, coef}) 114 | } else { 115 | const actual = parseInt(keys[i]) // convertie en int la clef 116 | const diff = Math.abs(older - actual) 117 | const maxImpot = diff * coef / 100 118 | 119 | if (maxImpot >= impot) { 120 | 121 | // on recupere le pourcentage de l'impot par rapport a l'impot maxium du palier 122 | const diffImpot = (maxImpot / impot) 123 | 124 | // on recupere la valuer real de ce pourcentage, et on l'additionne au total 125 | total = ((actual - older) / diffImpot) + older 126 | 127 | // on ajout le palier precedent 128 | tranch.push({min: older, impot: Math.round(impot) * quotient, coef}) 129 | finish = true 130 | } else { 131 | impot = impot - maxImpot 132 | tranch.push({min: older, impot: Math.ceil(maxImpot) * quotient, coef}) 133 | } 134 | } 135 | } 136 | // on mutiplie par le quotient, et on arondie 137 | total = Math.round(total * quotient) 138 | 139 | return [total, tranch] 140 | } -------------------------------------------------------------------------------- /JS/2-Impot.fr/rgermain/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Calculateur d'impôt sur le revenu 7 | 8 | 9 | 10 | 11 |
12 |

13 | Calculateur d'impôt sur le revenu 14 |

15 |
16 |
17 | 18 |
19 | 22 | 25 |
26 |
27 | 32 |
33 | 34 | Votre situation 35 | 36 | 40 |
41 | 45 |
46 | 51 | 54 |
55 | 73 |
74 | 123 |
124 |
125 | 126 |
127 |
128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/rgermain/init.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** init 3 | */ 4 | 5 | const impotBtn = document.getElementById('impot') 6 | const revenueBtn = document.getElementById('revenue') 7 | const impotTarget = document.querySelector(document.getElementById('impot').dataset.target) 8 | const revenueTarget = document.querySelector(document.getElementById('revenue').dataset.target) 9 | 10 | const resultTranch2020 = document.getElementById('recap-2020') 11 | const resultTranch2019 = document.getElementById('recap-2019') 12 | 13 | const resultTranchRevenue2020 = document.getElementById('recap-2020-revenue') 14 | const resultTranchRevenue2019 = document.getElementById('recap-2019-revenue') 15 | 16 | impotBtn.addEventListener('click', () => { 17 | impotBtn.classList.add('border-blue-700') 18 | impotBtn.classList.remove('border-transparent') 19 | 20 | revenueBtn.classList.remove('border-blue-700') 21 | revenueBtn.classList.add('border-transparent') 22 | 23 | impotTarget.classList.remove('hidden') 24 | revenueTarget.classList.add('hidden') 25 | 26 | resultTranch2020.classList.remove('hidden') 27 | resultTranch2019.classList.remove('hidden') 28 | resultTranchRevenue2020.classList.add('hidden') 29 | resultTranchRevenue2019.classList.add('hidden') 30 | }) 31 | 32 | revenueBtn.addEventListener('click', () => { 33 | revenueBtn.classList.add('border-blue-700') 34 | revenueBtn.classList.remove('border-transparent') 35 | 36 | impotBtn.classList.remove('border-blue-700') 37 | impotBtn.classList.add('border-transparent') 38 | 39 | revenueTarget.classList.remove('hidden') 40 | impotTarget.classList.add('hidden') 41 | 42 | resultTranch2020.classList.add('hidden') 43 | resultTranch2019.classList.add('hidden') 44 | resultTranchRevenue2020.classList.remove('hidden') 45 | resultTranchRevenue2019.classList.remove('hidden') 46 | }) 47 | -------------------------------------------------------------------------------- /JS/2-Impot.fr/rgermain/main.js: -------------------------------------------------------------------------------- 1 | const result = document.getElementById('result') 2 | const result2 = document.getElementById('result-revenue') 3 | 4 | function colors(idx) { 5 | return ["purple", "blue", "orange", "yellow", "red"][Math.floor(idx / 5)] 6 | } 7 | 8 | function constructTab(target, tranch, year) { 9 | 10 | const val = tranch.reduce((tab, el, idx) => { 11 | const v = `
12 | __min__ 13 | 14 | ${el.impot}€ 15 | 16 | 17 | ${el.coef}% 18 | 19 |
${tab}` 20 | const v2 = `${el.min}€` 21 | return v.replace('__min__', (idx ? v2 : "")) 22 | }, "") 23 | target.innerHTML = `

recap ${year}

${val}` 24 | } 25 | 26 | // formulaire pour calculer le taux dimposition 27 | const form = document.getElementById('form') 28 | form.addEventListener('submit', e => { 29 | e.preventDefault() 30 | const from = new FormData(e.target) 31 | 32 | // nombre de part du quotient familial 33 | // multiplie de nombre d'enfant par 0.5 si inferieur ou egal a 2 enfant 34 | // ou on eneleve une part ( is 3 enfant == 3 - 1 donc 2) 35 | let enfants = parseInt(from.get('enfants')) 36 | enfants = (enfants > 2 ? enfants - 1 : enfants *= 0.5) 37 | 38 | const quotient = parseInt(from.get('situation')) + enfants 39 | debug('quotient', quotient) 40 | 41 | // revenue 42 | const revenue = parseInt(from.get('revenue')) 43 | debug('revenue', revenue) 44 | 45 | // on calcule l'impot et on le multiplie par le quotient 46 | const [impot2020, tranch2020] = calculImpot(part2020, revenue, quotient) 47 | const [impot2019, tranch2019] = calculImpot(part2019, revenue, quotient) 48 | 49 | debug('impot 2020', impot2020) 50 | debug('vos tranche 2020', tranch2020) 51 | debug('impot 2019', impot2019) 52 | debug('vos tranche 2019', tranch2019) 53 | 54 | 55 | /* 56 | ** ------- front ------- 57 | */ 58 | result.classList.remove('hidden') 59 | document.getElementById('2020').innerText = `${impot2020}€` 60 | document.getElementById('2019').innerText = `${impot2019}€` 61 | document.getElementById('quotient').innerText = `${quotient} part` 62 | 63 | const diff = impot2019 - impot2020 64 | if (diff > 0) { 65 | document.getElementById('diff').innerText = `gagnez ${diff}€` 66 | } else if (diff < 0) { 67 | document.getElementById('diff').innerText = `perdu ${diff}€` 68 | } else { 69 | document.getElementById('diff').innerText = `rien gagnez ... ${revenue < 500 ? " et rien foutue ..." : ""}` 70 | } 71 | constructTab(resultTranch2020, tranch2020, "2020") 72 | constructTab(resultTranch2019, tranch2019, "2019") 73 | 74 | }) 75 | 76 | // formulaire pour calculer le revenue 77 | const from2 = document.getElementById('form-revenue') 78 | from2.addEventListener('submit', e => { 79 | e.preventDefault() 80 | 81 | const from = new FormData(e.target) 82 | 83 | let enfants = parseInt(from.get('enfants')) 84 | enfants = (enfants > 2 ? enfants - 1 : enfants *= 0.5) 85 | 86 | const quotient = parseInt(from.get('situation')) + enfants 87 | debug('quotient', quotient) 88 | 89 | // revenue 90 | const impot = parseInt(from.get('impot')) 91 | debug('impot', impot) 92 | 93 | // on calcule l'impot et on le multiplie par le quotient 94 | const [revenue2020, tranch2020] = calculeReverse(part2020, impot, quotient) 95 | const [revenue2019, tranch2019] = calculeReverse(part2019, impot, quotient) 96 | 97 | debug('revenue 2020', revenue2020) 98 | debug('vos tranche 2020', tranch2020) 99 | debug('revenue 2019', revenue2019) 100 | debug('vos tranche 2019', tranch2019) 101 | 102 | /* 103 | ** ------- front ------- 104 | */ 105 | result2.classList.remove('hidden') 106 | document.getElementById('2020-revenue').innerText = `${revenue2020}€` 107 | document.getElementById('2019-revenue').innerText = `${revenue2019}€` 108 | document.getElementById('quotient-revenue').innerText = `${quotient} part` 109 | 110 | const diff = revenue2019 - revenue2020 111 | if (diff > 0) { 112 | document.getElementById('diff-revenue').innerText = `perdu ${Math.abs(diff)}€` 113 | } else if (diff < 0) { 114 | document.getElementById('diff-revenue').innerText = `gagnez ${Math.abs(diff)}€` 115 | } else { 116 | document.getElementById('diff-revenue').innerText = `rien gagnez ... ${revenue < 500 ? " et rien foutue ..." : ""}` 117 | } 118 | constructTab(resultTranchRevenue2020, tranch2020, "2020") 119 | constructTab(resultTranchRevenue2019, tranch2019, "2019") 120 | }) -------------------------------------------------------------------------------- /JS/2-Impot.fr/rgermain/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | * { 6 | box-sizing: border-box; 7 | } 8 | 9 | .from > * + * { 10 | margin-top: 2rem; 11 | } 12 | .tab-recap > * + * { 13 | border-top-width: 2px; 14 | border-color: white; 15 | } 16 | .text-tab { 17 | position: absolute; 18 | bottom: calc(-1em + (2px / 2)); 19 | left: 0; 20 | padding: 0 1em; 21 | z-index: 10; 22 | background-color: inherit; 23 | } 24 | .text-tab-percent { 25 | bottom: -.5rem; 26 | } -------------------------------------------------------------------------------- /JS/3-DockMacOS/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /JS/3-DockMacOS/README.md: -------------------------------------------------------------------------------- 1 | # Recréer le dock de MacOS 2 | 3 | Tout est dans le titre ;) 4 | 5 | La solution se trouver dans le dossier SRC 6 | -------------------------------------------------------------------------------- /JS/3-DockMacOS/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /JS/3-DockMacOS/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 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 |
35 |
36 |
37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 |
47 | 48 |
49 |
50 | 51 |
52 |
53 | 54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 |
65 |
66 | 67 |
68 |
69 | 70 |
71 |
72 | 73 |
74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 | 82 |
83 |
84 |
85 |
86 |
87 |
88 | 89 |
90 |
91 | 92 |
93 |
94 | 95 |
96 |
97 | 98 |
99 |
100 | 101 |
102 |
103 | 104 |
105 |
106 | 107 |
108 |
109 |
110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /JS/3-DockMacOS/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dock-macos", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build", 7 | "serve": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "typescript": "^4.3.2", 11 | "vite": "^2.6.4" 12 | } 13 | } -------------------------------------------------------------------------------- /JS/3-DockMacOS/public/images/appstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/3-DockMacOS/public/images/appstore.png -------------------------------------------------------------------------------- /JS/3-DockMacOS/public/images/finder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/3-DockMacOS/public/images/finder.png -------------------------------------------------------------------------------- /JS/3-DockMacOS/public/images/mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/3-DockMacOS/public/images/mail.png -------------------------------------------------------------------------------- /JS/3-DockMacOS/public/images/messages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/3-DockMacOS/public/images/messages.png -------------------------------------------------------------------------------- /JS/3-DockMacOS/public/images/systempreferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/3-DockMacOS/public/images/systempreferences.png -------------------------------------------------------------------------------- /JS/3-DockMacOS/public/images/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/3-DockMacOS/public/images/terminal.png -------------------------------------------------------------------------------- /JS/3-DockMacOS/public/images/trashbin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/3-DockMacOS/public/images/trashbin.png -------------------------------------------------------------------------------- /JS/3-DockMacOS/src/main.ts: -------------------------------------------------------------------------------- 1 | import "./style.css"; 2 | 3 | function between(val: number, min: number, max: number) { 4 | return Math.max(min, Math.min(val, max)); 5 | } 6 | 7 | function scaling(x: number) { 8 | return between(-0.2 * Math.pow(x, 2) + 1.05, 0, 1); 9 | } 10 | 11 | enum Direction { 12 | Left = "right", 13 | Right = "left", 14 | Up = "bottom", 15 | Down = "top", 16 | None = "center", 17 | } 18 | 19 | enum Position { 20 | Top = "top", 21 | Bottom = "bottom", 22 | Left = "left", 23 | Right = "right", 24 | } 25 | 26 | class MacDock { 27 | private root: HTMLElement; 28 | private icons: HTMLElement[]; 29 | private iconSize: number; 30 | private mousePosition: number = 0; 31 | private scale: number = 0.5; 32 | private position: Position; 33 | 34 | constructor( 35 | el: HTMLElement, 36 | { position = Position.Left }: { position: Position } 37 | ) { 38 | this.root = el; 39 | this.position = position; 40 | this.icons = Array.from(el.children) as HTMLElement[]; 41 | this.iconSize = this.icons[0].offsetWidth; 42 | el.addEventListener("mousemove", this.handleMouseMove.bind(this)); 43 | el.addEventListener("mouseenter", this.handleMouseEnter.bind(this)); 44 | el.addEventListener("mouseleave", this.handleMouseLeave.bind(this)); 45 | } 46 | 47 | get isVertical() { 48 | return [Position.Left, Position.Right].includes(this.position); 49 | } 50 | 51 | handleMouseMove(e: MouseEvent) { 52 | this.mousePosition = between( 53 | this.isVertical 54 | ? (e.clientY - this.root.offsetTop) / this.iconSize 55 | : (e.clientX - this.root.offsetLeft) / this.iconSize, 56 | 0, 57 | this.icons.length 58 | ); 59 | 60 | this.scaleIcons(); 61 | } 62 | 63 | scaleIcons() { 64 | const selectedIndex = Math.floor(Math.abs(this.mousePosition)); 65 | const centerOffset = this.mousePosition - selectedIndex - 0.5; 66 | const baseOffset = this.scaleFromDirection( 67 | selectedIndex, 68 | Direction.None, 69 | -centerOffset * this.iconSize * this.scale 70 | ); 71 | let offset = baseOffset * (0.5 - centerOffset); 72 | for (let i = selectedIndex + 1; i < this.icons.length; i++) { 73 | offset += this.scaleFromDirection( 74 | i, 75 | this.isVertical ? Direction.Down : Direction.Right, 76 | offset 77 | ); 78 | } 79 | offset = baseOffset * (0.5 + centerOffset); 80 | for (let i = selectedIndex - 1; i >= 0; i--) { 81 | offset += this.scaleFromDirection( 82 | i, 83 | this.isVertical ? Direction.Up : Direction.Left, 84 | -offset 85 | ); 86 | } 87 | } 88 | 89 | scaleFromDirection( 90 | index: number, 91 | direction: Direction, 92 | offset: number 93 | ): number { 94 | const icon = this.icons[index]; 95 | const x = this.mousePosition - index - 0.5; 96 | const scale = scaling(x) * this.scale; 97 | icon.style.setProperty( 98 | "transform", 99 | `translate${this.isVertical ? "Y" : "X"}(${offset}px) scale(${scale + 1})` 100 | ); 101 | icon.style.setProperty("transform-origin", `${direction} ${this.position}`); 102 | return scale * this.iconSize; 103 | } 104 | 105 | handleMouseLeave() { 106 | this.icons.forEach((icon) => { 107 | icon.style.removeProperty("transform"); 108 | icon.style.setProperty("transition", "transform .1s"); 109 | }); 110 | } 111 | 112 | handleMouseEnter() { 113 | this.icons.forEach((icon) => { 114 | icon.style.setProperty("transition", "transform .1s"); 115 | }); 116 | window.setTimeout(() => { 117 | this.icons.forEach((icon) => { 118 | icon.style.removeProperty("transition"); 119 | }); 120 | }, 100); 121 | } 122 | } 123 | 124 | new MacDock(document.querySelector(".dock__wrapper--bottom .dock")!, { 125 | position: Position.Bottom, 126 | }); 127 | new MacDock(document.querySelector(".dock__wrapper--top .dock")!, { 128 | position: Position.Top, 129 | }); 130 | new MacDock(document.querySelector(".dock__wrapper--left .dock")!, { 131 | position: Position.Left, 132 | }); 133 | new MacDock(document.querySelector(".dock__wrapper--right .dock")!, { 134 | position: Position.Right, 135 | }); 136 | -------------------------------------------------------------------------------- /JS/3-DockMacOS/src/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | background-color: #000; 4 | margin: 0; 5 | padding: 0; 6 | font-family: sans-serif; 7 | } 8 | 9 | .dock__wrapper { 10 | position: absolute; 11 | left: 0; 12 | right: 0; 13 | bottom: 0; 14 | display: flex; 15 | justify-content: center; 16 | } 17 | 18 | .dock__wrapper--left, 19 | .dock__wrapper--right { 20 | position: absolute; 21 | top: 0; 22 | bottom: 0; 23 | left: 0; 24 | right: auto; 25 | display: flex; 26 | flex-direction: column; 27 | justify-content: center; 28 | } 29 | .dock__wrapper--right { 30 | left: auto; 31 | right: 0; 32 | } 33 | .dock__wrapper--left .dock, 34 | .dock__wrapper--right .dock { 35 | flex-direction: column; 36 | } 37 | 38 | .dock__wrapper--top { 39 | top: 0; 40 | bottom: auto; 41 | } 42 | 43 | .dock { 44 | position: relative; 45 | display: flex; 46 | } 47 | 48 | .dock img { 49 | width: 64px; 50 | height: 64px; 51 | object-fit: contain; 52 | } 53 | 54 | .icon { 55 | position: relative; 56 | z-index: 2; 57 | cursor: pointer; 58 | width: 64px; 59 | height: 64px; 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | color: #FFF; 64 | } 65 | -------------------------------------------------------------------------------- /JS/3-DockMacOS/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /JS/3-DockMacOS/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "moduleResolution": "Node", 8 | "strict": true, 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "noEmit": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "noImplicitReturns": true 16 | }, 17 | "include": ["./src"] 18 | } 19 | -------------------------------------------------------------------------------- /JS/3-DockMacOS/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | esbuild-android-arm64@0.13.8: 6 | version "0.13.8" 7 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz#c20e875c3c98164b1ffba9b28637bdf96f5e9e7c" 8 | integrity sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA== 9 | 10 | esbuild-darwin-64@0.13.8: 11 | version "0.13.8" 12 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz#f46e6b471ddbf62265234808a6a1aa91df18a417" 13 | integrity sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng== 14 | 15 | esbuild-darwin-arm64@0.13.8: 16 | version "0.13.8" 17 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz#a991157a6013facd4f2e14159b7da52626c90154" 18 | integrity sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ== 19 | 20 | esbuild-freebsd-64@0.13.8: 21 | version "0.13.8" 22 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz#301601d2e443ad458960e359b402a17d9500be9d" 23 | integrity sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw== 24 | 25 | esbuild-freebsd-arm64@0.13.8: 26 | version "0.13.8" 27 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz#039a63acc12ec0892006c147ea221e55f9125a9f" 28 | integrity sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng== 29 | 30 | esbuild-linux-32@0.13.8: 31 | version "0.13.8" 32 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz#c537b67d7e694b60bfa2786581412838c6ba0284" 33 | integrity sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw== 34 | 35 | esbuild-linux-64@0.13.8: 36 | version "0.13.8" 37 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz#0092fc8a064001a777bfa0e3b425bb8be8f96e6a" 38 | integrity sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w== 39 | 40 | esbuild-linux-arm64@0.13.8: 41 | version "0.13.8" 42 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz#5cd3f2bb924212971482e8dbc25c4afd09b28110" 43 | integrity sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ== 44 | 45 | esbuild-linux-arm@0.13.8: 46 | version "0.13.8" 47 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz#ad634f96bf2975536907aeb9fdb75a3194f4ddce" 48 | integrity sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg== 49 | 50 | esbuild-linux-mips64le@0.13.8: 51 | version "0.13.8" 52 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz#57857edfebf9bf65766dc8be1637f2179c990572" 53 | integrity sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA== 54 | 55 | esbuild-linux-ppc64le@0.13.8: 56 | version "0.13.8" 57 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz#fdb82a059a5b86bb10fb42091b4ebcf488b9cd46" 58 | integrity sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig== 59 | 60 | esbuild-netbsd-64@0.13.8: 61 | version "0.13.8" 62 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz#d7879e7123d3b2c04754ece8bd061aa6866deeff" 63 | integrity sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q== 64 | 65 | esbuild-openbsd-64@0.13.8: 66 | version "0.13.8" 67 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz#88b280b6cb0a3f6adb60abf27fc506c506a35cf0" 68 | integrity sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw== 69 | 70 | esbuild-sunos-64@0.13.8: 71 | version "0.13.8" 72 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz#229ae7c7703196a58acd0f0291ad9bebda815d63" 73 | integrity sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw== 74 | 75 | esbuild-windows-32@0.13.8: 76 | version "0.13.8" 77 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz#892d093e32a21c0c9135e5a0ffdc380aeb70e763" 78 | integrity sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A== 79 | 80 | esbuild-windows-64@0.13.8: 81 | version "0.13.8" 82 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz#7defd8d79ae3bb7e6f53b65a7190be7daf901686" 83 | integrity sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg== 84 | 85 | esbuild-windows-arm64@0.13.8: 86 | version "0.13.8" 87 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz#e59ae004496fd8a5ab67bfc7945a2e47480d6fb9" 88 | integrity sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg== 89 | 90 | esbuild@^0.13.2: 91 | version "0.13.8" 92 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.8.tgz#bd7cc51b881ab067789f88e17baca74724c1ec4f" 93 | integrity sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw== 94 | optionalDependencies: 95 | esbuild-android-arm64 "0.13.8" 96 | esbuild-darwin-64 "0.13.8" 97 | esbuild-darwin-arm64 "0.13.8" 98 | esbuild-freebsd-64 "0.13.8" 99 | esbuild-freebsd-arm64 "0.13.8" 100 | esbuild-linux-32 "0.13.8" 101 | esbuild-linux-64 "0.13.8" 102 | esbuild-linux-arm "0.13.8" 103 | esbuild-linux-arm64 "0.13.8" 104 | esbuild-linux-mips64le "0.13.8" 105 | esbuild-linux-ppc64le "0.13.8" 106 | esbuild-netbsd-64 "0.13.8" 107 | esbuild-openbsd-64 "0.13.8" 108 | esbuild-sunos-64 "0.13.8" 109 | esbuild-windows-32 "0.13.8" 110 | esbuild-windows-64 "0.13.8" 111 | esbuild-windows-arm64 "0.13.8" 112 | 113 | fsevents@~2.3.2: 114 | version "2.3.2" 115 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 116 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 117 | 118 | function-bind@^1.1.1: 119 | version "1.1.1" 120 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 121 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 122 | 123 | has@^1.0.3: 124 | version "1.0.3" 125 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 126 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 127 | dependencies: 128 | function-bind "^1.1.1" 129 | 130 | is-core-module@^2.2.0: 131 | version "2.8.0" 132 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" 133 | integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== 134 | dependencies: 135 | has "^1.0.3" 136 | 137 | nanoid@^3.1.28: 138 | version "3.1.30" 139 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" 140 | integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== 141 | 142 | path-parse@^1.0.6: 143 | version "1.0.7" 144 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 145 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 146 | 147 | picocolors@^0.2.1: 148 | version "0.2.1" 149 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" 150 | integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== 151 | 152 | postcss@^8.3.8: 153 | version "8.3.9" 154 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31" 155 | integrity sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw== 156 | dependencies: 157 | nanoid "^3.1.28" 158 | picocolors "^0.2.1" 159 | source-map-js "^0.6.2" 160 | 161 | resolve@^1.20.0: 162 | version "1.20.0" 163 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" 164 | integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== 165 | dependencies: 166 | is-core-module "^2.2.0" 167 | path-parse "^1.0.6" 168 | 169 | rollup@^2.57.0: 170 | version "2.58.0" 171 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.58.0.tgz#a643983365e7bf7f5b7c62a8331b983b7c4c67fb" 172 | integrity sha512-NOXpusKnaRpbS7ZVSzcEXqxcLDOagN6iFS8p45RkoiMqPHDLwJm758UF05KlMoCRbLBTZsPOIa887gZJ1AiXvw== 173 | optionalDependencies: 174 | fsevents "~2.3.2" 175 | 176 | source-map-js@^0.6.2: 177 | version "0.6.2" 178 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" 179 | integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== 180 | 181 | typescript@^4.3.2: 182 | version "4.4.4" 183 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" 184 | integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== 185 | 186 | vite@^2.6.4: 187 | version "2.6.10" 188 | resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.10.tgz#7a4f420c6e2c7d9062c7f9ce4578a817c72b3842" 189 | integrity sha512-XbevwpDJMs3lKiGEj0UQScsOCpwHIjFgfzPnFVkPgnxsF9oPv1uGyckLg58XkXv6LnO46KN9yZqJzINFmAxtUg== 190 | dependencies: 191 | esbuild "^0.13.2" 192 | postcss "^8.3.8" 193 | resolve "^1.20.0" 194 | rollup "^2.57.0" 195 | optionalDependencies: 196 | fsevents "~2.3.2" 197 | -------------------------------------------------------------------------------- /JS/4-2FAInput/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /JS/4-2FAInput/README.md: -------------------------------------------------------------------------------- 1 | # Champs pour code d'authentification 2 | 3 | Pour cet exercice, on cherche à créer un champ personnalisé pour permettre aux utilisateurs de rentrer un code à 6 chiffres 4 | 5 | ![Rendu attendu](./screenshot.png) 6 | 7 | ## Énoncé 8 | 9 | Le client exige que plusieurs comportements soient présent pour considérer le composant comme valide (des tests sont disponibles pour confirmer ces comportements) 10 | 11 | - L'interface doit correspondre au plus proche à celle de la capture 12 | - Quand on tape un nombre dans un champ, le champ suivant doit se sélectionner 13 | - On ne peut pas taper un caractère qui ne soit pas un chiffre 14 | - On doit pouvoir coller un nombre 15 | - La touche "Retour" (Backspace) doit permettre de supprimer les chiffre les uns à la suite des autres 16 | - On doit générer un champ caché qui a la valeur qui correspond à ce que voit l'utilisateur. 17 | 18 | Après une discussion avec l'équipe, vous convenez de la piste à adopter, un composant web qui permettra de réutiliser cet élément d'interface à plusieurs endroits. 19 | 20 | ```html 21 | 27 | ``` 28 | 29 | Si l'attribut "value" change, il faut que l'interface reflète ce changement. 30 | 31 | A vous de jouer ! 32 | 33 | ## Environnement 34 | 35 | Après avoir installé les dépendances via `npm install` (ou autre gestionnaire) vous pourrez lancer la page contenant le formulaire à l'aide de la commande : 36 | 37 | ```bash 38 | npm run dev 39 | ``` 40 | 41 | Un serveur local sera lancé et sera accessible sur [localhost:5173](http://localhost:5173). Vous pouvez commencer à travailler sur le fichier `src/source.js`. 42 | 43 | Pour tester que votre code corresponde aux attentes du client, vous pouvez lancer les tests à l'aide de la commande. 44 | 45 | ```bash 46 | npm run test 47 | ``` 48 | 49 | Une fenêtre devrait s'ouvrir vous listant les tests et vous pouvez la laisser ouverte pendant que vous développer pour tester le code automatiquement à chaque changement de code. 50 | 51 | Vous pouvez aussi comparer à la solution attendue en regardant http://localhost:5173/example.html 52 | -------------------------------------------------------------------------------- /JS/4-2FAInput/cypress.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | defaultCommandTimeout: 1000, 5 | component: { 6 | devServer: { 7 | framework: "svelte", 8 | bundler: "vite", 9 | }, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /JS/4-2FAInput/cypress/component/CodeInput.cy.js: -------------------------------------------------------------------------------- 1 | const expectFieldsToHave = (str) => { 2 | cy.get("input").should(($input) => { 3 | const v = Object.values($input) 4 | .filter(input => input instanceof HTMLElement && input.getAttribute('type') !== 'hidden') 5 | .map((input) => input.value) 6 | .join(""); 7 | expect(v).to.eq(str); 8 | }); 9 | }; 10 | 11 | const expectFormValue = (name, str) => { 12 | cy.get("form").should(($form) => { 13 | const data = new FormData($form[0]) 14 | expect(data.get(name)).to.eq(str); 15 | }); 16 | }; 17 | 18 | const defaultAttrs = { 19 | name: 'code', 20 | size: 6, 21 | legend: "Entrer le code à 6 chiffres généré par votre application" 22 | } 23 | 24 | function paste({ destinationSelector, pastePayload, pasteType = 'text' }) { 25 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event 26 | cy.get(destinationSelector).then($destination => { 27 | const pasteEvent = Object.assign(new Event('paste', { bubbles: true, cancelable: true }), { 28 | clipboardData: { 29 | getData: (type = pasteType) => pastePayload, 30 | }, 31 | }); 32 | $destination[0].dispatchEvent(pasteEvent); 33 | }); 34 | } 35 | 36 | describe("Spécification de ", () => { 37 | describe('Base', () => { 38 | beforeEach(() => { 39 | cy.mount(defaultAttrs) 40 | cy.get("input:first").focus(); 41 | }); 42 | it("Permet de taper plusieurs nombres à la suite", () => { 43 | cy.get("body").type("123456"); 44 | expectFieldsToHave("123456"); 45 | }); 46 | it("Respecte l'attribut value", () => { 47 | cy.mount({...defaultAttrs, value: '123456'}) 48 | expectFieldsToHave("123456"); 49 | }) 50 | it("Limite le nombre de caractères", () => { 51 | cy.mount(defaultAttrs) 52 | cy.get("input:first").focus(); 53 | cy.get("body").type("12345678"); 54 | expectFieldsToHave("123458"); 55 | }); 56 | it("Limite les champs à des nombres", () => { 57 | cy.mount(defaultAttrs) 58 | cy.get("input:first").focus(); 59 | cy.get("body").type("12345a"); 60 | expectFieldsToHave("12345"); 61 | }); 62 | it("Limite les champs à des nombres même au milieu", () => { 63 | cy.mount(defaultAttrs) 64 | cy.get("input:first").focus(); 65 | cy.get("body").type("12a345a6"); 66 | expectFieldsToHave("123456"); 67 | }); 68 | }) 69 | describe('Avancé', () => { 70 | it('Permet de retaper un nombre par dessus le premier', () => { 71 | cy.mount({...defaultAttrs, value: '123456'}) 72 | cy.get("input:first").focus(); 73 | cy.get("body").type("234567"); 74 | expectFieldsToHave("234567"); 75 | }) 76 | it("Respecte l'attribut size", () => { 77 | cy.mount({...defaultAttrs, size: 7}) 78 | cy.get("input:first").focus(); 79 | cy.get("body").type("1234567"); 80 | expectFieldsToHave("1234567"); 81 | }) 82 | it("L'attribut value peut être changé depuis après coup", () => { 83 | cy.mount({...defaultAttrs, value: '123456'}) 84 | expectFieldsToHave("123456"); 85 | cy.get('code-input').then(el => { 86 | el[0].setAttribute('value', '234567') 87 | }) 88 | expectFieldsToHave("234567"); 89 | }) 90 | it("Coller un code valide remplit tous les champs", () => { 91 | cy.mount(defaultAttrs) 92 | cy.get("input:first").paste("123456") 93 | expectFieldsToHave("123456"); 94 | }); 95 | it("Coller un code partiel remplit les champs correspondant", () => { 96 | cy.mount(defaultAttrs) 97 | cy.get("input:first").paste("1234") 98 | expectFieldsToHave("1234"); 99 | }); 100 | it("Coller un code avec des lettres ne garde que les nombres", () => { 101 | cy.mount(defaultAttrs) 102 | cy.get("input:first").paste("12a345b6") 103 | expectFieldsToHave("123456"); 104 | }); 105 | it("Coller un code avec des lettres ne garde que les nombres", () => { 106 | cy.mount(defaultAttrs) 107 | cy.get("input:first").focus() 108 | cy.get('body').type("123456"); 109 | expectFieldsToHave("123456"); 110 | expectFormValue("code", "123456"); 111 | }); 112 | }) 113 | }); 114 | -------------------------------------------------------------------------------- /JS/4-2FAInput/cypress/support/component-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Components App 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /JS/4-2FAInput/cypress/support/component.js: -------------------------------------------------------------------------------- 1 | import '../../src/source.js' 2 | 3 | /** 4 | * Commande personnalisée pour monter le custom element pour les tests 5 | */ 6 | Cypress.Commands.add('mount', (props) => { 7 | return cy.then(() => { 8 | const root = document.querySelector('body') 9 | const attrs = Object.keys(props).map(key => `${key}="${props[key]}"`).join(' ') 10 | root.innerHTML = `

Entrez votre code d'authentification

11 | 14 | 15 |
` 16 | }) 17 | }) 18 | 19 | Cypress.Commands.add( 20 | 'paste', 21 | { 22 | prevSubject: true, 23 | }, 24 | (subject, pastePayload) => { 25 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event 26 | const pasteEvent = Object.assign(new Event('paste', {bubbles: true, cancelable: true}), { 27 | clipboardData: { 28 | getData: (type = 'text') => pastePayload, 29 | }, 30 | }); 31 | subject[0].dispatchEvent(pasteEvent); 32 | 33 | return subject; 34 | } 35 | ); 36 | -------------------------------------------------------------------------------- /JS/4-2FAInput/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 40 | 41 | 42 |

Résultat attendu

43 |
44 | 50 | 51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /JS/4-2FAInput/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 | 11 |

Authentification à 2 facteurs

12 |
13 | 19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /JS/4-2FAInput/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "2FACode", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "test": "cypress open --component -b electron" 9 | }, 10 | "devDependencies": { 11 | "cypress": "^13.7.3", 12 | "prettier": "^3.2.5", 13 | "vite": "^5.2.8" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /JS/4-2FAInput/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | cypress: 12 | specifier: ^13.7.3 13 | version: 13.7.3 14 | prettier: 15 | specifier: ^3.2.5 16 | version: 3.2.5 17 | vite: 18 | specifier: ^5.2.8 19 | version: 5.2.8(@types/node@20.12.7) 20 | 21 | packages: 22 | 23 | '@colors/colors@1.5.0': 24 | resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} 25 | engines: {node: '>=0.1.90'} 26 | 27 | '@cypress/request@3.0.1': 28 | resolution: {integrity: sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==} 29 | engines: {node: '>= 6'} 30 | 31 | '@cypress/xvfb@1.2.4': 32 | resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==} 33 | 34 | '@esbuild/aix-ppc64@0.20.2': 35 | resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} 36 | engines: {node: '>=12'} 37 | cpu: [ppc64] 38 | os: [aix] 39 | 40 | '@esbuild/android-arm64@0.20.2': 41 | resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} 42 | engines: {node: '>=12'} 43 | cpu: [arm64] 44 | os: [android] 45 | 46 | '@esbuild/android-arm@0.20.2': 47 | resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} 48 | engines: {node: '>=12'} 49 | cpu: [arm] 50 | os: [android] 51 | 52 | '@esbuild/android-x64@0.20.2': 53 | resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} 54 | engines: {node: '>=12'} 55 | cpu: [x64] 56 | os: [android] 57 | 58 | '@esbuild/darwin-arm64@0.20.2': 59 | resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} 60 | engines: {node: '>=12'} 61 | cpu: [arm64] 62 | os: [darwin] 63 | 64 | '@esbuild/darwin-x64@0.20.2': 65 | resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} 66 | engines: {node: '>=12'} 67 | cpu: [x64] 68 | os: [darwin] 69 | 70 | '@esbuild/freebsd-arm64@0.20.2': 71 | resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} 72 | engines: {node: '>=12'} 73 | cpu: [arm64] 74 | os: [freebsd] 75 | 76 | '@esbuild/freebsd-x64@0.20.2': 77 | resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} 78 | engines: {node: '>=12'} 79 | cpu: [x64] 80 | os: [freebsd] 81 | 82 | '@esbuild/linux-arm64@0.20.2': 83 | resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} 84 | engines: {node: '>=12'} 85 | cpu: [arm64] 86 | os: [linux] 87 | 88 | '@esbuild/linux-arm@0.20.2': 89 | resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} 90 | engines: {node: '>=12'} 91 | cpu: [arm] 92 | os: [linux] 93 | 94 | '@esbuild/linux-ia32@0.20.2': 95 | resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} 96 | engines: {node: '>=12'} 97 | cpu: [ia32] 98 | os: [linux] 99 | 100 | '@esbuild/linux-loong64@0.20.2': 101 | resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} 102 | engines: {node: '>=12'} 103 | cpu: [loong64] 104 | os: [linux] 105 | 106 | '@esbuild/linux-mips64el@0.20.2': 107 | resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} 108 | engines: {node: '>=12'} 109 | cpu: [mips64el] 110 | os: [linux] 111 | 112 | '@esbuild/linux-ppc64@0.20.2': 113 | resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} 114 | engines: {node: '>=12'} 115 | cpu: [ppc64] 116 | os: [linux] 117 | 118 | '@esbuild/linux-riscv64@0.20.2': 119 | resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} 120 | engines: {node: '>=12'} 121 | cpu: [riscv64] 122 | os: [linux] 123 | 124 | '@esbuild/linux-s390x@0.20.2': 125 | resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} 126 | engines: {node: '>=12'} 127 | cpu: [s390x] 128 | os: [linux] 129 | 130 | '@esbuild/linux-x64@0.20.2': 131 | resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} 132 | engines: {node: '>=12'} 133 | cpu: [x64] 134 | os: [linux] 135 | 136 | '@esbuild/netbsd-x64@0.20.2': 137 | resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} 138 | engines: {node: '>=12'} 139 | cpu: [x64] 140 | os: [netbsd] 141 | 142 | '@esbuild/openbsd-x64@0.20.2': 143 | resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} 144 | engines: {node: '>=12'} 145 | cpu: [x64] 146 | os: [openbsd] 147 | 148 | '@esbuild/sunos-x64@0.20.2': 149 | resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} 150 | engines: {node: '>=12'} 151 | cpu: [x64] 152 | os: [sunos] 153 | 154 | '@esbuild/win32-arm64@0.20.2': 155 | resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} 156 | engines: {node: '>=12'} 157 | cpu: [arm64] 158 | os: [win32] 159 | 160 | '@esbuild/win32-ia32@0.20.2': 161 | resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} 162 | engines: {node: '>=12'} 163 | cpu: [ia32] 164 | os: [win32] 165 | 166 | '@esbuild/win32-x64@0.20.2': 167 | resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} 168 | engines: {node: '>=12'} 169 | cpu: [x64] 170 | os: [win32] 171 | 172 | '@rollup/rollup-android-arm-eabi@4.14.2': 173 | resolution: {integrity: sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==} 174 | cpu: [arm] 175 | os: [android] 176 | 177 | '@rollup/rollup-android-arm64@4.14.2': 178 | resolution: {integrity: sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==} 179 | cpu: [arm64] 180 | os: [android] 181 | 182 | '@rollup/rollup-darwin-arm64@4.14.2': 183 | resolution: {integrity: sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==} 184 | cpu: [arm64] 185 | os: [darwin] 186 | 187 | '@rollup/rollup-darwin-x64@4.14.2': 188 | resolution: {integrity: sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==} 189 | cpu: [x64] 190 | os: [darwin] 191 | 192 | '@rollup/rollup-linux-arm-gnueabihf@4.14.2': 193 | resolution: {integrity: sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==} 194 | cpu: [arm] 195 | os: [linux] 196 | 197 | '@rollup/rollup-linux-arm64-gnu@4.14.2': 198 | resolution: {integrity: sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==} 199 | cpu: [arm64] 200 | os: [linux] 201 | 202 | '@rollup/rollup-linux-arm64-musl@4.14.2': 203 | resolution: {integrity: sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==} 204 | cpu: [arm64] 205 | os: [linux] 206 | 207 | '@rollup/rollup-linux-powerpc64le-gnu@4.14.2': 208 | resolution: {integrity: sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==} 209 | cpu: [ppc64] 210 | os: [linux] 211 | 212 | '@rollup/rollup-linux-riscv64-gnu@4.14.2': 213 | resolution: {integrity: sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==} 214 | cpu: [riscv64] 215 | os: [linux] 216 | 217 | '@rollup/rollup-linux-s390x-gnu@4.14.2': 218 | resolution: {integrity: sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==} 219 | cpu: [s390x] 220 | os: [linux] 221 | 222 | '@rollup/rollup-linux-x64-gnu@4.14.2': 223 | resolution: {integrity: sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==} 224 | cpu: [x64] 225 | os: [linux] 226 | 227 | '@rollup/rollup-linux-x64-musl@4.14.2': 228 | resolution: {integrity: sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==} 229 | cpu: [x64] 230 | os: [linux] 231 | 232 | '@rollup/rollup-win32-arm64-msvc@4.14.2': 233 | resolution: {integrity: sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==} 234 | cpu: [arm64] 235 | os: [win32] 236 | 237 | '@rollup/rollup-win32-ia32-msvc@4.14.2': 238 | resolution: {integrity: sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==} 239 | cpu: [ia32] 240 | os: [win32] 241 | 242 | '@rollup/rollup-win32-x64-msvc@4.14.2': 243 | resolution: {integrity: sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==} 244 | cpu: [x64] 245 | os: [win32] 246 | 247 | '@types/estree@1.0.5': 248 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 249 | 250 | '@types/node@20.12.7': 251 | resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==} 252 | 253 | '@types/sinonjs__fake-timers@8.1.1': 254 | resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==} 255 | 256 | '@types/sizzle@2.3.8': 257 | resolution: {integrity: sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==} 258 | 259 | '@types/yauzl@2.10.3': 260 | resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} 261 | 262 | aggregate-error@3.1.0: 263 | resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} 264 | engines: {node: '>=8'} 265 | 266 | ansi-colors@4.1.3: 267 | resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} 268 | engines: {node: '>=6'} 269 | 270 | ansi-escapes@4.3.2: 271 | resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} 272 | engines: {node: '>=8'} 273 | 274 | ansi-regex@5.0.1: 275 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 276 | engines: {node: '>=8'} 277 | 278 | ansi-styles@4.3.0: 279 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 280 | engines: {node: '>=8'} 281 | 282 | arch@2.2.0: 283 | resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} 284 | 285 | asn1@0.2.6: 286 | resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} 287 | 288 | assert-plus@1.0.0: 289 | resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} 290 | engines: {node: '>=0.8'} 291 | 292 | astral-regex@2.0.0: 293 | resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} 294 | engines: {node: '>=8'} 295 | 296 | async@3.2.5: 297 | resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} 298 | 299 | asynckit@0.4.0: 300 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 301 | 302 | at-least-node@1.0.0: 303 | resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} 304 | engines: {node: '>= 4.0.0'} 305 | 306 | aws-sign2@0.7.0: 307 | resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} 308 | 309 | aws4@1.12.0: 310 | resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} 311 | 312 | base64-js@1.5.1: 313 | resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} 314 | 315 | bcrypt-pbkdf@1.0.2: 316 | resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} 317 | 318 | blob-util@2.0.2: 319 | resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} 320 | 321 | bluebird@3.7.2: 322 | resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} 323 | 324 | buffer-crc32@0.2.13: 325 | resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} 326 | 327 | buffer@5.7.1: 328 | resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} 329 | 330 | cachedir@2.4.0: 331 | resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} 332 | engines: {node: '>=6'} 333 | 334 | call-bind@1.0.7: 335 | resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} 336 | engines: {node: '>= 0.4'} 337 | 338 | caseless@0.12.0: 339 | resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} 340 | 341 | chalk@4.1.2: 342 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 343 | engines: {node: '>=10'} 344 | 345 | check-more-types@2.24.0: 346 | resolution: {integrity: sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==} 347 | engines: {node: '>= 0.8.0'} 348 | 349 | ci-info@3.9.0: 350 | resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} 351 | engines: {node: '>=8'} 352 | 353 | clean-stack@2.2.0: 354 | resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} 355 | engines: {node: '>=6'} 356 | 357 | cli-cursor@3.1.0: 358 | resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} 359 | engines: {node: '>=8'} 360 | 361 | cli-table3@0.6.4: 362 | resolution: {integrity: sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==} 363 | engines: {node: 10.* || >= 12.*} 364 | 365 | cli-truncate@2.1.0: 366 | resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} 367 | engines: {node: '>=8'} 368 | 369 | color-convert@2.0.1: 370 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 371 | engines: {node: '>=7.0.0'} 372 | 373 | color-name@1.1.4: 374 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 375 | 376 | colorette@2.0.20: 377 | resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} 378 | 379 | combined-stream@1.0.8: 380 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 381 | engines: {node: '>= 0.8'} 382 | 383 | commander@6.2.1: 384 | resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} 385 | engines: {node: '>= 6'} 386 | 387 | common-tags@1.8.2: 388 | resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} 389 | engines: {node: '>=4.0.0'} 390 | 391 | core-util-is@1.0.2: 392 | resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} 393 | 394 | cross-spawn@7.0.3: 395 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 396 | engines: {node: '>= 8'} 397 | 398 | cypress@13.7.3: 399 | resolution: {integrity: sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==} 400 | engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} 401 | hasBin: true 402 | 403 | dashdash@1.14.1: 404 | resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} 405 | engines: {node: '>=0.10'} 406 | 407 | dayjs@1.11.10: 408 | resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} 409 | 410 | debug@3.2.7: 411 | resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} 412 | peerDependencies: 413 | supports-color: '*' 414 | peerDependenciesMeta: 415 | supports-color: 416 | optional: true 417 | 418 | debug@4.3.4: 419 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 420 | engines: {node: '>=6.0'} 421 | peerDependencies: 422 | supports-color: '*' 423 | peerDependenciesMeta: 424 | supports-color: 425 | optional: true 426 | 427 | define-data-property@1.1.4: 428 | resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 429 | engines: {node: '>= 0.4'} 430 | 431 | delayed-stream@1.0.0: 432 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 433 | engines: {node: '>=0.4.0'} 434 | 435 | ecc-jsbn@0.1.2: 436 | resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} 437 | 438 | emoji-regex@8.0.0: 439 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 440 | 441 | end-of-stream@1.4.4: 442 | resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} 443 | 444 | enquirer@2.4.1: 445 | resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} 446 | engines: {node: '>=8.6'} 447 | 448 | es-define-property@1.0.0: 449 | resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} 450 | engines: {node: '>= 0.4'} 451 | 452 | es-errors@1.3.0: 453 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 454 | engines: {node: '>= 0.4'} 455 | 456 | esbuild@0.20.2: 457 | resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} 458 | engines: {node: '>=12'} 459 | hasBin: true 460 | 461 | escape-string-regexp@1.0.5: 462 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 463 | engines: {node: '>=0.8.0'} 464 | 465 | eventemitter2@6.4.7: 466 | resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==} 467 | 468 | execa@4.1.0: 469 | resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} 470 | engines: {node: '>=10'} 471 | 472 | executable@4.1.1: 473 | resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} 474 | engines: {node: '>=4'} 475 | 476 | extend@3.0.2: 477 | resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} 478 | 479 | extract-zip@2.0.1: 480 | resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} 481 | engines: {node: '>= 10.17.0'} 482 | hasBin: true 483 | 484 | extsprintf@1.3.0: 485 | resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} 486 | engines: {'0': node >=0.6.0} 487 | 488 | fd-slicer@1.1.0: 489 | resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} 490 | 491 | figures@3.2.0: 492 | resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} 493 | engines: {node: '>=8'} 494 | 495 | forever-agent@0.6.1: 496 | resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} 497 | 498 | form-data@2.3.3: 499 | resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} 500 | engines: {node: '>= 0.12'} 501 | 502 | fs-extra@9.1.0: 503 | resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} 504 | engines: {node: '>=10'} 505 | 506 | fsevents@2.3.3: 507 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 508 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 509 | os: [darwin] 510 | 511 | function-bind@1.1.2: 512 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 513 | 514 | get-intrinsic@1.2.4: 515 | resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} 516 | engines: {node: '>= 0.4'} 517 | 518 | get-stream@5.2.0: 519 | resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} 520 | engines: {node: '>=8'} 521 | 522 | getos@3.2.1: 523 | resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==} 524 | 525 | getpass@0.1.7: 526 | resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} 527 | 528 | global-dirs@3.0.1: 529 | resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} 530 | engines: {node: '>=10'} 531 | 532 | gopd@1.0.1: 533 | resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} 534 | 535 | graceful-fs@4.2.11: 536 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 537 | 538 | has-flag@4.0.0: 539 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 540 | engines: {node: '>=8'} 541 | 542 | has-property-descriptors@1.0.2: 543 | resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 544 | 545 | has-proto@1.0.3: 546 | resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} 547 | engines: {node: '>= 0.4'} 548 | 549 | has-symbols@1.0.3: 550 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 551 | engines: {node: '>= 0.4'} 552 | 553 | hasown@2.0.2: 554 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 555 | engines: {node: '>= 0.4'} 556 | 557 | http-signature@1.3.6: 558 | resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==} 559 | engines: {node: '>=0.10'} 560 | 561 | human-signals@1.1.1: 562 | resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} 563 | engines: {node: '>=8.12.0'} 564 | 565 | ieee754@1.2.1: 566 | resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 567 | 568 | indent-string@4.0.0: 569 | resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} 570 | engines: {node: '>=8'} 571 | 572 | ini@2.0.0: 573 | resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} 574 | engines: {node: '>=10'} 575 | 576 | is-ci@3.0.1: 577 | resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} 578 | hasBin: true 579 | 580 | is-fullwidth-code-point@3.0.0: 581 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 582 | engines: {node: '>=8'} 583 | 584 | is-installed-globally@0.4.0: 585 | resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} 586 | engines: {node: '>=10'} 587 | 588 | is-path-inside@3.0.3: 589 | resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} 590 | engines: {node: '>=8'} 591 | 592 | is-stream@2.0.1: 593 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 594 | engines: {node: '>=8'} 595 | 596 | is-typedarray@1.0.0: 597 | resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} 598 | 599 | is-unicode-supported@0.1.0: 600 | resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} 601 | engines: {node: '>=10'} 602 | 603 | isexe@2.0.0: 604 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 605 | 606 | isstream@0.1.2: 607 | resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} 608 | 609 | jsbn@0.1.1: 610 | resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} 611 | 612 | json-schema@0.4.0: 613 | resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} 614 | 615 | json-stringify-safe@5.0.1: 616 | resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} 617 | 618 | jsonfile@6.1.0: 619 | resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} 620 | 621 | jsprim@2.0.2: 622 | resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} 623 | engines: {'0': node >=0.6.0} 624 | 625 | lazy-ass@1.6.0: 626 | resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} 627 | engines: {node: '> 0.8'} 628 | 629 | listr2@3.14.0: 630 | resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==} 631 | engines: {node: '>=10.0.0'} 632 | peerDependencies: 633 | enquirer: '>= 2.3.0 < 3' 634 | peerDependenciesMeta: 635 | enquirer: 636 | optional: true 637 | 638 | lodash.once@4.1.1: 639 | resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} 640 | 641 | lodash@4.17.21: 642 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 643 | 644 | log-symbols@4.1.0: 645 | resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} 646 | engines: {node: '>=10'} 647 | 648 | log-update@4.0.0: 649 | resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} 650 | engines: {node: '>=10'} 651 | 652 | lru-cache@6.0.0: 653 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 654 | engines: {node: '>=10'} 655 | 656 | merge-stream@2.0.0: 657 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 658 | 659 | mime-db@1.52.0: 660 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 661 | engines: {node: '>= 0.6'} 662 | 663 | mime-types@2.1.35: 664 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 665 | engines: {node: '>= 0.6'} 666 | 667 | mimic-fn@2.1.0: 668 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 669 | engines: {node: '>=6'} 670 | 671 | minimist@1.2.8: 672 | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 673 | 674 | ms@2.1.2: 675 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 676 | 677 | ms@2.1.3: 678 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 679 | 680 | nanoid@3.3.7: 681 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 682 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 683 | hasBin: true 684 | 685 | npm-run-path@4.0.1: 686 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} 687 | engines: {node: '>=8'} 688 | 689 | object-inspect@1.13.1: 690 | resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} 691 | 692 | once@1.4.0: 693 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 694 | 695 | onetime@5.1.2: 696 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 697 | engines: {node: '>=6'} 698 | 699 | ospath@1.2.2: 700 | resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} 701 | 702 | p-map@4.0.0: 703 | resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} 704 | engines: {node: '>=10'} 705 | 706 | path-key@3.1.1: 707 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 708 | engines: {node: '>=8'} 709 | 710 | pend@1.2.0: 711 | resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} 712 | 713 | performance-now@2.1.0: 714 | resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} 715 | 716 | picocolors@1.0.0: 717 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 718 | 719 | pify@2.3.0: 720 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 721 | engines: {node: '>=0.10.0'} 722 | 723 | postcss@8.4.38: 724 | resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} 725 | engines: {node: ^10 || ^12 || >=14} 726 | 727 | prettier@3.2.5: 728 | resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} 729 | engines: {node: '>=14'} 730 | hasBin: true 731 | 732 | pretty-bytes@5.6.0: 733 | resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} 734 | engines: {node: '>=6'} 735 | 736 | process@0.11.10: 737 | resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} 738 | engines: {node: '>= 0.6.0'} 739 | 740 | proxy-from-env@1.0.0: 741 | resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==} 742 | 743 | psl@1.9.0: 744 | resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} 745 | 746 | pump@3.0.0: 747 | resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} 748 | 749 | punycode@2.3.1: 750 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 751 | engines: {node: '>=6'} 752 | 753 | qs@6.10.4: 754 | resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==} 755 | engines: {node: '>=0.6'} 756 | 757 | querystringify@2.2.0: 758 | resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} 759 | 760 | request-progress@3.0.0: 761 | resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} 762 | 763 | requires-port@1.0.0: 764 | resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} 765 | 766 | restore-cursor@3.1.0: 767 | resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} 768 | engines: {node: '>=8'} 769 | 770 | rfdc@1.3.1: 771 | resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} 772 | 773 | rollup@4.14.2: 774 | resolution: {integrity: sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==} 775 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 776 | hasBin: true 777 | 778 | rxjs@7.8.1: 779 | resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} 780 | 781 | safe-buffer@5.2.1: 782 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 783 | 784 | safer-buffer@2.1.2: 785 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 786 | 787 | semver@7.6.0: 788 | resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} 789 | engines: {node: '>=10'} 790 | hasBin: true 791 | 792 | set-function-length@1.2.2: 793 | resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 794 | engines: {node: '>= 0.4'} 795 | 796 | shebang-command@2.0.0: 797 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 798 | engines: {node: '>=8'} 799 | 800 | shebang-regex@3.0.0: 801 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 802 | engines: {node: '>=8'} 803 | 804 | side-channel@1.0.6: 805 | resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} 806 | engines: {node: '>= 0.4'} 807 | 808 | signal-exit@3.0.7: 809 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 810 | 811 | slice-ansi@3.0.0: 812 | resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} 813 | engines: {node: '>=8'} 814 | 815 | slice-ansi@4.0.0: 816 | resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} 817 | engines: {node: '>=10'} 818 | 819 | source-map-js@1.2.0: 820 | resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} 821 | engines: {node: '>=0.10.0'} 822 | 823 | sshpk@1.18.0: 824 | resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} 825 | engines: {node: '>=0.10.0'} 826 | hasBin: true 827 | 828 | string-width@4.2.3: 829 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 830 | engines: {node: '>=8'} 831 | 832 | strip-ansi@6.0.1: 833 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 834 | engines: {node: '>=8'} 835 | 836 | strip-final-newline@2.0.0: 837 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} 838 | engines: {node: '>=6'} 839 | 840 | supports-color@7.2.0: 841 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 842 | engines: {node: '>=8'} 843 | 844 | supports-color@8.1.1: 845 | resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} 846 | engines: {node: '>=10'} 847 | 848 | throttleit@1.0.1: 849 | resolution: {integrity: sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==} 850 | 851 | through@2.3.8: 852 | resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} 853 | 854 | tmp@0.2.3: 855 | resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} 856 | engines: {node: '>=14.14'} 857 | 858 | tough-cookie@4.1.3: 859 | resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} 860 | engines: {node: '>=6'} 861 | 862 | tslib@2.6.2: 863 | resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} 864 | 865 | tunnel-agent@0.6.0: 866 | resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} 867 | 868 | tweetnacl@0.14.5: 869 | resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} 870 | 871 | type-fest@0.21.3: 872 | resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} 873 | engines: {node: '>=10'} 874 | 875 | undici-types@5.26.5: 876 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} 877 | 878 | universalify@0.2.0: 879 | resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} 880 | engines: {node: '>= 4.0.0'} 881 | 882 | universalify@2.0.1: 883 | resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} 884 | engines: {node: '>= 10.0.0'} 885 | 886 | untildify@4.0.0: 887 | resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} 888 | engines: {node: '>=8'} 889 | 890 | url-parse@1.5.10: 891 | resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} 892 | 893 | uuid@8.3.2: 894 | resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} 895 | hasBin: true 896 | 897 | verror@1.10.0: 898 | resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=} 899 | engines: {'0': node >=0.6.0} 900 | 901 | vite@5.2.8: 902 | resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} 903 | engines: {node: ^18.0.0 || >=20.0.0} 904 | hasBin: true 905 | peerDependencies: 906 | '@types/node': ^18.0.0 || >=20.0.0 907 | less: '*' 908 | lightningcss: ^1.21.0 909 | sass: '*' 910 | stylus: '*' 911 | sugarss: '*' 912 | terser: ^5.4.0 913 | peerDependenciesMeta: 914 | '@types/node': 915 | optional: true 916 | less: 917 | optional: true 918 | lightningcss: 919 | optional: true 920 | sass: 921 | optional: true 922 | stylus: 923 | optional: true 924 | sugarss: 925 | optional: true 926 | terser: 927 | optional: true 928 | 929 | which@2.0.2: 930 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 931 | engines: {node: '>= 8'} 932 | hasBin: true 933 | 934 | wrap-ansi@6.2.0: 935 | resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} 936 | engines: {node: '>=8'} 937 | 938 | wrap-ansi@7.0.0: 939 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 940 | engines: {node: '>=10'} 941 | 942 | wrappy@1.0.2: 943 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 944 | 945 | yallist@4.0.0: 946 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 947 | 948 | yauzl@2.10.0: 949 | resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} 950 | 951 | snapshots: 952 | 953 | '@colors/colors@1.5.0': 954 | optional: true 955 | 956 | '@cypress/request@3.0.1': 957 | dependencies: 958 | aws-sign2: 0.7.0 959 | aws4: 1.12.0 960 | caseless: 0.12.0 961 | combined-stream: 1.0.8 962 | extend: 3.0.2 963 | forever-agent: 0.6.1 964 | form-data: 2.3.3 965 | http-signature: 1.3.6 966 | is-typedarray: 1.0.0 967 | isstream: 0.1.2 968 | json-stringify-safe: 5.0.1 969 | mime-types: 2.1.35 970 | performance-now: 2.1.0 971 | qs: 6.10.4 972 | safe-buffer: 5.2.1 973 | tough-cookie: 4.1.3 974 | tunnel-agent: 0.6.0 975 | uuid: 8.3.2 976 | 977 | '@cypress/xvfb@1.2.4(supports-color@8.1.1)': 978 | dependencies: 979 | debug: 3.2.7(supports-color@8.1.1) 980 | lodash.once: 4.1.1 981 | transitivePeerDependencies: 982 | - supports-color 983 | 984 | '@esbuild/aix-ppc64@0.20.2': 985 | optional: true 986 | 987 | '@esbuild/android-arm64@0.20.2': 988 | optional: true 989 | 990 | '@esbuild/android-arm@0.20.2': 991 | optional: true 992 | 993 | '@esbuild/android-x64@0.20.2': 994 | optional: true 995 | 996 | '@esbuild/darwin-arm64@0.20.2': 997 | optional: true 998 | 999 | '@esbuild/darwin-x64@0.20.2': 1000 | optional: true 1001 | 1002 | '@esbuild/freebsd-arm64@0.20.2': 1003 | optional: true 1004 | 1005 | '@esbuild/freebsd-x64@0.20.2': 1006 | optional: true 1007 | 1008 | '@esbuild/linux-arm64@0.20.2': 1009 | optional: true 1010 | 1011 | '@esbuild/linux-arm@0.20.2': 1012 | optional: true 1013 | 1014 | '@esbuild/linux-ia32@0.20.2': 1015 | optional: true 1016 | 1017 | '@esbuild/linux-loong64@0.20.2': 1018 | optional: true 1019 | 1020 | '@esbuild/linux-mips64el@0.20.2': 1021 | optional: true 1022 | 1023 | '@esbuild/linux-ppc64@0.20.2': 1024 | optional: true 1025 | 1026 | '@esbuild/linux-riscv64@0.20.2': 1027 | optional: true 1028 | 1029 | '@esbuild/linux-s390x@0.20.2': 1030 | optional: true 1031 | 1032 | '@esbuild/linux-x64@0.20.2': 1033 | optional: true 1034 | 1035 | '@esbuild/netbsd-x64@0.20.2': 1036 | optional: true 1037 | 1038 | '@esbuild/openbsd-x64@0.20.2': 1039 | optional: true 1040 | 1041 | '@esbuild/sunos-x64@0.20.2': 1042 | optional: true 1043 | 1044 | '@esbuild/win32-arm64@0.20.2': 1045 | optional: true 1046 | 1047 | '@esbuild/win32-ia32@0.20.2': 1048 | optional: true 1049 | 1050 | '@esbuild/win32-x64@0.20.2': 1051 | optional: true 1052 | 1053 | '@rollup/rollup-android-arm-eabi@4.14.2': 1054 | optional: true 1055 | 1056 | '@rollup/rollup-android-arm64@4.14.2': 1057 | optional: true 1058 | 1059 | '@rollup/rollup-darwin-arm64@4.14.2': 1060 | optional: true 1061 | 1062 | '@rollup/rollup-darwin-x64@4.14.2': 1063 | optional: true 1064 | 1065 | '@rollup/rollup-linux-arm-gnueabihf@4.14.2': 1066 | optional: true 1067 | 1068 | '@rollup/rollup-linux-arm64-gnu@4.14.2': 1069 | optional: true 1070 | 1071 | '@rollup/rollup-linux-arm64-musl@4.14.2': 1072 | optional: true 1073 | 1074 | '@rollup/rollup-linux-powerpc64le-gnu@4.14.2': 1075 | optional: true 1076 | 1077 | '@rollup/rollup-linux-riscv64-gnu@4.14.2': 1078 | optional: true 1079 | 1080 | '@rollup/rollup-linux-s390x-gnu@4.14.2': 1081 | optional: true 1082 | 1083 | '@rollup/rollup-linux-x64-gnu@4.14.2': 1084 | optional: true 1085 | 1086 | '@rollup/rollup-linux-x64-musl@4.14.2': 1087 | optional: true 1088 | 1089 | '@rollup/rollup-win32-arm64-msvc@4.14.2': 1090 | optional: true 1091 | 1092 | '@rollup/rollup-win32-ia32-msvc@4.14.2': 1093 | optional: true 1094 | 1095 | '@rollup/rollup-win32-x64-msvc@4.14.2': 1096 | optional: true 1097 | 1098 | '@types/estree@1.0.5': {} 1099 | 1100 | '@types/node@20.12.7': 1101 | dependencies: 1102 | undici-types: 5.26.5 1103 | optional: true 1104 | 1105 | '@types/sinonjs__fake-timers@8.1.1': {} 1106 | 1107 | '@types/sizzle@2.3.8': {} 1108 | 1109 | '@types/yauzl@2.10.3': 1110 | dependencies: 1111 | '@types/node': 20.12.7 1112 | optional: true 1113 | 1114 | aggregate-error@3.1.0: 1115 | dependencies: 1116 | clean-stack: 2.2.0 1117 | indent-string: 4.0.0 1118 | 1119 | ansi-colors@4.1.3: {} 1120 | 1121 | ansi-escapes@4.3.2: 1122 | dependencies: 1123 | type-fest: 0.21.3 1124 | 1125 | ansi-regex@5.0.1: {} 1126 | 1127 | ansi-styles@4.3.0: 1128 | dependencies: 1129 | color-convert: 2.0.1 1130 | 1131 | arch@2.2.0: {} 1132 | 1133 | asn1@0.2.6: 1134 | dependencies: 1135 | safer-buffer: 2.1.2 1136 | 1137 | assert-plus@1.0.0: {} 1138 | 1139 | astral-regex@2.0.0: {} 1140 | 1141 | async@3.2.5: {} 1142 | 1143 | asynckit@0.4.0: {} 1144 | 1145 | at-least-node@1.0.0: {} 1146 | 1147 | aws-sign2@0.7.0: {} 1148 | 1149 | aws4@1.12.0: {} 1150 | 1151 | base64-js@1.5.1: {} 1152 | 1153 | bcrypt-pbkdf@1.0.2: 1154 | dependencies: 1155 | tweetnacl: 0.14.5 1156 | 1157 | blob-util@2.0.2: {} 1158 | 1159 | bluebird@3.7.2: {} 1160 | 1161 | buffer-crc32@0.2.13: {} 1162 | 1163 | buffer@5.7.1: 1164 | dependencies: 1165 | base64-js: 1.5.1 1166 | ieee754: 1.2.1 1167 | 1168 | cachedir@2.4.0: {} 1169 | 1170 | call-bind@1.0.7: 1171 | dependencies: 1172 | es-define-property: 1.0.0 1173 | es-errors: 1.3.0 1174 | function-bind: 1.1.2 1175 | get-intrinsic: 1.2.4 1176 | set-function-length: 1.2.2 1177 | 1178 | caseless@0.12.0: {} 1179 | 1180 | chalk@4.1.2: 1181 | dependencies: 1182 | ansi-styles: 4.3.0 1183 | supports-color: 7.2.0 1184 | 1185 | check-more-types@2.24.0: {} 1186 | 1187 | ci-info@3.9.0: {} 1188 | 1189 | clean-stack@2.2.0: {} 1190 | 1191 | cli-cursor@3.1.0: 1192 | dependencies: 1193 | restore-cursor: 3.1.0 1194 | 1195 | cli-table3@0.6.4: 1196 | dependencies: 1197 | string-width: 4.2.3 1198 | optionalDependencies: 1199 | '@colors/colors': 1.5.0 1200 | 1201 | cli-truncate@2.1.0: 1202 | dependencies: 1203 | slice-ansi: 3.0.0 1204 | string-width: 4.2.3 1205 | 1206 | color-convert@2.0.1: 1207 | dependencies: 1208 | color-name: 1.1.4 1209 | 1210 | color-name@1.1.4: {} 1211 | 1212 | colorette@2.0.20: {} 1213 | 1214 | combined-stream@1.0.8: 1215 | dependencies: 1216 | delayed-stream: 1.0.0 1217 | 1218 | commander@6.2.1: {} 1219 | 1220 | common-tags@1.8.2: {} 1221 | 1222 | core-util-is@1.0.2: {} 1223 | 1224 | cross-spawn@7.0.3: 1225 | dependencies: 1226 | path-key: 3.1.1 1227 | shebang-command: 2.0.0 1228 | which: 2.0.2 1229 | 1230 | cypress@13.7.3: 1231 | dependencies: 1232 | '@cypress/request': 3.0.1 1233 | '@cypress/xvfb': 1.2.4(supports-color@8.1.1) 1234 | '@types/sinonjs__fake-timers': 8.1.1 1235 | '@types/sizzle': 2.3.8 1236 | arch: 2.2.0 1237 | blob-util: 2.0.2 1238 | bluebird: 3.7.2 1239 | buffer: 5.7.1 1240 | cachedir: 2.4.0 1241 | chalk: 4.1.2 1242 | check-more-types: 2.24.0 1243 | cli-cursor: 3.1.0 1244 | cli-table3: 0.6.4 1245 | commander: 6.2.1 1246 | common-tags: 1.8.2 1247 | dayjs: 1.11.10 1248 | debug: 4.3.4(supports-color@8.1.1) 1249 | enquirer: 2.4.1 1250 | eventemitter2: 6.4.7 1251 | execa: 4.1.0 1252 | executable: 4.1.1 1253 | extract-zip: 2.0.1(supports-color@8.1.1) 1254 | figures: 3.2.0 1255 | fs-extra: 9.1.0 1256 | getos: 3.2.1 1257 | is-ci: 3.0.1 1258 | is-installed-globally: 0.4.0 1259 | lazy-ass: 1.6.0 1260 | listr2: 3.14.0(enquirer@2.4.1) 1261 | lodash: 4.17.21 1262 | log-symbols: 4.1.0 1263 | minimist: 1.2.8 1264 | ospath: 1.2.2 1265 | pretty-bytes: 5.6.0 1266 | process: 0.11.10 1267 | proxy-from-env: 1.0.0 1268 | request-progress: 3.0.0 1269 | semver: 7.6.0 1270 | supports-color: 8.1.1 1271 | tmp: 0.2.3 1272 | untildify: 4.0.0 1273 | yauzl: 2.10.0 1274 | 1275 | dashdash@1.14.1: 1276 | dependencies: 1277 | assert-plus: 1.0.0 1278 | 1279 | dayjs@1.11.10: {} 1280 | 1281 | debug@3.2.7(supports-color@8.1.1): 1282 | dependencies: 1283 | ms: 2.1.3 1284 | optionalDependencies: 1285 | supports-color: 8.1.1 1286 | 1287 | debug@4.3.4(supports-color@8.1.1): 1288 | dependencies: 1289 | ms: 2.1.2 1290 | optionalDependencies: 1291 | supports-color: 8.1.1 1292 | 1293 | define-data-property@1.1.4: 1294 | dependencies: 1295 | es-define-property: 1.0.0 1296 | es-errors: 1.3.0 1297 | gopd: 1.0.1 1298 | 1299 | delayed-stream@1.0.0: {} 1300 | 1301 | ecc-jsbn@0.1.2: 1302 | dependencies: 1303 | jsbn: 0.1.1 1304 | safer-buffer: 2.1.2 1305 | 1306 | emoji-regex@8.0.0: {} 1307 | 1308 | end-of-stream@1.4.4: 1309 | dependencies: 1310 | once: 1.4.0 1311 | 1312 | enquirer@2.4.1: 1313 | dependencies: 1314 | ansi-colors: 4.1.3 1315 | strip-ansi: 6.0.1 1316 | 1317 | es-define-property@1.0.0: 1318 | dependencies: 1319 | get-intrinsic: 1.2.4 1320 | 1321 | es-errors@1.3.0: {} 1322 | 1323 | esbuild@0.20.2: 1324 | optionalDependencies: 1325 | '@esbuild/aix-ppc64': 0.20.2 1326 | '@esbuild/android-arm': 0.20.2 1327 | '@esbuild/android-arm64': 0.20.2 1328 | '@esbuild/android-x64': 0.20.2 1329 | '@esbuild/darwin-arm64': 0.20.2 1330 | '@esbuild/darwin-x64': 0.20.2 1331 | '@esbuild/freebsd-arm64': 0.20.2 1332 | '@esbuild/freebsd-x64': 0.20.2 1333 | '@esbuild/linux-arm': 0.20.2 1334 | '@esbuild/linux-arm64': 0.20.2 1335 | '@esbuild/linux-ia32': 0.20.2 1336 | '@esbuild/linux-loong64': 0.20.2 1337 | '@esbuild/linux-mips64el': 0.20.2 1338 | '@esbuild/linux-ppc64': 0.20.2 1339 | '@esbuild/linux-riscv64': 0.20.2 1340 | '@esbuild/linux-s390x': 0.20.2 1341 | '@esbuild/linux-x64': 0.20.2 1342 | '@esbuild/netbsd-x64': 0.20.2 1343 | '@esbuild/openbsd-x64': 0.20.2 1344 | '@esbuild/sunos-x64': 0.20.2 1345 | '@esbuild/win32-arm64': 0.20.2 1346 | '@esbuild/win32-ia32': 0.20.2 1347 | '@esbuild/win32-x64': 0.20.2 1348 | 1349 | escape-string-regexp@1.0.5: {} 1350 | 1351 | eventemitter2@6.4.7: {} 1352 | 1353 | execa@4.1.0: 1354 | dependencies: 1355 | cross-spawn: 7.0.3 1356 | get-stream: 5.2.0 1357 | human-signals: 1.1.1 1358 | is-stream: 2.0.1 1359 | merge-stream: 2.0.0 1360 | npm-run-path: 4.0.1 1361 | onetime: 5.1.2 1362 | signal-exit: 3.0.7 1363 | strip-final-newline: 2.0.0 1364 | 1365 | executable@4.1.1: 1366 | dependencies: 1367 | pify: 2.3.0 1368 | 1369 | extend@3.0.2: {} 1370 | 1371 | extract-zip@2.0.1(supports-color@8.1.1): 1372 | dependencies: 1373 | debug: 4.3.4(supports-color@8.1.1) 1374 | get-stream: 5.2.0 1375 | yauzl: 2.10.0 1376 | optionalDependencies: 1377 | '@types/yauzl': 2.10.3 1378 | transitivePeerDependencies: 1379 | - supports-color 1380 | 1381 | extsprintf@1.3.0: {} 1382 | 1383 | fd-slicer@1.1.0: 1384 | dependencies: 1385 | pend: 1.2.0 1386 | 1387 | figures@3.2.0: 1388 | dependencies: 1389 | escape-string-regexp: 1.0.5 1390 | 1391 | forever-agent@0.6.1: {} 1392 | 1393 | form-data@2.3.3: 1394 | dependencies: 1395 | asynckit: 0.4.0 1396 | combined-stream: 1.0.8 1397 | mime-types: 2.1.35 1398 | 1399 | fs-extra@9.1.0: 1400 | dependencies: 1401 | at-least-node: 1.0.0 1402 | graceful-fs: 4.2.11 1403 | jsonfile: 6.1.0 1404 | universalify: 2.0.1 1405 | 1406 | fsevents@2.3.3: 1407 | optional: true 1408 | 1409 | function-bind@1.1.2: {} 1410 | 1411 | get-intrinsic@1.2.4: 1412 | dependencies: 1413 | es-errors: 1.3.0 1414 | function-bind: 1.1.2 1415 | has-proto: 1.0.3 1416 | has-symbols: 1.0.3 1417 | hasown: 2.0.2 1418 | 1419 | get-stream@5.2.0: 1420 | dependencies: 1421 | pump: 3.0.0 1422 | 1423 | getos@3.2.1: 1424 | dependencies: 1425 | async: 3.2.5 1426 | 1427 | getpass@0.1.7: 1428 | dependencies: 1429 | assert-plus: 1.0.0 1430 | 1431 | global-dirs@3.0.1: 1432 | dependencies: 1433 | ini: 2.0.0 1434 | 1435 | gopd@1.0.1: 1436 | dependencies: 1437 | get-intrinsic: 1.2.4 1438 | 1439 | graceful-fs@4.2.11: {} 1440 | 1441 | has-flag@4.0.0: {} 1442 | 1443 | has-property-descriptors@1.0.2: 1444 | dependencies: 1445 | es-define-property: 1.0.0 1446 | 1447 | has-proto@1.0.3: {} 1448 | 1449 | has-symbols@1.0.3: {} 1450 | 1451 | hasown@2.0.2: 1452 | dependencies: 1453 | function-bind: 1.1.2 1454 | 1455 | http-signature@1.3.6: 1456 | dependencies: 1457 | assert-plus: 1.0.0 1458 | jsprim: 2.0.2 1459 | sshpk: 1.18.0 1460 | 1461 | human-signals@1.1.1: {} 1462 | 1463 | ieee754@1.2.1: {} 1464 | 1465 | indent-string@4.0.0: {} 1466 | 1467 | ini@2.0.0: {} 1468 | 1469 | is-ci@3.0.1: 1470 | dependencies: 1471 | ci-info: 3.9.0 1472 | 1473 | is-fullwidth-code-point@3.0.0: {} 1474 | 1475 | is-installed-globally@0.4.0: 1476 | dependencies: 1477 | global-dirs: 3.0.1 1478 | is-path-inside: 3.0.3 1479 | 1480 | is-path-inside@3.0.3: {} 1481 | 1482 | is-stream@2.0.1: {} 1483 | 1484 | is-typedarray@1.0.0: {} 1485 | 1486 | is-unicode-supported@0.1.0: {} 1487 | 1488 | isexe@2.0.0: {} 1489 | 1490 | isstream@0.1.2: {} 1491 | 1492 | jsbn@0.1.1: {} 1493 | 1494 | json-schema@0.4.0: {} 1495 | 1496 | json-stringify-safe@5.0.1: {} 1497 | 1498 | jsonfile@6.1.0: 1499 | dependencies: 1500 | universalify: 2.0.1 1501 | optionalDependencies: 1502 | graceful-fs: 4.2.11 1503 | 1504 | jsprim@2.0.2: 1505 | dependencies: 1506 | assert-plus: 1.0.0 1507 | extsprintf: 1.3.0 1508 | json-schema: 0.4.0 1509 | verror: 1.10.0 1510 | 1511 | lazy-ass@1.6.0: {} 1512 | 1513 | listr2@3.14.0(enquirer@2.4.1): 1514 | dependencies: 1515 | cli-truncate: 2.1.0 1516 | colorette: 2.0.20 1517 | log-update: 4.0.0 1518 | p-map: 4.0.0 1519 | rfdc: 1.3.1 1520 | rxjs: 7.8.1 1521 | through: 2.3.8 1522 | wrap-ansi: 7.0.0 1523 | optionalDependencies: 1524 | enquirer: 2.4.1 1525 | 1526 | lodash.once@4.1.1: {} 1527 | 1528 | lodash@4.17.21: {} 1529 | 1530 | log-symbols@4.1.0: 1531 | dependencies: 1532 | chalk: 4.1.2 1533 | is-unicode-supported: 0.1.0 1534 | 1535 | log-update@4.0.0: 1536 | dependencies: 1537 | ansi-escapes: 4.3.2 1538 | cli-cursor: 3.1.0 1539 | slice-ansi: 4.0.0 1540 | wrap-ansi: 6.2.0 1541 | 1542 | lru-cache@6.0.0: 1543 | dependencies: 1544 | yallist: 4.0.0 1545 | 1546 | merge-stream@2.0.0: {} 1547 | 1548 | mime-db@1.52.0: {} 1549 | 1550 | mime-types@2.1.35: 1551 | dependencies: 1552 | mime-db: 1.52.0 1553 | 1554 | mimic-fn@2.1.0: {} 1555 | 1556 | minimist@1.2.8: {} 1557 | 1558 | ms@2.1.2: {} 1559 | 1560 | ms@2.1.3: {} 1561 | 1562 | nanoid@3.3.7: {} 1563 | 1564 | npm-run-path@4.0.1: 1565 | dependencies: 1566 | path-key: 3.1.1 1567 | 1568 | object-inspect@1.13.1: {} 1569 | 1570 | once@1.4.0: 1571 | dependencies: 1572 | wrappy: 1.0.2 1573 | 1574 | onetime@5.1.2: 1575 | dependencies: 1576 | mimic-fn: 2.1.0 1577 | 1578 | ospath@1.2.2: {} 1579 | 1580 | p-map@4.0.0: 1581 | dependencies: 1582 | aggregate-error: 3.1.0 1583 | 1584 | path-key@3.1.1: {} 1585 | 1586 | pend@1.2.0: {} 1587 | 1588 | performance-now@2.1.0: {} 1589 | 1590 | picocolors@1.0.0: {} 1591 | 1592 | pify@2.3.0: {} 1593 | 1594 | postcss@8.4.38: 1595 | dependencies: 1596 | nanoid: 3.3.7 1597 | picocolors: 1.0.0 1598 | source-map-js: 1.2.0 1599 | 1600 | prettier@3.2.5: {} 1601 | 1602 | pretty-bytes@5.6.0: {} 1603 | 1604 | process@0.11.10: {} 1605 | 1606 | proxy-from-env@1.0.0: {} 1607 | 1608 | psl@1.9.0: {} 1609 | 1610 | pump@3.0.0: 1611 | dependencies: 1612 | end-of-stream: 1.4.4 1613 | once: 1.4.0 1614 | 1615 | punycode@2.3.1: {} 1616 | 1617 | qs@6.10.4: 1618 | dependencies: 1619 | side-channel: 1.0.6 1620 | 1621 | querystringify@2.2.0: {} 1622 | 1623 | request-progress@3.0.0: 1624 | dependencies: 1625 | throttleit: 1.0.1 1626 | 1627 | requires-port@1.0.0: {} 1628 | 1629 | restore-cursor@3.1.0: 1630 | dependencies: 1631 | onetime: 5.1.2 1632 | signal-exit: 3.0.7 1633 | 1634 | rfdc@1.3.1: {} 1635 | 1636 | rollup@4.14.2: 1637 | dependencies: 1638 | '@types/estree': 1.0.5 1639 | optionalDependencies: 1640 | '@rollup/rollup-android-arm-eabi': 4.14.2 1641 | '@rollup/rollup-android-arm64': 4.14.2 1642 | '@rollup/rollup-darwin-arm64': 4.14.2 1643 | '@rollup/rollup-darwin-x64': 4.14.2 1644 | '@rollup/rollup-linux-arm-gnueabihf': 4.14.2 1645 | '@rollup/rollup-linux-arm64-gnu': 4.14.2 1646 | '@rollup/rollup-linux-arm64-musl': 4.14.2 1647 | '@rollup/rollup-linux-powerpc64le-gnu': 4.14.2 1648 | '@rollup/rollup-linux-riscv64-gnu': 4.14.2 1649 | '@rollup/rollup-linux-s390x-gnu': 4.14.2 1650 | '@rollup/rollup-linux-x64-gnu': 4.14.2 1651 | '@rollup/rollup-linux-x64-musl': 4.14.2 1652 | '@rollup/rollup-win32-arm64-msvc': 4.14.2 1653 | '@rollup/rollup-win32-ia32-msvc': 4.14.2 1654 | '@rollup/rollup-win32-x64-msvc': 4.14.2 1655 | fsevents: 2.3.3 1656 | 1657 | rxjs@7.8.1: 1658 | dependencies: 1659 | tslib: 2.6.2 1660 | 1661 | safe-buffer@5.2.1: {} 1662 | 1663 | safer-buffer@2.1.2: {} 1664 | 1665 | semver@7.6.0: 1666 | dependencies: 1667 | lru-cache: 6.0.0 1668 | 1669 | set-function-length@1.2.2: 1670 | dependencies: 1671 | define-data-property: 1.1.4 1672 | es-errors: 1.3.0 1673 | function-bind: 1.1.2 1674 | get-intrinsic: 1.2.4 1675 | gopd: 1.0.1 1676 | has-property-descriptors: 1.0.2 1677 | 1678 | shebang-command@2.0.0: 1679 | dependencies: 1680 | shebang-regex: 3.0.0 1681 | 1682 | shebang-regex@3.0.0: {} 1683 | 1684 | side-channel@1.0.6: 1685 | dependencies: 1686 | call-bind: 1.0.7 1687 | es-errors: 1.3.0 1688 | get-intrinsic: 1.2.4 1689 | object-inspect: 1.13.1 1690 | 1691 | signal-exit@3.0.7: {} 1692 | 1693 | slice-ansi@3.0.0: 1694 | dependencies: 1695 | ansi-styles: 4.3.0 1696 | astral-regex: 2.0.0 1697 | is-fullwidth-code-point: 3.0.0 1698 | 1699 | slice-ansi@4.0.0: 1700 | dependencies: 1701 | ansi-styles: 4.3.0 1702 | astral-regex: 2.0.0 1703 | is-fullwidth-code-point: 3.0.0 1704 | 1705 | source-map-js@1.2.0: {} 1706 | 1707 | sshpk@1.18.0: 1708 | dependencies: 1709 | asn1: 0.2.6 1710 | assert-plus: 1.0.0 1711 | bcrypt-pbkdf: 1.0.2 1712 | dashdash: 1.14.1 1713 | ecc-jsbn: 0.1.2 1714 | getpass: 0.1.7 1715 | jsbn: 0.1.1 1716 | safer-buffer: 2.1.2 1717 | tweetnacl: 0.14.5 1718 | 1719 | string-width@4.2.3: 1720 | dependencies: 1721 | emoji-regex: 8.0.0 1722 | is-fullwidth-code-point: 3.0.0 1723 | strip-ansi: 6.0.1 1724 | 1725 | strip-ansi@6.0.1: 1726 | dependencies: 1727 | ansi-regex: 5.0.1 1728 | 1729 | strip-final-newline@2.0.0: {} 1730 | 1731 | supports-color@7.2.0: 1732 | dependencies: 1733 | has-flag: 4.0.0 1734 | 1735 | supports-color@8.1.1: 1736 | dependencies: 1737 | has-flag: 4.0.0 1738 | 1739 | throttleit@1.0.1: {} 1740 | 1741 | through@2.3.8: {} 1742 | 1743 | tmp@0.2.3: {} 1744 | 1745 | tough-cookie@4.1.3: 1746 | dependencies: 1747 | psl: 1.9.0 1748 | punycode: 2.3.1 1749 | universalify: 0.2.0 1750 | url-parse: 1.5.10 1751 | 1752 | tslib@2.6.2: {} 1753 | 1754 | tunnel-agent@0.6.0: 1755 | dependencies: 1756 | safe-buffer: 5.2.1 1757 | 1758 | tweetnacl@0.14.5: {} 1759 | 1760 | type-fest@0.21.3: {} 1761 | 1762 | undici-types@5.26.5: 1763 | optional: true 1764 | 1765 | universalify@0.2.0: {} 1766 | 1767 | universalify@2.0.1: {} 1768 | 1769 | untildify@4.0.0: {} 1770 | 1771 | url-parse@1.5.10: 1772 | dependencies: 1773 | querystringify: 2.2.0 1774 | requires-port: 1.0.0 1775 | 1776 | uuid@8.3.2: {} 1777 | 1778 | verror@1.10.0: 1779 | dependencies: 1780 | assert-plus: 1.0.0 1781 | core-util-is: 1.0.2 1782 | extsprintf: 1.3.0 1783 | 1784 | vite@5.2.8(@types/node@20.12.7): 1785 | dependencies: 1786 | esbuild: 0.20.2 1787 | postcss: 8.4.38 1788 | rollup: 4.14.2 1789 | optionalDependencies: 1790 | '@types/node': 20.12.7 1791 | fsevents: 2.3.3 1792 | 1793 | which@2.0.2: 1794 | dependencies: 1795 | isexe: 2.0.0 1796 | 1797 | wrap-ansi@6.2.0: 1798 | dependencies: 1799 | ansi-styles: 4.3.0 1800 | string-width: 4.2.3 1801 | strip-ansi: 6.0.1 1802 | 1803 | wrap-ansi@7.0.0: 1804 | dependencies: 1805 | ansi-styles: 4.3.0 1806 | string-width: 4.2.3 1807 | strip-ansi: 6.0.1 1808 | 1809 | wrappy@1.0.2: {} 1810 | 1811 | yallist@4.0.0: {} 1812 | 1813 | yauzl@2.10.0: 1814 | dependencies: 1815 | buffer-crc32: 0.2.13 1816 | fd-slicer: 1.1.0 1817 | -------------------------------------------------------------------------------- /JS/4-2FAInput/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/4-2FAInput/screenshot.png -------------------------------------------------------------------------------- /JS/4-2FAInput/src/example.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Elément web personnalisé 3 | * Plus d'info : https://grafikart.fr/tutoriels/web-component-1201 4 | */ 5 | class CodeInput extends HTMLElement { 6 | 7 | /** @var {HTMLInputElement[]} */ 8 | #inputs = [] 9 | /** @var {HTMLInputElement | null} */ 10 | #hiddenInput = null 11 | 12 | static get observedAttributes() { 13 | return ['value']; 14 | } 15 | 16 | connectedCallback() { 17 | const legend = this.getAttribute('legend') ?? 'Entrez votre code' 18 | const name = this.getAttribute('name') ?? '' 19 | const size = parseInt(this.getAttribute('size') ?? '6', 10) 20 | const value = this.getAttribute('value') ?? '' 21 | this.innerHTML = ` 22 |
23 | ${legend} 24 |
25 | ${Array.from({length: size}, (_, k) => ``).join('')} 33 |
34 | 35 |
` 36 | this.#hiddenInput = this.querySelector('input[type="hidden"]') 37 | this.#inputs = Array.from(this.querySelectorAll('input[type="text"]')) 38 | this.#inputs.forEach(input => { 39 | input.addEventListener('paste', this.#onPaste.bind(this)) 40 | input.addEventListener('input', this.#onInput.bind(this)) 41 | input.addEventListener('keydown', this.#onKeyDown.bind(this)) 42 | }) 43 | } 44 | 45 | /** 46 | * Code exécuté lorsqu'un attribut change 47 | * 48 | * @param name Nom de l'attribut 49 | * @param oldValue Ancienne valeur 50 | * @param newValue Nouvelle valeur 51 | */ 52 | attributeChangedCallback(name, oldValue, newValue) { 53 | if (name === 'value') { 54 | this.value = newValue 55 | } 56 | } 57 | 58 | /** 59 | * @param {string | null} str 60 | */ 61 | set value (str) { 62 | if (!this.#inputs || this.#inputs.length <= 0) { 63 | return; 64 | } 65 | const value = str ?? '' 66 | this.#inputs.forEach((input, k) => { 67 | input.value = value[k] ?? '' 68 | }) 69 | this.#updateHiddenInput() 70 | } 71 | 72 | /** 73 | * @param {InputEvent} e 74 | */ 75 | #onInput (e) { 76 | e.currentTarget.value = e.currentTarget.value.replaceAll(/\D/g, '').slice(0, 1) 77 | this.#updateHiddenInput() 78 | } 79 | 80 | /** 81 | * @param {KeyboardEvent} e 82 | */ 83 | #onKeyDown (e) { 84 | if (e.key.match(/\d/)) { 85 | e.preventDefault() 86 | e.currentTarget.value = e.key 87 | const nextInput = e.currentTarget.nextElementSibling 88 | if (nextInput) { 89 | nextInput.focus() 90 | } 91 | this.#updateHiddenInput() 92 | } 93 | if (e.key === 'Backspace' && e.currentTarget.value === '') { 94 | const previousInput = e.currentTarget.previousElementSibling 95 | if (!previousInput) { 96 | return; 97 | } 98 | previousInput.value = '' 99 | previousInput.focus() 100 | this.#updateHiddenInput() 101 | } 102 | } 103 | 104 | #updateHiddenInput() { 105 | this.#hiddenInput.value = this.#inputs.map(input => input.value).join('') 106 | } 107 | 108 | /** 109 | * @param {ClipboardEvent} e 110 | */ 111 | #onPaste(e) { 112 | e.preventDefault() 113 | const index = this.#inputs.findIndex(input => input === e.currentTarget) 114 | const text = e.clipboardData.getData('text').replaceAll(/\D/g, '') 115 | if (text.length === 0) { 116 | return; 117 | } 118 | let lastInput 119 | this.#inputs.slice(index).forEach((input, k) => { 120 | if (!text[k]) { 121 | return; 122 | } 123 | input.value = text[k] 124 | lastInput = input 125 | }) 126 | const nextAfterLastInput = lastInput.nextElementSibling 127 | if (nextAfterLastInput) { 128 | nextAfterLastInput.focus() 129 | } else { 130 | lastInput.focus() 131 | } 132 | this.#updateHiddenInput() 133 | } 134 | 135 | } 136 | 137 | customElements.define("code-input", CodeInput); 138 | -------------------------------------------------------------------------------- /JS/4-2FAInput/src/source.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Elément web personnalisé 3 | * Plus d'info : https://grafikart.fr/tutoriels/web-component-1201 4 | */ 5 | class CodeInput extends HTMLElement { 6 | 7 | static get observedAttributes() { 8 | return ['value']; 9 | } 10 | 11 | connectedCallback() { 12 | // Ce code sera éxécuté lorsque le composant entre dans le DOM 13 | // COMMENCEZ ICI 14 | } 15 | 16 | /** 17 | * Code exécuté lorsqu'un attribut change 18 | * 19 | * @param name Nom de l'attribut 20 | * @param oldValue Ancienne valeur 21 | * @param newValue Nouvelle valeur 22 | */ 23 | attributeChangedCallback(name, oldValue, newValue) { 24 | } 25 | 26 | } 27 | 28 | customElements.define("code-input", CodeInput); 29 | -------------------------------------------------------------------------------- /JS/4-2FAInput/style.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | /*** 3 | The new CSS reset - version 1.8.5 (last updated 14.6.2023) 4 | GitHub page: https://github.com/elad2412/the-new-css-reset 5 | ***/ 6 | 7 | /* 8 | Remove all the styles of the "User-Agent-Stylesheet", except for the 'display' property 9 | - The "symbol *" part is to solve Firefox SVG sprite bug 10 | - The "html" attribute is exclud, because otherwise a bug in Chrome breaks the CSS hyphens property (https://github.com/elad2412/the-new-css-reset/issues/36) 11 | */ 12 | *:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) { 13 | all: unset; 14 | display: revert; 15 | } 16 | 17 | /* Preferred box-sizing value */ 18 | *, 19 | *::before, 20 | *::after { 21 | box-sizing: border-box; 22 | } 23 | 24 | /* Reapply the pointer cursor for anchor tags */ 25 | a, button { 26 | cursor: revert; 27 | } 28 | 29 | /* Remove list styles (bullets/numbers) */ 30 | ol, ul, menu { 31 | list-style: none; 32 | } 33 | 34 | /* For images to not be able to exceed their container */ 35 | img { 36 | max-inline-size: 100%; 37 | max-block-size: 100%; 38 | } 39 | 40 | /* removes spacing between cells in tables */ 41 | table { 42 | border-collapse: collapse; 43 | } 44 | 45 | /* Safari - solving issue when using user-select:none on the text input doesn't working */ 46 | input, textarea { 47 | -webkit-user-select: auto; 48 | } 49 | 50 | /* revert the 'white-space' property for textarea elements on Safari */ 51 | textarea { 52 | white-space: revert; 53 | } 54 | 55 | /* minimum style to allow to style meter element */ 56 | meter { 57 | -webkit-appearance: revert; 58 | appearance: revert; 59 | } 60 | 61 | /* preformatted text - use only for this feature */ 62 | :where(pre) { 63 | all: revert; 64 | } 65 | 66 | /* reset default text opacity of input placeholder */ 67 | ::placeholder { 68 | color: unset; 69 | } 70 | 71 | /* remove default dot (•) sign */ 72 | ::marker { 73 | content: initial; 74 | } 75 | 76 | /* fix the feature of 'hidden' attribute. 77 | display:revert; revert to element instead of attribute */ 78 | :where([hidden]) { 79 | display: none; 80 | } 81 | 82 | /* revert for bug in Chromium browsers 83 | - fix for the content editable attribute will work properly. 84 | - webkit-user-select: auto; added for Safari in case of using user-select:none on wrapper element */ 85 | :where([contenteditable]:not([contenteditable="false"])) { 86 | -moz-user-modify: read-write; 87 | -webkit-user-modify: read-write; 88 | overflow-wrap: break-word; 89 | -webkit-line-break: after-white-space; 90 | -webkit-user-select: auto; 91 | } 92 | 93 | /* apply back the draggable feature - exist only in Chromium and Safari */ 94 | :where([draggable="true"]) { 95 | -webkit-user-drag: element; 96 | } 97 | 98 | /* Revert Modal native behavior */ 99 | :where(dialog:modal) { 100 | all: revert; 101 | } 102 | 103 | body { 104 | font-family: sans-serif; 105 | } 106 | 107 | h1 { 108 | text-align: center; 109 | font-weight: bold; 110 | font-size: 2rem; 111 | line-height: 1.2; 112 | margin-top: 1rem; 113 | margin-bottom: 2rem; 114 | } 115 | 116 | button { 117 | background: rgb(28, 100, 242); 118 | padding: .5rem 1rem; 119 | line-height: 22px; 120 | border-radius: 8px; 121 | color: #FFF; 122 | margin: 0 auto; 123 | display: block; 124 | cursor: pointer; 125 | } 126 | -------------------------------------------------------------------------------- /JS/4-2FAInput/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | 3 | export default defineConfig({ 4 | // ... 5 | }) 6 | -------------------------------------------------------------------------------- /JS/5-Calendar/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /JS/5-Calendar/README.md: -------------------------------------------------------------------------------- 1 | # Calendrier d'évènement 2 | 3 | Pour cet exercice, on cherche à créer une visualisation capable d'afficher les évènements sous forme de calendrier mensuel 4 | 5 | ![Rendu attendu](./screenshot.jpg) 6 | 7 | ## Énoncé 8 | 9 | Le client exige que plusieurs comportements soient présent pour considérer le composant comme valide (des tests sont disponibles pour confirmer ces comportements) 10 | 11 | - On doit pouvoir afficher des évènements sur plusieurs jours 12 | - On peut afficher des évènements qui ont lieu a une heure précise 13 | - On ne souhaite pas utiliser de librairie comme fullcalendar car on veut avoir la main sur les évolutions. 14 | 15 | ## Environnement 16 | 17 | Après avoir installé les dépendances via `npm install` (ou autre gestionnaire) vous pourrez lancer la page contenant le tableau à l'aide de la commande : 18 | 19 | ```bash 20 | npm run dev 21 | ``` 22 | 23 | Un serveur local sera lancé et sera accessible sur [localhost:5173](http://localhost:5173). Vous pouvez commencer à travailler sur le fichier `src/main.js`. Les évènements sont disponibles dans le fichier `data.json`. 24 | 25 | ## Pour aller plus loin 26 | 27 | Si vous voulez pousser un peu plus loin la reflection, vous pouvez prévoir le code pour gérer la navigation (un bouton mois suivant / mois précédent). 28 | -------------------------------------------------------------------------------- /JS/5-Calendar/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/5-Calendar/bun.lockb -------------------------------------------------------------------------------- /JS/5-Calendar/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Calendrier 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /JS/5-Calendar/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Calendrier 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /JS/5-Calendar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prepcalendar", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "prettier": "^3.3.2", 6 | "vite": "^5.2.0" 7 | }, 8 | "private": true, 9 | "scripts": { 10 | "dev": "vite", 11 | "build": "vite build", 12 | "preview": "vite preview" 13 | }, 14 | "type": "module" 15 | } 16 | -------------------------------------------------------------------------------- /JS/5-Calendar/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/5-Calendar/screenshot.jpg -------------------------------------------------------------------------------- /JS/5-Calendar/src/data.js: -------------------------------------------------------------------------------- 1 | const today = immutDate(new Date()); 2 | 3 | export const Events = [ 4 | { 5 | name: "All day event", 6 | type: "doctor", 7 | fullDay: true, 8 | start: today.setDate(29).addMonth(-1).target, 9 | end: today.setDate(2).target, 10 | }, 11 | { 12 | name: "FullWeeks", 13 | fullDay: true, 14 | type: "holidays", 15 | start: today.setDate(1).target, 16 | end: today.setDate(13).target, 17 | }, 18 | { 19 | name: "a", 20 | fullDay: true, 21 | start: today.setDate(3).target, 22 | end: today.setDate(6).target, 23 | }, 24 | { 25 | name: "b", 26 | fullDay: true, 27 | start: today.setDate(5).target, 28 | end: today.setDate(8).target, 29 | }, 30 | { 31 | name: "Long Event", 32 | fullDay: true, 33 | start: today.setDate(7).target, 34 | end: today.setDate(10).target, 35 | }, 36 | { 37 | name: "SemiLong Event super long", 38 | fullDay: true, 39 | start: today.setDate(8).target, 40 | end: today.setDate(9).target, 41 | }, 42 | { 43 | name: "Repeating event", 44 | type: "holidays", 45 | start: today.setDate(9).setHours(16).target, 46 | end: today.setDate(9).setHours(17).target, 47 | }, 48 | { 49 | name: "Conference", 50 | fullDay: true, 51 | start: today.setDate(19).target, 52 | end: today.setDate(20).target, 53 | }, 54 | { 55 | name: "Meeting", 56 | start: today.setDate(20).setHours(10).target, 57 | end: today.setDate(20).setHours(11).target, 58 | }, 59 | { 60 | name: "Lunch", 61 | start: today.setDate(20).setHours(12).target, 62 | end: today.setDate(20).setHours(13).target, 63 | }, 64 | { 65 | name: "Birthday", 66 | start: today.setDate(21).setHours(7).target, 67 | end: today.setDate(21).setHours(7).target, 68 | }, 69 | { 70 | name: "Day event", 71 | fullDay: true, 72 | start: today.setDate(28).target, 73 | end: today.setDate(28).target, 74 | }, 75 | ]; 76 | 77 | /** 78 | * @typedef {{ 79 | * target: Date, 80 | * setMonth: (n: number) => ImmutableDate, 81 | * addMonth: (n: number) => ImmutableDate, 82 | * setDate: (n: number) => ImmutableDate, 83 | * setHours: (n: number) => ImmutableDate, 84 | * }} ImmutableDate 85 | */ 86 | 87 | /** 88 | * Crée un proxy pour transformer une date en immutable 89 | * 90 | * @param {Date} date 91 | * @return {ImmutableDate} 92 | */ 93 | function immutDate(date) { 94 | const immutableHandler = { 95 | get(target, props) { 96 | if (props === "target") { 97 | return target; 98 | } 99 | if (typeof props === "string" && props.startsWith("set")) { 100 | return function (...args) { 101 | const date = new Date(target); 102 | Reflect.get(date, props).apply(date, args); 103 | return new Proxy(date, immutableHandler); 104 | }; 105 | } 106 | if (typeof props === "string" && props.startsWith("add")) { 107 | return function (n) { 108 | const date = new Date(target); 109 | Reflect.get(date, props.replace("add", "set")).apply(date, [ 110 | Reflect.get(date, props.replace("add", "get")).apply(date) + n, 111 | ]); 112 | return new Proxy(date, immutableHandler); 113 | }; 114 | } 115 | const value = Reflect.get(...arguments); 116 | return typeof value == "function" ? value.bind(target) : value; 117 | }, 118 | }; 119 | return new Proxy(date, immutableHandler); 120 | } 121 | -------------------------------------------------------------------------------- /JS/5-Calendar/src/example/date.js: -------------------------------------------------------------------------------- 1 | const weekStartsOn = 1; 2 | 3 | const dayMs = 86400000; 4 | 5 | /** 6 | * Renvoie un identifiant unique pour un jour 7 | * @param {Date} date 8 | * @returns {string} 9 | */ 10 | export function dayId(date) { 11 | return `${date.getFullYear()}-${date.getMonth().toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; 12 | } 13 | 14 | /** 15 | * Renvoie la date en début de semaine 16 | * @param {Date} date 17 | * @returns {Date} 18 | */ 19 | export function startOfWeek(date) { 20 | const d = new Date(date); 21 | const day = d.getDay(); 22 | const diff = (day < weekStartsOn ? 7 : 0) + day - weekStartsOn; 23 | d.setDate(d.getDate() - diff); 24 | d.setHours(0, 0, 0, 0); 25 | return d; 26 | } 27 | 28 | /** 29 | * Renvoie la date en fin de semaine 30 | * @param {Date} date 31 | * @returns {Date} 32 | */ 33 | export function endOfWeek(date) { 34 | const d = new Date(date); 35 | const day = d.getDay(); 36 | const diff = (day < weekStartsOn ? -7 : 0) + 6 - (day - weekStartsOn); 37 | d.setDate(d.getDate() + diff); 38 | d.setHours(23, 59, 59, 999); 39 | return d; 40 | } 41 | 42 | /** 43 | * Renvoie la date en fin de mois 44 | * @param {Date} date 45 | * @returns {Date} 46 | */ 47 | export function endOfMonth(date) { 48 | const d = new Date(date); 49 | d.setMonth(d.getMonth() + 1); 50 | d.setDate(0); 51 | d.setHours(23, 59, 59, 999); 52 | return d; 53 | } 54 | 55 | /** 56 | * Ajoute plusieurs jours à une date 57 | * @param {Date} date 58 | * @param {number} n 59 | * @returns {Date} 60 | */ 61 | export function addDays(date, n) { 62 | const d = new Date(date); 63 | d.setDate(d.getDate() + n); 64 | return d; 65 | } 66 | 67 | /** 68 | * Renvoie les jours compris entre les 2 dates 69 | * @param {Date} start 70 | * @param {Date} end 71 | * @returns {Date[]} 72 | */ 73 | export function daysBetween(start, end) { 74 | if (start > end) { 75 | throw new Error( 76 | "La date de début ne peut pas être supérieur à la date de fin", 77 | ); 78 | } 79 | const days = []; 80 | let cursor = new Date(start); 81 | cursor.setHours(0, 0, 0, 0); 82 | while (cursor < end) { 83 | days.push(cursor); 84 | cursor = addDays(cursor, 1); 85 | } 86 | return days; 87 | } 88 | 89 | /** 90 | * Trouve le nombre de jours (calendrier) entre 2 dates 91 | * @param {Date} end 92 | * @param {Date} start 93 | * @returns {number} 94 | */ 95 | export function diffInDays(end, start) { 96 | const b = new Date(end); 97 | b.setHours(23, 59, 59, 999); 98 | const a = new Date(start); 99 | a.setHours(0, 0, 0, 0); 100 | return Math.round((b.getTime() - a.getTime()) / dayMs); 101 | } 102 | 103 | /** 104 | * Trouve la date la plus loin dans le temps 105 | * @param {Date[]} dates 106 | */ 107 | export function minDates(dates) { 108 | if (dates.length === 0) { 109 | throw new Error("Impossible de trouver le minimum sans dates"); 110 | } 111 | let min = dates[0]; 112 | for (const date of dates) { 113 | if (date < min) { 114 | min = date; 115 | } 116 | } 117 | return min; 118 | } 119 | -------------------------------------------------------------------------------- /JS/5-Calendar/src/example/main.js: -------------------------------------------------------------------------------- 1 | import { Events } from "../data.js"; 2 | import { 3 | addDays, 4 | dayId, 5 | daysBetween, 6 | diffInDays, 7 | endOfMonth, 8 | endOfWeek, 9 | minDates, 10 | startOfWeek, 11 | } from "./date.js"; 12 | 13 | /** 14 | * @typedef {{name: string, start: Date, end: Date, fullDay?: boolean, type?: string}} CalendarEvent 15 | */ 16 | 17 | const dayFormatter = new Intl.DateTimeFormat(undefined, { weekday: "long" }); 18 | const timeFormatter = new Intl.DateTimeFormat(undefined, { 19 | hour: "2-digit", 20 | minute: "2-digit", 21 | }); 22 | 23 | class Calendar { 24 | /** @type {Map} */ 25 | #eventsMap = new Map(); 26 | 27 | /** 28 | * @param {HTMLElement} root Element sur lequel on monte le calendrier 29 | * @param {CalendarEvent[]} events Liste des évènements à afficher 30 | * @param {number} month Mois du calendrier (0 pour Janvier) 31 | * @param {number} year Année du calendrier 32 | */ 33 | constructor(root, events, month, year) { 34 | this.#fillEventsMap(events); 35 | const startOfMonth = new Date(year, month, 1, 0, 0, 0, 0); 36 | const start = startOfWeek(startOfMonth); 37 | const end = endOfWeek(endOfMonth(startOfMonth)); 38 | const days = daysBetween(start, end); 39 | root.innerHTML = ` 40 | 41 | 42 | ${Array.from( 43 | { length: 7 }, 44 | (_, k) => ``, 45 | ).join("")} 46 | 47 | 48 | 49 |
${dayFormatter.format(addDays(start, k))}
`; 50 | const tbody = root.querySelector("tbody"); 51 | let tr = document.createElement("tr"); 52 | /** @type {Map} */ 53 | const positionMap = new Map(); 54 | for (const day of days) { 55 | tr.append(this.#buildCell(day, month, positionMap)); 56 | if (day.getDay() === 0) { 57 | tbody.append(tr); 58 | tr = document.createElement("tr"); 59 | positionMap.clear(); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Construit l'élément qui représente un jour 66 | * @param {Date} date 67 | * @param {number} month 68 | * @param {Map} positionMap 69 | * @returns {HTMLTableCellElement} 70 | */ 71 | #buildCell(date, month, positionMap) { 72 | const getAvailablePosition = () => { 73 | if (positionMap.size === 0) { 74 | return 0; 75 | } 76 | const positions = Array.from(positionMap.values()); 77 | const max = Math.max(...positions); 78 | for (let i = 0; i < max; i++) { 79 | if (!positions.includes(i)) { 80 | return i; 81 | } 82 | } 83 | return max + 1; 84 | }; 85 | const td = document.createElement("td"); 86 | const isCurrentMonth = date.getMonth() === month; 87 | td.innerHTML = `
88 |
${date.getDate()}
89 |
90 |
`; 91 | const container = td.querySelector(".calendar__events"); 92 | const idDate = dayId(date); 93 | const events = this.#eventsMap.get(idDate) ?? []; 94 | const finishedEvents = []; 95 | for (const event of events) { 96 | const classes = ["calendar__event"]; 97 | if (event.type) { 98 | classes.push("calendar__event-" + event.type); 99 | } 100 | 101 | // On est au début d'un évènement sur plusieurs jours 102 | if ( 103 | event.fullDay && 104 | (idDate === dayId(event.start) || date.getDay() === 1) 105 | ) { 106 | const position = getAvailablePosition(); 107 | positionMap.set(event, position); 108 | const endDate = minDates([event.end, endOfWeek(date)]); 109 | const days = diffInDays(endDate, date); 110 | if (idDate !== dayId(event.start)) { 111 | classes.push("calendar__event-overflow-left"); 112 | } 113 | if (endDate !== event.end) { 114 | classes.push("calendar__event-overflow-right"); 115 | } 116 | classes.push("calendar__event-fullday"); 117 | container.insertAdjacentHTML( 118 | "beforeend", 119 | `
123 | ${event.name} 124 |
`, 125 | ); 126 | } 127 | 128 | if (event.fullDay && idDate === dayId(event.end)) { 129 | finishedEvents.push(event); 130 | } 131 | if (!event.fullDay) { 132 | classes.push("calendar__event-hour"); 133 | container.insertAdjacentHTML( 134 | "beforeend", 135 | `
136 | ${timeFormatter.format(event.start)} - ${event.name} 137 |
`, 138 | ); 139 | } 140 | } 141 | 142 | container.style.setProperty( 143 | "--offset", 144 | (Math.max(...positionMap.values()) + 1).toString(), 145 | ); 146 | 147 | for (const event of finishedEvents) { 148 | positionMap.delete(event); 149 | } 150 | 151 | return td; 152 | } 153 | 154 | /** 155 | * @param {CalendarEvent[]} events 156 | */ 157 | #fillEventsMap(events) { 158 | const sortedEvents = [...events].sort((a, b) => 159 | a.start < b.start ? -1 : 1, 160 | ); 161 | for (const event of sortedEvents) { 162 | const days = daysBetween(event.start, event.end); 163 | for (const day of days) { 164 | const id = dayId(day); 165 | this.#eventsMap.set(id, [...(this.#eventsMap.get(id) ?? []), event]); 166 | } 167 | } 168 | } 169 | } 170 | 171 | const c = new Calendar( 172 | document.getElementById("app"), 173 | Events, 174 | new Date().getMonth(), 175 | new Date().getFullYear(), 176 | ); 177 | console.log(c); 178 | -------------------------------------------------------------------------------- /JS/5-Calendar/src/example/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 1rem; 3 | } 4 | 5 | .calendar { 6 | width: 100%; 7 | border-collapse: collapse; 8 | --color: #3788d8; 9 | } 10 | 11 | .calendar td, .calendar th { 12 | border: 1px solid rgba(255, 255, 255, 0.3); 13 | } 14 | 15 | .calendar th { 16 | border-color: #FFF; 17 | border-bottom-width: 2px; 18 | width: calc(100% / 7); 19 | } 20 | 21 | .calendar td { 22 | vertical-align: top; 23 | width: calc(100% / 7); 24 | max-width: 0; 25 | margin: 0; 26 | padding: 0; 27 | } 28 | 29 | .calendar__cell { 30 | --padding: .5rem; 31 | padding: var(--padding); 32 | aspect-ratio: 1; 33 | 34 | min-width: 0; 35 | width: 100%; 36 | } 37 | 38 | .calendar__date { 39 | text-align: right; 40 | } 41 | 42 | .calendar__date-diff { 43 | opacity: .3; 44 | } 45 | 46 | .calendar__events { 47 | position: relative; 48 | padding-top: calc(var(--offset) * 24px); 49 | } 50 | 51 | .calendar__event-hours { 52 | font-size: .9rem; 53 | } 54 | 55 | .calendar__event-hour { 56 | display: flex; 57 | align-items: center; 58 | gap: .3rem; 59 | } 60 | 61 | .calendar__event-hour::before { 62 | content:''; 63 | flex: none; 64 | width: 8px; 65 | height: 8px; 66 | display: block; 67 | border-radius: 50%; 68 | background-color: var(--color); 69 | } 70 | 71 | .calendar__event-hour span { 72 | display: block; 73 | white-space: nowrap; 74 | overflow: hidden; 75 | text-overflow: ellipsis; 76 | } 77 | 78 | .calendar__event-fullday { 79 | position: absolute; 80 | --overflow: 0px; 81 | width: calc(100% * var(--days) + (var(--padding) * 2 + 1px) * (var(--days) - 1) + var(--overflow)); 82 | background-color: var(--color); 83 | color: #FFF; 84 | height: 20px; 85 | line-height: 20px; 86 | border-radius: 2px; 87 | padding: 0 5px; 88 | top: calc(var(--offset) * 24px) 89 | } 90 | 91 | .calendar__event-overflow-left{ 92 | margin-left: calc(var(--padding) * -1); 93 | --overflow: var(--padding); 94 | border-bottom-left-radius: 0; 95 | border-top-left-radius: 0; 96 | } 97 | .calendar__event-overflow-right{ 98 | --overflow: var(--padding); 99 | border-bottom-right-radius: 0; 100 | border-top-right-radius: 0; 101 | } 102 | 103 | .calendar__event-overflow-left.calendar__event-overflow-right { 104 | --overflow: calc(var(--padding) * 2); 105 | } 106 | 107 | .calendar__event-doctor { 108 | --color: #ff5858; 109 | } 110 | 111 | .calendar__event-holidays { 112 | --color: #74b057; 113 | } 114 | 115 | -------------------------------------------------------------------------------- /JS/5-Calendar/src/main.js: -------------------------------------------------------------------------------- 1 | import {Events} from "./data.js"; 2 | 3 | /** 4 | * @typedef {{ 5 | * name: string, 6 | * start: Date, 7 | * end: Date, 8 | * fullDay?: boolean, 9 | * type?: string 10 | * }} CalendarEvent Évènement à placer dans le calendrier 11 | */ 12 | 13 | class Calendar { 14 | 15 | /** 16 | * @param {HTMLElement} root Element sur lequel on monte le calendrier 17 | * @param {CalendarEvent[]} events Liste des évènements à afficher 18 | * @param {number} month Mois du calendrier (0 pour Janvier) 19 | * @param {number} year Année du calendrier 20 | */ 21 | constructor(root, events, month, year) { 22 | /** 23 | * Commencer le code ici 24 | */ 25 | } 26 | } 27 | 28 | const c = new Calendar( 29 | document.getElementById('app'), 30 | Events, 31 | new Date().getMonth(), 32 | new Date().getFullYear() 33 | ) 34 | -------------------------------------------------------------------------------- /JS/5-Calendar/src/reset.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | --contrast: #3788d8 15 | } 16 | 17 | /* 18 | 1. Use a more-intuitive box-sizing model. 19 | */ 20 | *, *::before, *::after { 21 | box-sizing: border-box; 22 | } 23 | /* 24 | 2. Remove default margin 25 | */ 26 | * { 27 | margin: 0; 28 | } 29 | /* 30 | Typographic tweaks! 31 | 3. Add accessible line-height 32 | 4. Improve text rendering 33 | */ 34 | body { 35 | line-height: 1.5; 36 | -webkit-font-smoothing: antialiased; 37 | } 38 | /* 39 | 5. Improve media defaults 40 | */ 41 | img, picture, video, canvas, svg { 42 | display: block; 43 | max-width: 100%; 44 | } 45 | /* 46 | 6. Remove built-in form typography styles 47 | */ 48 | input, button, textarea, select { 49 | font: inherit; 50 | } 51 | /* 52 | 7. Avoid text overflows 53 | */ 54 | p, h1, h2, h3, h4, h5, h6 { 55 | overflow-wrap: break-word; 56 | } 57 | /* 58 | 8. Create a root stacking context 59 | */ 60 | #root, #__next { 61 | isolation: isolate; 62 | } 63 | -------------------------------------------------------------------------------- /JS/5-Calendar/src/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/Challenges/32cd9e470ca91951122625bce9eb31322579a6e3/JS/5-Calendar/src/style.css -------------------------------------------------------------------------------- /JS/5-Calendar/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /JS/5-Calendar/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true 21 | }, 22 | "include": ["src"] 23 | } 24 | --------------------------------------------------------------------------------