diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..f47ef9586 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# All files +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintrc b/.eslintrc index 570701c76..7cc3223f3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,11 +1,22 @@ { - "extends": ["airbnb", "plugin:jest/recommended"], + "extends": [ + "plugin:jest/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:testing-library/react", // added in for RTL tests + "plugin:jest-dom/recommended" // added in for RTL tests + ], "root": true, - "plugins": ["jest", "react"], + "plugins": ["jest", "react", "react-hooks", "@typescript-eslint", "testing-library", "jest-dom"], "rules": { "arrow-parens": [2, "as-needed"], "import/no-unresolved": "off", - "import/extensions": "off" + "import/extensions": "off", + "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks + "react-hooks/exhaustive-deps": "warn", // Checks effect dependencies + "react/jsx-filename-extension": [0], + "linebreak-style": "off", + "max-len": [{ "ignoreComments": true }] }, "env": { "es6": true, @@ -25,4 +36,4 @@ "ecmaVersion": 2018, "sourceType": "module" } -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index 9ae5b953c..aa3509cac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,19 @@ node_modules +node_modules/ +/node_modules +./node_modules .DS_Store src/extension/build/bundles package/reactime-*.tgz -tictactoe parents coverage src/extension/build.zip src/extension/build.crx -src/extension/build/key.pem +src/extension/build.pem bower_components +sandboxes/manual-tests/NextJS/.next +.vscode +package-lock.json +yarn.lock +docs/**/* +docs/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..2fc98bd91 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "reactime-website"] + path = reactime-website + url = https://github.com/reactimetravel/reactime-website diff --git a/.npmcheckrc b/.npmcheckrc new file mode 100644 index 000000000..297d19125 --- /dev/null +++ b/.npmcheckrc @@ -0,0 +1,10 @@ +{"depcheck": + { + "ignoreMatches": [ + "css-loader", + "sass-loader", + "style-loader", + "typedoc-webpack-plugin" + ] + } +} \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..9bdd4961d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "printWidth": 100, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "tabWidth": 2, + "bracketSpacing": true, + "trailingComma": "all" +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0655bdd57..000000000 --- a/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -services: - - docker -script: - - docker-compose up --abort-on-container-exit diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..86a390f5d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "eslint.enable": true, + "git.ignoreLimitWarning": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..d63679ccb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,107 @@ +# Contributing to Reactime :sparkles: + +Thank you for your interest in making Reactime even better! :heart_eyes: Your help is invaluable, and we appreciate every contribution, big or small. This guide will walk you through the process of opening issues, creating pull requests, and navigating our workflow. + +## New Contributor Guide :hatching_chick: + +Whether you’re brand new to open source or a seasoned pro, we encourage you to: + +- **Check out our [README](README.md).** + It’ll give you a birds-eye view of what Reactime does and how you can get involved. +- **Explore these helpful resources:** + - [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) + - [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git) + - [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) + - [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) + +## Getting Started :rocket: + +If you want to understand the codebase in more detail, take a look at our [Developer Guidelines](src/DEVELOPER_README.md). :confetti_ball: + +### Issues :eyes: + +#### Creating a New Issue :new: + +1. **Check existing issues.** + Before opening a new issue, please [search if it already exists](https://github.com/open-source-labs/reactime/issues). + +2. **Open your own issue.** + If you can’t find an existing issue, feel free to [open a new one](https://github.com/open-source-labs/reactime/issues/new) to report bugs, request features, or suggest improvements. + +#### Solving an Issue :wrench: + +1. **Pick an issue.** + Browse through our [open issues](https://github.com/open-source-labs/reactime/issues). We don’t officially assign them, so if something sparks your interest, go for it! + +2. **Open a pull request.** + Once you’re ready to propose a fix or feature, you can open a PR referencing the issue you’re solving. + +### Make Changes :rainbow: + +#### Small Edits in the UI :pencil2: + +- Click **Make a contribution** at the bottom of any documentation page to quickly fix typos, broken links, or small wording changes. +- This will take you directly to the `.md` file, where you can make edits and open a pull request. + +#### Larger Changes Locally :computer: + +1. **Install Git LFS.** + Follow the instructions [here](https://docs.github.com/en/github/managing-large-files/versioning-large-files/installing-git-large-file-storage). + +2. **Fork the Repository.** + + - **GitHub Desktop:** + [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) walks you through setup. Then, you can [fork the repo](https://github.com/open-source-labs/reactime.git) right from GitHub Desktop! + - **Command Line:** + [Fork the repo](https://github.com/open-source-labs/reactime.git) and clone your fork locally so you can work on your own copy. + +3. **Create a working branch.** + Name it something descriptive (e.g., `feature/new-feature` or `fix/typo-in-docs`). + +### Commit Your Changes :white_check_mark: + +When you’re happy with your updates: + +1. **Commit them locally.** + Write clear commit messages describing _what_ you changed and _why_. + +2. **Push to your branch.** + This makes your changes visible on GitHub. + +### Open a Pull Request :arrows_counterclockwise: + +Once you’ve finished working and pushed your code: + +1. **Create the PR.** + Click on **Compare & pull request** on your branch to open a new PR. + +2. **Fill the “Ready for review” template.** + This helps reviewers quickly understand the context and purpose of your changes. + +3. **Link Issues.** + If your PR fixes or relates to an existing issue, [link it](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) in the PR description. + +4. **Allow Maintainer Edits.** + Check the box so our team can help update your branch if needed. + +5. **Address Feedback.** + If reviewers suggest changes, you can: + + - Apply **suggested changes** directly on the GitHub UI. + - Make edits in your local branch and push them. + +6. **Resolve Conversations.** + Mark each PR comment as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations) once you’ve addressed it. + +7. **Handle Merge Conflicts.** + Check out [this tutorial](https://github.com/skills/resolve-merge-conflicts) if you get stuck. + +## Your PR is Merged! :tada: + +**Congratulations and thank you!** :dancer: :dancer: Once we merge your PR, your contributions become part of Reactime. We appreciate every contribution, and we hope you’ll stick around for more. + +> If you have any further questions or ideas, don’t hesitate to open another issue or join the conversation in the repo! + +--- + +Happy coding and welcome to the Reactime community! :sparkles: diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 7eae5ac62..000000000 --- a/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM node:10.16.2 -WORKDIR /usr/src/app -COPY package*.json ./ -RUN npm i diff --git a/LICENSE b/LICENSE index d19ddaf80..498003dd9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 reactime +Copyright (c) 2025 reactime Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.fr.md b/README.fr.md new file mode 100644 index 000000000..86ac9d8e2 --- /dev/null +++ b/README.fr.md @@ -0,0 +1,379 @@ + +

+ + Outil de Performance React +
+ 🏆 Nominé pour les React Open Source Awards 2020 +
+

+ +

Une puissante extension Chrome qui améliore le développement React grâce au débogage avec retour dans le temps et à la surveillance avancée des performances

+
+ +

+ + Chrome Web Store + + + Utilisateurs sur le Chrome Web Store + + + Évaluation sur le Chrome Web Store + +

+ +
+
+ 🇷🇺   РУССКАЯ ВЕРСИЯ   •   🇺🇸   ENGLISH VERSION   •   👩‍💻 README Développeur +
+
+
+ +

+ +

+ +##

✨ Fonctionnalités Clés

+ +### 🔍 Visualisation de l'État + +- **Vues Multiples** : Visualisez l’état de votre application via des Graphiques de Composants, des Arborescences JSON, des Graphiques de Performances et des Arbres d’Accessibilité +- **Historique Chronologique** : Suivez l’évolution de l’état dans le temps grâce à une représentation intuitive de l’historique +- **Métriques Web** : Surveillez en temps réel les métriques de performance essentielles +- **Aperçus d’Accessibilité** : Analysez l’arbre d’accessibilité de votre application pour chaque changement d’état +
+ +

Sur la page principale, vous disposez de deux choix principaux depuis le menu déroulant :

+ +- **Timejump** : Consultez et naviguez dans l’historique des snapshots de l’état de votre application. Vous pouvez revenir à n’importe quel point dans le temps pour observer l’évolution de l’état au fil des modifications. Vous pouvez également utiliser le bouton de lecture pour rejouer chaque changement d’état automatiquement. +- **Providers / Consumers** : Comprenez mieux les dépendances de contexte de votre application et leurs interactions grâce à une visualisation des relations entre fournisseurs et consommateurs. +
+ +

+ +

+
+ +### ⏱️ Débogage avec Retour dans le Temps + +- **Snapshots d’État** : Capturez et naviguez à travers l’historique d’état de votre application +- **Commandes de Lecture** : Rejouez automatiquement les changements d’état avec une vitesse ajustable +- **Points de Saut** : Naviguez instantanément vers n’importe quel état antérieur +- **Comparaisons Diff** : Comparez l’état entre différents snapshots +
+ +

+ +

+
+ +### 📊 Analyse de Performance + +- **Métriques de Composants** : Mesurez les temps de rendu et identifiez les goulets d’étranglement +- **Comparaison de Séries** : Comparez les performances sur différentes séries de changements d’état +- **Détection de Re-rendu** : Identifiez et corrigez les rendus inutiles +- **Web Vitals** : Surveillez les Core Web Vitals et d’autres métriques de performance +
+
+ +### 🔄 Prise en Charge des Frameworks Modernes + + +
+ +### 💾 Persistance & Partage d’État + +Reactime facilite la sauvegarde et le partage de l’historique d’état de votre application : + +- **Exporter l’Historique d’État** : Enregistrez vos snapshots sous forme de fichier JSON pour une analyse ultérieure ou pour les partager +- **Importer des Sessions Précédentes** : Chargez des snapshots enregistrés précédemment pour comparer les changements d’état entre différentes sessions +- **Analyse Inter-Session** : Comparez les performances et les changements d’état entre différentes sessions de développement +
+ +

+ +

+
+ +### 📚 Documentation Interactive + +Reactime propose une documentation complète pour aider les développeurs à comprendre son architecture et ses APIs. +Après avoir cloné ce référentiel, les développeurs peuvent simplement exécuter `npm run docs` à la racine et servir le fichier `/docs/index.html` généré dynamiquement, offrant : + + +
+ +

🎉 Nouveautés !

+ +La version 26.0 de Reactime propose une refonte complète de l’expérience de débogage React, avec : + +- **Nouvelle Visualisation des Données de Contexte** + + - Première visualisation des changements d’état du hook useContext + - Cartographie claire des relations fournisseur-consommateur + - Surveillance en temps réel de la valeur d’état du contexte + - Visualisation détaillée des données du fournisseur + +- **Débogage avec Retour dans le Temps Amélioré** + + - Interface du curseur de temps repensée, positionnée à côté des snapshots + - Contrôles de vitesse de lecture variables + - Navigation plus intuitive dans l’état + - Visualisation de snapshot améliorée + +- **Refonte Complète de l’UI Moderne** + + - Design élégant et contemporain avec composants arrondis + - Améliorations de la disposition pour une meilleure intuitivité + - Nouveau mode sombre + - Hiérarchie visuelle améliorée + +- **Améliorations Techniques Majeures** + - Correction de la persistance de connexion lors de périodes d’inactivité et de changements d’onglet + - Restauration de la visualisation de l’arbre d’accessibilité + - Résolution de problèmes de capture d’état pour les hooks useState basés sur des fonctions + - Fiabilité et performance globales de l’extension grandement améliorées + +Ces mises à jour rendent Reactime plus puissant, plus fiable et plus convivial que jamais, établissant un nouveau standard pour les outils de débogage React. + +Pour en savoir plus sur les versions précédentes, cliquez ici ! +
+
+ +

🚀 Bien Commencer

+ +### Installation + +1. Installez l’[extension Reactime](https://chrome.google.com/webstore/detail/reactime/cgibknllccemdnfhfpmjhffpjfeidjga) depuis le Chrome Web Store +2. Installez l’extension requise [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) si vous ne l’avez pas déjà + +### Prérequis + +- Votre application React doit fonctionner en **mode développement** +- L’extension React Developer Tools doit être installée +- Navigateur Chrome (version 80 ou supérieure recommandée) + +### Lancer Reactime + +Il existe deux manières d’ouvrir le panneau Reactime : + +1. **Menu Contextuel** + + - Faites un clic droit n’importe où dans votre application React + - Sélectionnez "Reactime" dans le menu contextuel + +2. **DevTools** + - Ouvrez les DevTools de Chrome (F12 ou ⌘+⌥+I) + - Naviguez jusqu’à l’onglet "Reactime" + +Une fois lancé, Reactime commencera automatiquement à surveiller les changements d’état et les métriques de performance de votre application. +
+
+ +

🤝 Contribuer à Reactime

+ +Nous accueillons avec joie les contributions de développeurs de tous niveaux ! Voici comment vous pouvez aider à améliorer Reactime: 🙋 Contributing README + +1. **Commencer** + + - Forkez le dépôt + - Consultez notre README Développeur détaillé + - Mettez en place votre environnement de développement local + +2. **Processus de Build** + + - Suivez les instructions de build dans le README Développeur + - Testez soigneusement vos modifications + - Soumettez une Pull Request + +Rejoignez notre communauté grandissante de contributeurs et participez à façonner l’avenir des outils de débogage React ! Pour des lignes directrices de contribution plus détaillées et des informations sur l’architecture du projet, veuillez vous référer à notre 👩‍💻 README Développeur. +
+
+ +

🛠️ Dépannage

+ +### ❓ Pourquoi Reactime n’enregistre-t-il pas les nouveaux changements d’état ? + +Reactime a perdu sa connexion avec l’onglet que vous surveillez ; il vous suffit de cliquer sur le bouton "reconnecter" +pour reprendre votre travail. + +### ❓ Pourquoi Reactime ne trouve-t-il pas mes hooks ? + +Reactime détecte et surveille les hooks en parcourant le code React non minifié de votre application en mode développement. Si votre processus de build minifie ou "uglifie" votre code — même pour les builds de développement — Reactime risque de ne pas pouvoir localiser et suivre correctement vos hooks. Pour résoudre ce problème : + +1. **Assurez-vous d’une vraie build de développement** : Vérifiez la configuration de votre bundler ou outil de build (par ex. Webpack, Babel, Vite, etc.) pour vous assurer que votre application n’est pas minimisée ou "uglifiée" en mode développement. + + - Par exemple, avec Webpack, assurez-vous d’exécuter le mode : 'development', ce qui devrait désactiver la minification par défaut. + - Dans un projet Create React App, il suffit d’exécuter `npm start` ou `yarn start` pour configurer automatiquement une build de développement non minifiée. + +2. **Vérifiez les surcharges** : Assurez-vous qu’aucun plugin Babel ou Webpack personnalisé ne minifie votre code, surtout si vous utilisez des frameworks comme Next.js ou Gatsby. Parfois, des plugins ou scripts supplémentaires peuvent s’exécuter en arrière-plan. + +3. **Redémarrez & recompilez** : Après avoir modifié toute configuration de build, recompilez ou redémarrez votre serveur de développement pour vous assurer que la nouvelle configuration est prise en compte. Ensuite, rafraîchissez l’onglet de votre navigateur afin que Reactime puisse détecter vos hooks non minifiés. + +Après avoir modifié toute configuration de build, recompilez ou redémarrez votre serveur de développement pour vous assurer que la nouvelle configuration est prise en compte. Ensuite, rafraîchissez l’onglet de votre navigateur afin que Reactime puisse détecter vos hooks non minifiés. + +### ❓ Pourquoi Reactime m’indique qu’aucune application React n’a été trouvée ? + +Reactime s’exécute initialement grâce au hook global des dev tools de Chrome. +Il faut du temps à Chrome pour le charger. Essayez de rafraîchir votre application plusieurs fois jusqu’à ce que vous voyiez Reactime en fonctionnement. + +### ❓ Pourquoi dois-je avoir les React Dev Tools activées ? + +Reactime fonctionne de concert avec les React Developer Tools pour accéder à l’arbre Fiber d’une application React ; en interne, Reactime parcourt l’arbre Fiber via le hook global des React Dev Tools, récupérant toutes les informations pertinentes à afficher au développeur. + +### ❓ J’ai trouvé un bug dans Reactime + +Reactime est un projet open-source, et nous serions ravis d’avoir vos retours pour améliorer l’expérience utilisateur. Veuillez consulter le 👩‍💻 README Développeur, +et créer une Pull Request (ou une issue) pour proposer et collaborer sur des modifications de Reactime. + +### ❓ Compatibilité avec les versions Node + +Depuis la sortie de Node v18.12.1 (LTS) le 04/11/22, le script a été mis à jour avec +`npm run dev` | `npm run build` pour assurer une rétrocompatibilité.
+Pour la version Node v16.16.0, veuillez utiliser les scripts `npm run devlegacy` | `npm run buildlegacy` +
+
+ +

✍️ Auteurs

+ +- **Garrett Chow** - [@garrettlchow](https://github.com/garrettlchow) +- **Ellie Simens** - [@elliesimens](https://github.com/elliesimens) +- **Ragad Mohammed** - [@ragad-mohammed](https://github.com/ragad-mohammed) +- **Daniel Ryczek** - [@dryczek14](https://github.com/dryczek01) +- **Patrice Pinardo** - [@pinardo88](https://github.com/pinardo88) +- **Haider Ali** - [@hali03](https://github.com/hali03) +- **Jose Luis Sanchez** - [@JoseSanchez1996](https://github.com/JoseSanchez1996) +- **Logan Nelsen** - [@ljn16](https://github.com/ljn16) +- **Mel Koppens** - [@MelKoppens](https://github.com/MelKoppens) +- **Amy Yang** - [@ay7991](https://github.com/ay7991) +- **Eva Ury** - [@evaSUry](https://github.com/evaSUry) +- **Jesse Guerrero** - [@jguerrero35](https://github.com/jguerrero35) +- **Oliver Cho** - [@Oliver-Cho](https://github.com/Oliver-Cho) +- **Ben Margolius** - [@benmarg](https://github.com/benmarg) +- **Eric Yun** - [@ericsngyun](https://github.com/ericsngyun) +- **James Nghiem** - [@jemzir](https://github.com/jemzir) +- **Wilton Lee** - [@wiltonlee948](https://github.com/wiltonlee948) +- **Louis Lam** - [@llam722](https://github.com/llam722) +- **Samuel Tran** - [@leumastr](https://github.com/leumastr) +- **Brian Yang** - [@yangbrian310](https://github.com/yangbrian310) +- **Emin Tahirov** - [@eminthrv](https://github.com/eminthrv) +- **Peng Dong** - [@d28601581](https://github.com/d28601581) +- **Ozair Ghulam** - [@ozairgh](https://github.com/ozairgh) +- **Christina Or** - [@christinaor](https://github.com/christinaor) +- **Khanh Bui** - [@AndyB909](https://github.com/AndyB909) +- **David Kim** - [@codejunkie7](https://github.com/codejunkie7) +- **Robby Tipton** - [@RobbyTipton](https://github.com/RobbyTipton) +- **Kevin HoEun Lee** - [@khobread](https://github.com/khobread) +- **Christopher LeBrett** - [@fscgolden](https://github.com/fscgolden) +- **Joseph Park** - [@joeepark](https://github.com/joeepark) +- **Kris Sorensen** - [@kris-sorensen](https://github.com/kris-sorensen) +- **Daljit Gill** - [@dgill05](https://github.com/dgill05) +- **Ben Michareune** - [@bmichare](https://github.com/bmichare) +- **Dane Corpion** - [@danecorpion](https://github.com/danecorpion) +- **Harry Fox** - + [@StackOverFlowWhereArtThou](https://github.com/StackOverFlowWhereArtThou) +- **Nathan Richardson** - [@BagelEnthusiast](https://github.com/BagelEnthusiast) +- **David Bernstein** - [@dangitbobbeh](https://github.com/dangitbobbeh) +- **Joseph Stern** - [@josephiswhere](https://github.com/josephiswhere) +- **Dennis Lopez** - [@DennisLpz](https://github.com/DennisLpz) +- **Cole Styron** - [@colestyron](https://github.com/C-STYR) +- **Ali Rahman** - [@CourageWolf](https://github.com/CourageWolf) +- **Caner Demir** - [@demircaner](https://github.com/demircaner) +- **Kevin Ngo** - [@kev-ngo](https://github.com/kev-ngo) +- **Becca Viner** - [@rtviner](https://github.com/rtviner) +- **Caitlin Chan** - [@caitlinchan23](https://github.com/caitlinchan23) +- **Kim Mai Nguyen** - [@Nkmai](https://github.com/Nkmai) +- **Tania Lind** - [@lind-tania](https://github.com/lind-tania) +- **Alex Landeros** - [@AlexanderLanderos](https://github.com/AlexanderLanderos) +- **Chris Guizzetti** - [@guizzettic](https://github.com/guizzettic) +- **Jason Victor** - [@theqwertypusher](https://github.com/Theqwertypusher) +- **Sanjay Lavingia** - [@sanjaylavingia](https://github.com/sanjaylavingia) +- **Vincent Nguyen** - [@VNguyenCode](https://github.com/VNguyenCode) +- **Haejin Jo** - [@haejinjo](https://github.com/haejinjo) +- **Hien Nguyen** - [@hienqn](https://github.com/hienqn) +- **Jack Crish** - [@JackC27](https://github.com/JackC27) +- **Kevin Fey** - [@kevinfey](https://github.com/kevinfey) +- **Carlos Perez** - [@crperezt](https://github.com/crperezt) +- **Edwin Menendez** - [@edwinjmenendez](https://github.com/edwinjmenendez) +- **Gabriela Jardim Aquino** - [@aquinojardim](https://github.com/aquinojardim) +- **Greg Panciera** - [@gpanciera](https://github.com/gpanciera) +- **Nathanael Wa Mwenze** - [@nmwenz90](https://github.com/nmwenz90) +- **Ryan Dang** - [@rydang](https://github.com/rydang) +- **Bryan Lee** - [@mylee1995](https://github.com/mylee1995) +- **Josh Kim** - [@joshua0308](https://github.com/joshua0308) +- **Sierra Swaby** - [@starkspark](https://github.com/starkspark) +- **Ruth Anam** - [@nusanam](https://github.com/nusanam) +- **David Chai** - [@davidchaidev](https://github.com/davidchai717) +- **Yujin Kang** - [@yujinkay](https://github.com/yujinkay) +- **Andy Wong** - [@andynullwong](https://github.com/andynullwong) +- **Chris Flannery** - + [@chriswillsflannery](https://github.com/chriswillsflannery) +- **Rajeeb Banstola** - [@rajeebthegreat](https://github.com/rajeebthegreat) +- **Prasanna Malla** - [@prasmalla](https://github.com/prasmalla) +- **Rocky Lin** - [@rocky9413](https://github.com/rocky9413) +- **Abaas Khorrami** - [@dubalol](https://github.com/dubalol) +- **Ergi Shehu** - [@Ergi516](https://github.com/ergi516) +- **Raymond Kwan** - [@rkwn](https://github.com/rkwn) +- **Joshua Howard** - [@joshua-howard](https://github.com/joshua-howard) +- **Lina Shin** - [@rxlina](https://github.com/rxlina) +- **Andy Tsou** - [@andytsou19](https://github.com/andytsou19) +- **Feiyi Wu** - [@FreyaWu](https://github.com/FreyaWu) +- **Viet Nguyen** - [@vnguyen95](https://github.com/vnguyen95) +- **Alex Gomez** - [@alexgomez9](https://github.com/alexgomez9) +- **Edar Liu** - [@liuedar](https://github.com/liuedar) +- **Kristina Wallen** - [@kristinawallen](https://github.com/kristinawallen) +- **Quan Le** - [@Blachfog](https://github.com/Blachfog) +- **Robert Maeda** - [@robmaeda](https://github.com/robmaeda) +- **Lance Ziegler** - [@lanceziegler](https://github.com/lanceziegler) +- **Ngoc Zwolinski** - [@ngoczwolinski](https://github.com/ngoczwolinski) +- **Peter Lam** - [@dev-plam](https://github.com/dev-plam) +- **Zachary Freeman** - [@zacharydfreeman](https://github.com/zacharydfreeman/) +- **Jackie Yuan** - [@yuanjackie1](https://github.com/yuanjackie1) +- **Jasmine Noor** - [@jasnoo](https://github.com/jasnoo) +- **Minzo Kim** - [@minzo-kim](https://github.com/minzo-kim) +- **Mark Teets** - [@MarkTeets](https://github.com/MarkTeets) +- **Nick Huemmer** - [@NickHuemmer](https://github.com/ElDuke717) +- **James McCollough** - [@j-mccoll](https://github.com/j-mccoll) +- **Mike Bednarz** - [@mikebednarz](https://github.com/mikebednarz) +- **Sergei Liubchenko** - [@sergeylvq](https://github.com/sergeylvq) +- **Yididia Ketema** - [@yididiaketema](https://github.com/yididiaketema) +- **Morah Geist** - [@morahgeist](https://github.com/morahgeist) +- **Eivind Del Fierro** - [@EivindDelFierro](https://github.com/EivindDelFierro) +- **Kyle Bell** - [@KyEBell](https://github.com/KyEBell) +- **Sean Kelly** - [@brok3turtl3](https://github.com/brok3turtl3) +- **Christopher Stamper** - [@ctstamper](https://github.com/ctstamper) +- **Jimmy Phy** - [@jimmally](https://github.com/jimmally) +- **Andrew Byun** - [@AndrewByun](https://github.com/AndrewByun) +- **Kelvin Mirhan** - [@kelvinmirhan](https://github.com/kelvinmirhan) +- **Jesse Rosengrant** - [@jrosengrant](https://github.com/jrosengrant) +- **Liam Donaher** - [@leebology](https://github.com/leebology) +- **David Moore** - [@Solodt55](https://github.com/Solodt55) +- **John Banks** - [@Jbanks123](https://github.com/Jbanks123) +
+ +

⚖️ Licence

+ +Ce projet est distribué sous licence MIT - voir le fichier [LICENSE](LICENSE) pour plus de détails. diff --git a/README.id.md b/README.id.md new file mode 100644 index 000000000..80b2b8898 --- /dev/null +++ b/README.id.md @@ -0,0 +1,361 @@ +

+ +
+ Alat Kinerja React +
+ 🏆 Dinominasikan untuk React Open Source Awards 2020 +
+

+ +

Ekstensi Chrome yang kuat untuk meningkatkan pengembangan React dengan debugging lintas-waktu dan pemantauan kinerja lanjutan

+ +

Baca Artikel Kami di Medium untuk mempelajari lebih lanjut tentang proses di balik layar dan pengembangan Reactime!

+
+ +

+ + Chrome Web Store + + + Chrome Web Store Users + + + Chrome Web Store Rating + +

+ +
+
+ 🇷🇺   ВЕРСИ РUSIA   •   🇫🇷   VERSI PRANCIS   •   👩‍💻 README Pengembang +
+
+
+ +

+ +

+ +##

✨ Fitur Utama

+ +### 🔍 Visualisasi State + +- **Beberapa Tampilan**: Visualisasikan state aplikasi Anda melalui Component Graphs, JSON Trees, Performance Graphs, dan Accessibility Trees +- **History Timeline**: Lacak perubahan state seiring waktu melalui tampilan riwayat yang intuitif +- **Web Metrics**: Pantau metrik kinerja penting secara real-time +- **Insight Aksesibilitas**: Analisis accessibility tree aplikasi Anda pada setiap perubahan state +
+ +

Pada halaman utama, terdapat dua pilihan utama di panel dropdown:

+ +- **Timejump**: Melihat dan menavigasi riwayat snapshot state aplikasi Anda. Anda dapat melompat ke titik mana pun dalam waktu untuk melihat bagaimana state berubah di setiap perubahan. Anda juga dapat menggunakan tombol play untuk memutar ulang setiap perubahan state secara otomatis. +- **Providers / Consumers**: Memahami dependensi context aplikasi Anda dan bagaimana elemen-elemen tersebut berinteraksi, melalui visualisasi hubungan provider dan consumer. +
+ +

+ +

+
+ +### ⏱️ Debugging Lintas-Waktu + +- **Snapshot State**: Tangkap dan navigasi riwayat state aplikasi Anda +- **Playback Controls**: Putar ulang perubahan state secara otomatis dengan kecepatan yang dapat disesuaikan +- **Jump Points**: Langsung menuju state yang sebelumnya +- **Perbandingan Diff**: Bandingkan state antar snapshot +
+ +

+ +

+
+ +### 📊 Analisis Kinerja + +- **Metrik Komponen**: Lacak waktu render dan potensi bottleneck pada kinerja +- **Perbandingan Seri**: Bandingkan kinerja di berbagai set perubahan state +- **Deteksi Re-render**: Identifikasi dan perbaiki siklus render yang tidak diperlukan +- **Web Vitals**: Pantau Core Web Vitals dan metrik kinerja lainnya +
+
+ +### 🔄 Dukungan Framework Modern + + +
+ +### 💾 Persistensi & Berbagi State + +Reactime memudahkan proses menyimpan dan berbagi riwayat state aplikasi Anda: + +- **Ekspor Riwayat State**: Simpan snapshot yang direkam sebagai berkas JSON untuk analisis lebih lanjut atau dibagikan +- **Impor Sesi Sebelumnya**: Unggah snapshot yang telah disimpan untuk membandingkan perubahan state di sesi yang berbeda +- **Analisis Lintas-Sesi**: Bandingkan kinerja dan perubahan state antara berbagai sesi pengembangan +
+ +

+ +

+
+ +### 📚 Dokumentasi Interaktif + +Reactime menyediakan dokumentasi komprehensif untuk membantu pengembang memahami arsitektur dan API-nya: +Setelah melakukan clone pada repositori ini, pengembang dapat menjalankan `npm run docs` di +level root, dan menyajikan `/docs/index.html` yang dihasilkan secara dinamis, yang menyediakan: + + +
+ +

🎉 Apa yang Baru!

+ +Reactime 26.0 menghadirkan perombakan total pada pengalaman debugging React, dengan fitur-fitur: + +- **Tampilan Data Context Baru** + - Visualisasi pertama untuk perubahan state hook useContext + - Pemetaan jelas hubungan provider-consumer + - Pemantauan nilai state context secara real-time + - Visualisasi data provider yang terperinci + +- **Debugging Lintas-Waktu yang Ditingkatkan** + - Antarmuka slider yang didesain ulang, ditempatkan di samping snapshot + - Kontrol kecepatan pemutaran yang bervariasi + - Navigasi state yang lebih intuitif + - Visualisasi snapshot yang lebih baik + +- **Perombakan UI Modern** + - Desain yang lebih segar dengan komponen membulat + - Peningkatan tata letak yang intuitif + - Dukungan mode gelap baru + - Hierarki visual yang diperbarui + +- **Peningkatan Teknis Besar** + - Mengatasi masalah koneksi yang terputus saat idle dan perpindahan tab + - Mengembalikan visualisasi accessibility tree + - Memperbaiki masalah penangkapan state pada useState berbasis fungsi + - Meningkatkan keandalan dan kinerja ekstensi secara keseluruhan + +Pembaruan ini membuat Reactime lebih kuat, andal, dan ramah pengguna dari sebelumnya, menetapkan standar baru bagi alat debugging React. +
+
+ +

🚀 Memulai

+ +### Instalasi + +1. Pasang [Ekstensi Reactime](https://chrome.google.com/webstore/detail/reactime/cgibknllccemdnfhfpmjhffpjfeidjga) dari Chrome Web Store +2. Pasang ekstensi [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) yang diperlukan jika Anda belum memasangnya + +### Prasyarat + +- Aplikasi React Anda harus berjalan dalam **mode development** +- Ekstensi React Developer Tools harus terpasang +- Peramban Chrome (disarankan versi 80 atau lebih tinggi) + +### Menjalankan Reactime + +Ada dua cara untuk membuka panel Reactime: + +1. **DevTools** + - Buka Chrome DevTools (F12 atau ⌘+⌥+I) + - Buka tab "Reactime" + - Ini akan membuka Reactime sebagai panel di dalam Chrome DevTools, terintegrasi bersama alat pengembangan Anda yang lain + +2. **Context Menu** + - Klik kanan di mana saja pada aplikasi React Anda + - Pilih "Reactime" dari context menu + - Ini akan membuka Reactime di jendela popup terpisah yang dapat Anda ubah ukuran dan posisinya secara mandiri + +Setelah diluncurkan, Reactime secara otomatis akan mulai memantau perubahan state dan metrik kinerja aplikasi Anda. +
+
+ +

🤝 Berkontribusi di Reactime

+ +Kami menyambut kontribusi dari pengembang di semua tingkatan! Untuk panduan terperinci tentang cara berkontribusi: + +1. **Mulai** + - Fork repositori + - Pelajari README Pengembang kami yang komprehensif + - Siapkan lingkungan pengembangan lokal Anda + +2. **Proses Build** + - Ikuti petunjuk build di README Pengembang kami + - Uji perubahan Anda secara menyeluruh + - Ajukan pull request + +Bergabunglah dengan komunitas kontributor kami yang terus berkembang dan bantu membentuk masa depan alat debugging React! Untuk panduan kontribusi dan informasi arsitektur proyek yang lebih rinci, silakan lihat 👩‍💻 README Pengembang dan 🙋 Contributing README. +
+
+ +

🛠️ Pemecahan Masalah

+ +### ❓ Mengapa Reactime tidak merekam perubahan state baru? + +Reactime kehilangan koneksi dengan tab yang Anda pantau. Cukup klik tombol "reconnect" untuk melanjutkan pekerjaan Anda. + +### ❓ Mengapa Reactime tidak menemukan hooks saya? + +Reactime mendeteksi dan memantau hooks dengan menelusuri kode React Anda yang belum di-minify di mode development. Jika proses build Anda meminifikasi atau meng-uglify kode—even untuk build development—Reactime mungkin tidak bisa mendeteksi dan melacak hooks Anda dengan benar. Untuk memperbaikinya: + +1. **Pastikan build benar-benar development**: Periksa konfigurasi bundler atau build tool Anda (misalnya, Webpack, Babel, Vite, dll.) untuk memastikan aplikasi tidak di-minify atau di-uglify dalam mode development. + - Misalnya, dengan Webpack, pastikan Anda menjalankan mode: 'development' yang menonaktifkan minification default. + - Pada proyek Create React App, cukup jalankan npm start atau yarn start yang secara otomatis mengonfigurasi build development yang tidak di-minify. +2. **Periksa override**: Pastikan tidak ada plugin Babel atau Webpack kustom yang meminifikasi kode Anda, terutama jika Anda menggunakan framework seperti Next.js atau Gatsby. Terkadang ada plugin atau skrip tambahan yang berjalan di belakang layar. +3. **Restart & rebuild**: Setelah mengubah konfigurasi build, lakukan rebuild atau restart server development Anda agar konfigurasi baru diterapkan. Lalu segarkan tab peramban Anda agar Reactime dapat mendeteksi hooks yang tidak di-minify. + +Setelah mengubah konfigurasi build, rebuild atau restart server pengembangan Anda agar konfigurasi baru diterapkan. Kemudian, refresh tab peramban sehingga Reactime dapat mendeteksi hooks Anda yang tidak di-minify. + +### ❓ Mengapa Reactime mengatakan bahwa tidak ada aplikasi React yang ditemukan? + +Reactime awalnya dijalankan menggunakan dev tools global hook dari API Chrome. Proses ini memerlukan waktu untuk dimuat. Coba refresh aplikasi Anda beberapa kali hingga Reactime muncul. + +### ❓ Mengapa saya harus mengaktifkan React Dev Tools? + +Reactime bekerja berdampingan dengan React Developer Tools untuk mengakses Fiber tree aplikasi React; di balik layar, Reactime menjelajahi Fiber tree melalui global hook dari React Developer Tools, dan mengambil semua informasi relevan yang diperlukan untuk ditampilkan kepada pengembang. + +### ❓ Saya menemukan bug di Reactime + +Reactime adalah proyek open-source, dan kami ingin mendengar masukan Anda untuk meningkatkan pengalaman pengguna. Silakan baca 👩‍💻 README Pengembang, +dan buat pull request (atau issue) untuk mengusulkan dan berkolaborasi dalam perubahan pada Reactime. + +### ❓ Kecocokan versi Node + +Dengan rilis Node v18.12.1 (LTS) pada 4/11/22, skrip telah diperbarui menjadi +'npm run dev' | 'npm run build' untuk menjaga kompatibilitas mundur. +Untuk versi Node v16.16.0, silakan gunakan skrip 'npm run devlegacy' | 'npm run buildlegacy' +
+
+ +

✍️ Penulis

+ +- **Garrett Chow** - [@garrettlchow](https://github.com/garrettlchow) +- **Ellie Simens** - [@elliesimens](https://github.com/elliesimens) +- **Ragad Mohammed** - [@ragad-mohammed](https://github.com/ragad-mohammed) +- **Daniel Ryczek** - [@dryczek14](https://github.com/dryczek01) +- **Patrice Pinardo** - [@pinardo88](https://github.com/pinardo88) +- **Haider Ali** - [@hali03](https://github.com/hali03) +- **Jose Luis Sanchez** - [@JoseSanchez1996](https://github.com/JoseSanchez1996) +- **Logan Nelsen** - [@ljn16](https://github.com/ljn16) +- **Mel Koppens** - [@MelKoppens](https://github.com/MelKoppens) +- **Amy Yang** - [@ay7991](https://github.com/ay7991) +- **Eva Ury** - [@evaSUry](https://github.com/evaSUry) +- **Jesse Guerrero** - [@jguerrero35](https://github.com/jguerrero35) +- **Oliver Cho** - [@Oliver-Cho](https://github.com/Oliver-Cho) +- **Ben Margolius** - [@benmarg](https://github.com/benmarg) +- **Eric Yun** - [@ericsngyun](https://github.com/ericsngyun) +- **James Nghiem** - [@jemzir](https://github.com/jemzir) +- **Wilton Lee** - [@wiltonlee948](https://github.com/wiltonlee948) +- **Louis Lam** - [@llam722](https://github.com/llam722) +- **Samuel Tran** - [@leumastr](https://github.com/leumastr) +- **Brian Yang** - [@yangbrian310](https://github.com/yangbrian310) +- **Emin Tahirov** - [@eminthrv](https://github.com/eminthrv) +- **Peng Dong** - [@d28601581](https://github.com/d28601581) +- **Ozair Ghulam** - [@ozairgh](https://github.com/ozairgh) +- **Christina Or** - [@christinaor](https://github.com/christinaor) +- **Khanh Bui** - [@AndyB909](https://github.com/AndyB909) +- **David Kim** - [@codejunkie7](https://github.com/codejunkie7) +- **Robby Tipton** - [@RobbyTipton](https://github.com/RobbyTipton) +- **Kevin HoEun Lee** - [@khobread](https://github.com/khobread) +- **Christopher LeBrett** - [@fscgolden](https://github.com/fscgolden) +- **Joseph Park** - [@joeepark](https://github.com/joeepark) +- **Kris Sorensen** - [@kris-sorensen](https://github.com/kris-sorensen) +- **Daljit Gill** - [@dgill05](https://github.com/dgill05) +- **Ben Michareune** - [@bmichare](https://github.com/bmichare) +- **Dane Corpion** - [@danecorpion](https://github.com/danecorpion) +- **Harry Fox** - [@StackOverFlowWhereArtThou](https://github.com/StackOverFlowWhereArtThou) +- **Nathan Richardson** - [@BagelEnthusiast](https://github.com/BagelEnthusiast) +- **David Bernstein** - [@dangitbobbeh](https://github.com/dangitbobbeh) +- **Joseph Stern** - [@josephiswhere](https://github.com/josephiswhere) +- **Dennis Lopez** - [@DennisLpz](https://github.com/DennisLpz) +- **Cole Styron** - [@C-STYR](https://github.com/C-STYR) +- **Ali Rahman** - [@CourageWolf](https://github.com/CourageWolf) +- **Caner Demir** - [@demircaner](https://github.com/demircaner) +- **Kevin Ngo** - [@kev-ngo](https://github.com/kev-ngo) +- **Becca Viner** - [@rtviner](https://github.com/rtviner) +- **Caitlin Chan** - [@caitlinchan23](https://github.com/caitlinchan23) +- **Kim Mai Nguyen** - [@Nkmai](https://github.com/Nkmai) +- **Tania Lind** - [@lind-tania](https://github.com/lind-tania) +- **Alex Landeros** - [@AlexanderLanderos](https://github.com/AlexanderLanderos) +- **Chris Guizzetti** - [@guizzettic](https://github.com/guizzettic) +- **Jason Victor** - [@Theqwertypusher](https://github.com/Theqwertypusher) +- **Sanjay Lavingia** - [@sanjaylavingia](https://github.com/sanjaylavingia) +- **Vincent Nguyen** - [@VNguyenCode](https://github.com/VNguyenCode) +- **Haejin Jo** - [@haejinjo](https://github.com/haejinjo) +- **Hien Nguyen** - [@hienqn](https://github.com/hienqn) +- **Jack Crish** - [@JackC27](https://github.com/JackC27) +- **Kevin Fey** - [@kevinfey](https://github.com/kevinfey) +- **Carlos Perez** - [@crperezt](https://github.com/crperezt) +- **Edwin Menendez** - [@edwinjmenendez](https://github.com/edwinjmenendez) +- **Gabriela Jardim Aquino** - [@aquinojardim](https://github.com/aquinojardim) +- **Greg Panciera** - [@gpanciera](https://github.com/gpanciera) +- **Nathanael Wa Mwenze** - [@nmwenz90](https://github.com/nmwenz90) +- **Ryan Dang** - [@rydang](https://github.com/rydang) +- **Bryan Lee** - [@mylee1995](https://github.com/mylee1995) +- **Josh Kim** - [@joshua0308](https://github.com/joshua0308) +- **Sierra Swaby** - [@starkspark](https://github.com/starkspark) +- **Ruth Anam** - [@nusanam](https://github.com/nusanam) +- **David Chai** - [@davidchai717](https://github.com/davidchai717) +- **Yujin Kang** - [@yujinkay](https://github.com/yujinkay) +- **Andy Wong** - [@andynullwong](https://github.com/andynullwong) +- **Chris Flannery** - [@chriswillsflannery](https://github.com/chriswillsflannery) +- **Rajeeb Banstola** - [@rajeebthegreat](https://github.com/rajeebthegreat) +- **Prasanna Malla** - [@prasmalla](https://github.com/prasmalla) +- **Rocky Lin** - [@rocky9413](https://github.com/rocky9413) +- **Abaas Khorrami** - [@dubalol](https://github.com/dubalol) +- **Ergi Shehu** - [@ergi516](https://github.com/ergi516) +- **Raymond Kwan** - [@rkwn](https://github.com/rkwn) +- **Joshua Howard** - [@joshua-howard](https://github.com/joshua-howard) +- **Lina Shin** - [@rxlina](https://github.com/rxlina) +- **Andy Tsou** - [@andytsou19](https://github.com/andytsou19) +- **Feiyi Wu** - [@FreyaWu](https://github.com/FreyaWu) +- **Viet Nguyen** - [@vnguyen95](https://github.com/vnguyen95) +- **Alex Gomez** - [@alexgomez9](https://github.com/alexgomez9) +- **Edar Liu** - [@liuedar](https://github.com/liuedar) +- **Kristina Wallen** - [@kristinawallen](https://github.com/kristinawallen) +- **Quan Le** - [@Blachfog](https://github.com/Blachfog) +- **Robert Maeda** - [@robmaeda](https://github.com/robmaeda) +- **Lance Ziegler** - [@lanceziegler](https://github.com/lanceziegler) +- **Ngoc Zwolinski** - [@ngoczwolinski](https://github.com/ngoczwolinski) +- **Peter Lam** - [@dev-plam](https://github.com/dev-plam) +- **Zachary Freeman** - [@zacharydfreeman](https://github.com/zacharydfreeman/) +- **Jackie Yuan** - [@yuanjackie1](https://github.com/yuanjackie1) +- **Jasmine Noor** - [@jasnoo](https://github.com/jasnoo) +- **Minzo Kim** - [@minzo-kim](https://github.com/minzo-kim) +- **Mark Teets** - [@MarkTeets](https://github.com/MarkTeets) +- **Nick Huemmer** - [@ElDuke717](https://github.com/ElDuke717) +- **James McCollough** - [@j-mccoll](https://github.com/j-mccoll) +- **Mike Bednarz** - [@mikebednarz](https://github.com/mikebednarz) +- **Sergei Liubchenko** - [@sergeylvq](https://github.com/sergeylvq) +- **Yididia Ketema** - [@yididiaketema](https://github.com/yididiaketema) +- **Morah Geist** - [@morahgeist](https://github.com/morahgeist) +- **Eivind Del Fierro** - [@EivindDelFierro](https://github.com/EivindDelFierro) +- **Kyle Bell** - [@KyEBell](https://github.com/KyEBell) +- **Sean Kelly** - [@brok3turtl3](https://github.com/brok3turtl3) +- **Christopher Stamper** - [@ctstamper](https://github.com/ctstamper) +- **Jimmy Phy** - [@jimmally](https://github.com/jimmally) +- **Andrew Byun** - [@AndrewByun](https://github.com/AndrewByun) +- **Kelvin Mirhan** - [@kelvinmirhan](https://github.com/kelvinmirhan) +- **Jesse Rosengrant** - [@jrosengrant](https://github.com/jrosengrant) +- **Liam Donaher** - [@leebology](https://github.com/leebology) +- **David Moore** - [@Solodt55](https://github.com/Solodt55) +- **John Banks** - [@Jbanks123](https://github.com/Jbanks123) +
+ +

⚖️ Lisensi

+ +Proyek ini dilisensikan di bawah MIT License - lihat berkas [LICENSE](LICENSE) untuk detail selengkapnya. diff --git a/README.md b/README.md new file mode 100644 index 000000000..c9ac28701 --- /dev/null +++ b/README.md @@ -0,0 +1,384 @@ +

+ +
+ React Performance Tool +
+ 🏆 Nominated for React Open Source Awards 2020 +
+

+ +

A powerful Chrome extension that enhances React development with time-travel debugging and advanced performance monitoring

+ +

Read our Medium Article to learn more about Reactime’s behind-the-scenes and development process!

+
+ +

+ + Chrome Web Store + + + Chrome Web Store Users + + + Chrome Web Store Rating + +

+ +
+
+ 🇷🇺   РУССКАЯ ВЕРСИЯ   •   🇫🇷   VERSION FRANÇAISE   •   🇮🇩   VERSI BAHASA INDONESIA   •   👩‍💻 Developer README +
+
+
+ +

+ +

+ +##

✨ Key Features

+ +### 🔍 State Visualization + +- **Multiple Views**: Visualize your application state through Component Graphs, JSON Trees, Performance Graphs, and Accessibility Trees +- **History Timeline**: Track state changes over time with an intuitive history visualization +- **Web Metrics**: Monitor critical performance metrics in real-time +- **Accessibility Insights**: Analyze your app's accessibility tree for each state change +
+ +

On the main page, there are two main selections from the dropdown panel:

+ +- **Timejump**: View and navigate through the snapshot history of your application's state. You can jump to any point in time to see how the state evolves across changes. You can also use the play button to replay each state change automatically. +- **Providers / Consumers**: Understand your application's context dependencies and their interactions better through visualizing its provider and consumer relationships. +
+ +

+ +

+
+ +### ⏱️ Time-Travel Debugging + +- **State Snapshots**: Capture and navigate through your application's state history +- **Playback Controls**: Automatically replay state changes with adjustable speed +- **Jump Points**: Instantly navigate to any previous state +- **Diff Comparisons**: Compare states between snapshots +
+ +

+ +

+
+ +### 📊 Performance Analysis + +- **Component Metrics**: Track render times and performance bottlenecks +- **Series Comparison**: Compare performance across different sets of state changes +- **Re-render Detection**: Identify and fix unnecessary render cycles +- **Web Vitals**: Monitor Core Web Vitals and other performance metrics +
+
+ +### 🔄 Modern Framework Support + + +
+ +### 💾 State Persistence & Sharing + +Reactime makes it easy to save and share your application's state history: + +- **Export State History**: Save your recorded snapshots as a JSON file for later analysis or sharing +- **Import Previous Sessions**: Upload previously saved snapshots to compare state changes across different sessions +- **Cross-Session Analysis**: Compare performance and state changes between different development sessions +
+ +

+ +

+
+ +### 📚 Interactive Documentation + +Reactime provides comprehensive documentation to help developers understand its architecture and APIs: +After cloning this repository, developers can simply run `npm run docs` at the +root level and serve the dynamically generated `/docs/index.html` provding: + + +
+ +

🎉 What's New!

+ +Reactime 26.0 brings a complete overhaul to the React debugging experience, featuring: + +- **New Context Data Display** + + - First-ever visualization of useContext hook state changes + - Clear mapping of provider-consumer relationships + - Real-time context state value monitoring + - Detailed provider data visualization + +- **Enhanced Time Travel Debugging** + + - Redesigned slider interface positioned alongside snapshots + - Variable playback speed controls + - More intuitive state navigation + - Improved snapshot visualization + +- **Modern UI Overhaul** + + - Sleek, contemporary design with rounded components + - Intuitive layout improvements + - New dark mode support + - Enhanced visual hierarchy + +- **Major Technical Improvements** + - Fixed connection persistence during idle time and tab switches + - Restored accessibility tree visualization + - Resolved state capture issues for function-based useState hooks + - Improved overall extension reliability and performance + +These updates make Reactime more powerful, reliable, and user-friendly than ever before, setting a new standard for React debugging tools. +
+
+ +

🚀 Getting Started

+ +### Installation + +1. Install the [Reactime extension](https://chrome.google.com/webstore/detail/reactime/cgibknllccemdnfhfpmjhffpjfeidjga) from the Chrome Web Store +2. Install the required [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) extension if you haven't already + +### Prerequisites + +- Your React application must be running in **development mode** +- React Developer Tools extension must be installed +- Chrome browser (version 80 or higher recommended) + +### Launch Reactime + +There are two ways to open the Reactime panel: + +1. **DevTools** + + - Open Chrome DevTools (F12 or ⌘+⌥+I) + - Navigate to the "Reactime" tab + - This will open Reactime as a panel within Chrome DevTools, integrated alongside your other development tools + +2. **Context Menu** + + - Right-click anywhere on your React application + - Select "Reactime" from the context menu + - This will open Reactime in a separate popup window which you can resize and position independently + +Once launched, Reactime will automatically begin monitoring your application's state changes and performance metrics. +
+
+ +

🤝 Contributing to Reactime

+ +We welcome contributions from developers of all skill levels! For detailed guidelines on how to contribute: + +1. **Get Started** + + - Fork the repository + - Review our comprehensive Developer README + - Set up your local development environment + +2. **Build Process** + + - Follow our build instructions in the Developer README + - Test your changes thoroughly + - Submit a pull request + +Join our growing community of contributors and help shape the future of React debugging tools! For detailed contribution guidelines and project architecture information, please refer to our 👩‍💻 Developer README and 🙋 Contributing README +
+
+ +

🛠️ Troubleshooting

+ +### ❓ Why is Reactime not recording new state changes? + +Reactime lost its connection to the tab you're monitoring, simply click the "reconnect" button to resume your work. + +### ❓ Why isn’t Reactime finding my hooks? + +Reactime detects and monitors hooks by traversing your application’s unminified React code in development mode. If your build process is minifying or uglifying your code—even for development builds—Reactime may not be able to properly locate and track your hooks. To fix this: + +1. **Ensure a true development build**: Double-check your bundler or build tool configuration (e.g., Webpack, Babel, Vite, etc.) to make sure that your application is not minimized or uglified in development mode. + + - For example, with Webpack, make sure you’re running in mode: 'development', which should disable default minification. + - In a Create React App project, simply running npm start or yarn start will automatically configure a non-minified development build. + +2. **Check for overrides**: Ensure there are no custom Babel or Webpack plugins that minify your code, especially if you’re using frameworks like Next.js or Gatsby. Sometimes additional plugins or scripts might be running under the hood. + +3. **Restart & rebuild**: After changing any build configuration, rebuild or restart your development server to ensure the new configuration is applied. Then refresh your browser tab so Reactime can detect your unminified hooks. + +After changing any build configuration, rebuild or restart your development server to ensure the new configuration is applied. Then refresh your browser tab so Reactime can detect your unminified hooks. + +### ❓ Why is Reactime telling me that no React application is found? + +Reactime initially runs using the dev tools global hook from the Chrome API. It +takes time for Chrome to load this. Try refreshing your application a couple of +times until you see Reactime running. + +### ❓ Why do I need to have React Dev Tools enabled? + +Reactime works in tandem with the React Developer Tools to access a React application's Fiber tree; under the hood, Reactime traverses the Fiber tree through the React Developer Tool's global hook, pulling all relevant information needed to display to the developer + +### ❓ I found a bug in Reactime + +Reactime is an open-source project, and we'd love to hear from you about +improving the user experience. Please read the 👩‍💻 Developer README, +and create a pull request (or issue) to propose and collaborate on changes to Reactime. + +### ❓ Node version compatibility + +With the release of Node v18.12.1(LTS) on 11/4/22, the script has been updated to +'npm run dev' | 'npm run build' for backwards compatibility.
For version +Node v16.16.0, please use script 'npm run devlegacy' | 'npm run buildlegacy' +
+
+ +

✍️ Authors

+ +- **Garrett Chow** - [@garrettlchow](https://github.com/garrettlchow) +- **Ellie Simens** - [@elliesimens](https://github.com/elliesimens) +- **Ragad Mohammed** - [@ragad-mohammed](https://github.com/ragad-mohammed) +- **Daniel Ryczek** - [@dryczek14](https://github.com/dryczek01) +- **Patrice Pinardo** - [@pinardo88](https://github.com/pinardo88) +- **Haider Ali** - [@hali03](https://github.com/hali03) +- **Jose Luis Sanchez** - [@JoseSanchez1996](https://github.com/JoseSanchez1996) +- **Logan Nelsen** - [@ljn16](https://github.com/ljn16) +- **Mel Koppens** - [@MelKoppens](https://github.com/MelKoppens) +- **Amy Yang** - [@ay7991](https://github.com/ay7991) +- **Eva Ury** - [@evaSUry](https://github.com/evaSUry) +- **Jesse Guerrero** - [@jguerrero35](https://github.com/jguerrero35) +- **Oliver Cho** - [@Oliver-Cho](https://github.com/Oliver-Cho) +- **Ben Margolius** - [@benmarg](https://github.com/benmarg) +- **Eric Yun** - [@ericsngyun](https://github.com/ericsngyun) +- **James Nghiem** - [@jemzir](https://github.com/jemzir) +- **Wilton Lee** - [@wiltonlee948](https://github.com/wiltonlee948) +- **Louis Lam** - [@llam722](https://github.com/llam722) +- **Samuel Tran** - [@leumastr](https://github.com/leumastr) +- **Brian Yang** - [@yangbrian310](https://github.com/yangbrian310) +- **Emin Tahirov** - [@eminthrv](https://github.com/eminthrv) +- **Peng Dong** - [@d28601581](https://github.com/d28601581) +- **Ozair Ghulam** - [@ozairgh](https://github.com/ozairgh) +- **Christina Or** - [@christinaor](https://github.com/christinaor) +- **Khanh Bui** - [@AndyB909](https://github.com/AndyB909) +- **David Kim** - [@codejunkie7](https://github.com/codejunkie7) +- **Robby Tipton** - [@RobbyTipton](https://github.com/RobbyTipton) +- **Kevin HoEun Lee** - [@khobread](https://github.com/khobread) +- **Christopher LeBrett** - [@fscgolden](https://github.com/fscgolden) +- **Joseph Park** - [@joeepark](https://github.com/joeepark) +- **Kris Sorensen** - [@kris-sorensen](https://github.com/kris-sorensen) +- **Daljit Gill** - [@dgill05](https://github.com/dgill05) +- **Ben Michareune** - [@bmichare](https://github.com/bmichare) +- **Dane Corpion** - [@danecorpion](https://github.com/danecorpion) +- **Harry Fox** - + [@StackOverFlowWhereArtThou](https://github.com/StackOverFlowWhereArtThou) +- **Nathan Richardson** - [@BagelEnthusiast](https://github.com/BagelEnthusiast) +- **David Bernstein** - [@dangitbobbeh](https://github.com/dangitbobbeh) +- **Joseph Stern** - [@josephiswhere](https://github.com/josephiswhere) +- **Dennis Lopez** - [@DennisLpz](https://github.com/DennisLpz) +- **Cole Styron** - [@colestyron](https://github.com/C-STYR) +- **Ali Rahman** - [@CourageWolf](https://github.com/CourageWolf) +- **Caner Demir** - [@demircaner](https://github.com/demircaner) +- **Kevin Ngo** - [@kev-ngo](https://github.com/kev-ngo) +- **Becca Viner** - [@rtviner](https://github.com/rtviner) +- **Caitlin Chan** - [@caitlinchan23](https://github.com/caitlinchan23) +- **Kim Mai Nguyen** - [@Nkmai](https://github.com/Nkmai) +- **Tania Lind** - [@lind-tania](https://github.com/lind-tania) +- **Alex Landeros** - [@AlexanderLanderos](https://github.com/AlexanderLanderos) +- **Chris Guizzetti** - [@guizzettic](https://github.com/guizzettic) +- **Jason Victor** - [@theqwertypusher](https://github.com/Theqwertypusher) +- **Sanjay Lavingia** - [@sanjaylavingia](https://github.com/sanjaylavingia) +- **Vincent Nguyen** - [@VNguyenCode](https://github.com/VNguyenCode) +- **Haejin Jo** - [@haejinjo](https://github.com/haejinjo) +- **Hien Nguyen** - [@hienqn](https://github.com/hienqn) +- **Jack Crish** - [@JackC27](https://github.com/JackC27) +- **Kevin Fey** - [@kevinfey](https://github.com/kevinfey) +- **Carlos Perez** - [@crperezt](https://github.com/crperezt) +- **Edwin Menendez** - [@edwinjmenendez](https://github.com/edwinjmenendez) +- **Gabriela Jardim Aquino** - [@aquinojardim](https://github.com/aquinojardim) +- **Greg Panciera** - [@gpanciera](https://github.com/gpanciera) +- **Nathanael Wa Mwenze** - [@nmwenz90](https://github.com/nmwenz90) +- **Ryan Dang** - [@rydang](https://github.com/rydang) +- **Bryan Lee** - [@mylee1995](https://github.com/mylee1995) +- **Josh Kim** - [@joshua0308](https://github.com/joshua0308) +- **Sierra Swaby** - [@starkspark](https://github.com/starkspark) +- **Ruth Anam** - [@nusanam](https://github.com/nusanam) +- **David Chai** - [@davidchaidev](https://github.com/davidchai717) +- **Yujin Kang** - [@yujinkay](https://github.com/yujinkay) +- **Andy Wong** - [@andynullwong](https://github.com/andynullwong) +- **Chris Flannery** - + [@chriswillsflannery](https://github.com/chriswillsflannery) +- **Rajeeb Banstola** - [@rajeebthegreat](https://github.com/rajeebthegreat) +- **Prasanna Malla** - [@prasmalla](https://github.com/prasmalla) +- **Rocky Lin** - [@rocky9413](https://github.com/rocky9413) +- **Abaas Khorrami** - [@dubalol](https://github.com/dubalol) +- **Ergi Shehu** - [@Ergi516](https://github.com/ergi516) +- **Raymond Kwan** - [@rkwn](https://github.com/rkwn) +- **Joshua Howard** - [@Joshua-Howard](https://github.com/joshua-howard) +- **Lina Shin** - [@rxlina](https://github.com/rxlina) +- **Andy Tsou** - [@andytsou19](https://github.com/andytsou19) +- **Feiyi Wu** - [@FreyaWu](https://github.com/FreyaWu) +- **Viet Nguyen** - [@vnguyen95](https://github.com/vnguyen95) +- **Alex Gomez** - [@alexgomez9](https://github.com/alexgomez9) +- **Edar Liu** - [@liuedar](https://github.com/liuedar) +- **Kristina Wallen** - [@kristinawallen](https://github.com/kristinawallen) +- **Quan Le** - [@blachfog](https://github.com/Blachfog) +- **Robert Maeda** - [@robmaeda](https://github.com/robmaeda) +- **Lance Ziegler** - [@lanceziegler](https://github.com/lanceziegler) +- **Ngoc Zwolinski** - [@ngoczwolinski](https://github.com/ngoczwolinski) +- **Peter Lam** - [@dev-plam](https://github.com/dev-plam) +- **Zachary Freeman** - [@zacharydfreeman](https://github.com/zacharydfreeman/) +- **Jackie Yuan** - [@yuanjackie1](https://github.com/yuanjackie1) +- **Jasmine Noor** - [@jasnoo](https://github.com/jasnoo) +- **Minzo Kim** - [@minzo-kim](https://github.com/minzo-kim) +- **Mark Teets** - [@MarkTeets](https://github.com/MarkTeets) +- **Nick Huemmer** - [@NickHuemmer](https://github.com/ElDuke717) +- **James McCollough** - [@j-mccoll](https://github.com/j-mccoll) +- **Mike Bednarz** - [@mikebednarz](https://github.com/mikebednarz) +- **Sergei Liubchenko** - [@sergeylvq](https://github.com/sergeylvq) +- **Yididia Ketema** - [@yididiaketema](https://github.com/yididiaketema) +- **Morah Geist** - [@morahgeist](https://github.com/morahgeist) +- **Eivind Del Fierro** - [@EivindDelFierro](https://github.com/EivindDelFierro) +- **Kyle Bell** - [@KyEBell](https://github.com/KyEBell) +- **Sean Kelly** - [@brok3turtl3](https://github.com/brok3turtl3) +- **Christopher Stamper** - [@ctstamper](https://github.com/ctstamper) +- **Jimmy Phy** - [@jimmally](https://github.com/jimmally) +- **Andrew Byun** - [@AndrewByun](https://github.com/AndrewByun) +- **Kelvin Mirhan** - [@kelvinmirhan](https://github.com/kelvinmirhan) +- **Jesse Rosengrant** - [@jrosengrant](https://github.com/jrosengrant) +- **Liam Donaher** - [@leebology](https://github.com/leebology) +- **David Moore** - [@Solodt55](https://github.com/Solodt55) +- **John Banks** - [@Jbanks123](https://github.com/Jbanks123) +
+ +

⚖️ License

+ +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/README.rus.md b/README.rus.md new file mode 100644 index 000000000..508f69a1a --- /dev/null +++ b/README.rus.md @@ -0,0 +1,366 @@ +

+ + Инструмент для анализа производительности React +
+ 🏆 Номинант на React Open Source Awards 2020 +
+

+ +

Мощное расширение Chrome, которое улучшает процесс разработки React за счёт отладки с путешествиями во времени и углублённого мониторинга производительности

+
+ +

+ + Chrome Web Store + + + Chrome Web Store Users + + + Chrome Web Store Rating + +

+ +
+
+ 🇺🇸   ENGLISH VERSION   •   🇫🇷   VERSION FRANÇAISE   •   🇮🇩   VERSI BAHASA INDONESIA   •   👩‍💻 Руководство для разработчиков +
+
+
+ +

+ +

+ +##

✨ Ключевые особенности

+ +### 🔍 Визуализация состояния + +- **Разнообразные представления**: Отображение состояния приложения в виде графов компонентов, JSON-деревьев, графиков производительности и деревьев доступности +- **История изменений**: Отслеживайте изменения состояния во времени с удобной визуализацией истории +- **Метрики веб-приложения**: Отслеживайте важные метрики производительности в реальном времени +- **Аналитика доступности**: Анализируйте дерево доступности вашего приложения на каждом этапе изменения состояния +
+ +

На главном экране доступны два основных выбора из выпадающего списка:

+ +- **Timejump**: Просматривайте и перемещайтесь по истории снимков состояния (snapshot) вашего приложения. Можно переместиться в любую точку во времени, чтобы увидеть, как состояние эволюционировало при изменениях. Также доступна кнопка воспроизведения, чтобы автоматически проиграть каждое изменение состояния. +- **Providers / Consumers**: Глубже понимайте зависимости контекста приложения и его взаимодействия, визуализируя отношения провайдеров и потребителей. +
+ +

+ +

+
+ +### ⏱️ Отладка с путешествиями во времени + +- **Снимки состояния**: Фиксируйте и перемещайтесь по истории состояния приложения +- **Элементы управления воспроизведением**: Автоматически воспроизводите изменения состояния с регулировкой скорости +- **Точки мгновенного перехода**: Мгновенно возвращайтесь к любому предыдущему состоянию +- **Сравнение состояний**: Сравнивайте разницу между снимками состояния +
+ +

+ +

+
+ +### 📊 Анализ производительности + +- **Метрики компонентов**: Отслеживайте время рендера и узкие места в производительности +- **Сравнение серий**: Сопоставляйте производительность при разных наборах изменений состояния +- **Определение перерисовок**: Находите и исправляйте избыточные циклы рендера +- **Web Vitals**: Следите за Core Web Vitals и другими метриками +
+
+ +### 🔄 Поддержка современных фреймворков + + +
+ +### 💾 Сохранение и обмен состоянием + +Reactime упрощает сохранение и обмен историей состояния вашего приложения: + +- **Экспорт истории**: Сохраняйте записанные снимки в JSON-файл для дальнейшего анализа или передачи +- **Импорт предыдущих сессий**: Загружайте ранее сохранённые снимки, чтобы сравнивать изменения состояния между разными сессиями +- **Межсессионный анализ**: Сопоставляйте производительность и изменения состояния между разными этапами разработки +
+ +

+ +

+
+ +### 📚 Интерактивная документация + +Reactime предлагает обширную документацию, помогающую разработчикам разобраться в архитектуре и API инструмента. После клонирования репозитория достаточно запустить `npm run docs` в корневой директории, а затем открыть сгенерированный файл `/docs/index.html`, в котором представлены: + + +
+ +

🎉 Что нового!

+ +Версия Reactime 26.0 предлагает полное обновление инструмента отладки React, включая: + +- **Новый показ данных контекста** + + - Первая в своём роде визуализация состояния, основанного на хуке useContext + - Чёткое отображение отношений «провайдер – потребитель» + - Мониторинг значений контекста в реальном времени + - Подробная визуализация данных провайдеров + +- **Улучшенная отладка с путешествиями во времени** + + - Переработанный интерфейс слайдера, размещённого вместе со снимками + - Элементы управления скоростью воспроизведения + - Более интуитивная навигация по состояниям + - Улучшенная визуализация снимков + +- **Современный переработанный интерфейс** + + - Стильный, современный дизайн со скруглёнными элементами + - Интуитивная структура расположения элементов + - Новый тёмный режим + - Улучшенная визуальная иерархия + +- **Крупные технические улучшения** + - Исправлена проблема с сохранением соединения при бездействии или переключении вкладок + - Восстановлена визуализация дерева доступности (Accessibility Tree) + - Исправлены проблемы с захватом состояния для хуков useState в функциональных компонентах + - Общий рост стабильности и производительности расширения + +Благодаря этим обновлениям Reactime стал ещё более мощным, надёжным и удобным в использовании, устанавливая новый стандарт среди инструментов отладки React. + +Чтобы узнать больше о предыдущих релизах, перейдите по ссылке. +
+
+ +

🚀 Начало работы

+ +### Установка + +1. Установите [Reactime](https://chrome.google.com/webstore/detail/reactime/cgibknllccemdnfhfpmjhffpjfeidjga) из Интернет-магазина Chrome +2. Установите необходимое расширение [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en), если у вас его ещё нет + +### Предварительные требования + +- Ваше React-приложение должно работать в **режиме разработки (development)** +- Расширение React Developer Tools должно быть установлено +- Браузер Chrome (рекомендуется версия 80 или выше) + +### Запуск Reactime + +Существует два способа открыть панель Reactime: + +1. **Контекстное меню** + + - Кликните правой кнопкой мыши в любом месте вашего React-приложения + - Выберите «Reactime» в контекстном меню + +2. **Инструменты разработчика** + - Откройте Chrome DevTools (F12 или ⌘+⌥+I) + - Перейдите на вкладку «Reactime» + +После запуска Reactime автоматически начнёт отслеживать изменения состояния и метрики производительности вашего приложения. +
+
+ +

🤝 Участие в развитии Reactime

+ +Мы приглашаем всех желающих внести свой вклад в улучшение Reactime! Вот как вы можете помочь: 🙋 Contributing README + +1. **Начальный шаг** + + - Сделайте форк репозитория + - Изучите наше подробное Руководство для разработчиков (Developer README) + - Настройте локальную среду разработки + +2. **Процесс сборки** + - Следуйте инструкциям по сборке в Руководстве для разработчиков + - Тщательно протестируйте свои изменения + - Создайте пул-реквест + +Присоединяйтесь к нашему растущему сообществу контрибьюторов и помогите формировать будущее инструментов отладки React! Подробные инструкции по участию и архитектуре проекта вы найдёте в 👩‍💻 Руководстве для разработчиков. +
+
+ +

🛠️ Устранение неполадок

+ +### ❓ Почему Reactime не записывает новые изменения состояния? + +Reactime потерял соединение с вкладкой, за которой ведётся наблюдение. Просто нажмите кнопку «reconnect», чтобы возобновить работу. + +### ❓ Почему Reactime не может найти мои хуки? + +Reactime обнаруживает и отслеживает хуки, просматривая «неминифицированный» код React в режиме разработки. Если ваш процесс сборки минифицирует или «уродует» (uglify) код — даже в режиме разработки — Reactime может не найти и не отследить ваши хуки. Чтобы исправить это: + +1. **Убедитесь, что сборка действительно предназначена для разработки**: Проверьте настройки вашего бандлера или инструмента сборки (например, Webpack, Babel, Vite и т. д.), чтобы приложение не было минимизировано и «уродовано» в режиме разработки. + - Например, в Webpack установите `mode: 'development'`, чтобы по умолчанию отключить минификацию. + - В Create React App, достаточно запустить `npm start` или `yarn start`, чтобы автоматически настроить неминифицированную сборку. +2. **Проверьте, нет ли переопределений**: Убедитесь, что нет дополнительных Babel- или Webpack-плагинов, которые минифицируют ваш код, особенно если вы используете фреймворки вроде Next.js или Gatsby. Иногда дополнительные плагины или скрипты могут незаметно запускать минификацию. +3. **Перезапустите и пересоберите**: После изменения настроек сборки перезапустите или пересоберите ваше приложение, чтобы новые настройки были применены. Затем обновите вкладку браузера, чтобы Reactime мог обнаружить ваши неминифицированные хуки. + +### ❓ Почему Reactime говорит, что React-приложение не найдено? + +Reactime изначально запускается, используя глобальный хук dev tools из API Chrome. Чтобы Chrome успел это загрузить, может потребоваться время. Попробуйте обновить (refresh) ваше приложение несколько раз, пока не увидите, что Reactime заработал. + +### ❓ Почему мне нужно включать React Dev Tools? + +Reactime работает в связке с React Developer Tools, чтобы получить доступ к дереву Fiber в React-приложении. Внутри Reactime просматривает дерево Fiber через глобальный хук из React Developer Tools, получая всю необходимую информацию для отображения разработчику. + +### ❓ Я нашёл(ла) баг в Reactime + +Reactime — это проект с открытым исходным кодом. Мы будем рады услышать ваши идеи по улучшению пользовательского опыта. Ознакомьтесь с 👩‍💻 Руководством для разработчиков и создайте пул-реквест (или Issue), чтобы предложить и совместно доработать изменения в Reactime. + +### ❓ Совместимость с версиями Node + +С выходом Node v18.12.1 (LTS) от 04.11.22 в скриптах появились команды +`npm run dev` | `npm run build` для обратной совместимости.
Для версии +Node v16.16.0 используйте скрипты `npm run devlegacy` | `npm run buildlegacy` +
+
+ +

✍️ Авторы

+ +- **Garrett Chow** - [@garrettlchow](https://github.com/garrettlchow) +- **Ellie Simens** - [@elliesimens](https://github.com/elliesimens) +- **Ragad Mohammed** - [@ragad-mohammed](https://github.com/ragad-mohammed) +- **Daniel Ryczek** - [@dryczek14](https://github.com/dryczek01) +- **Patrice Pinardo** - [@pinardo88](https://github.com/pinardo88) +- **Haider Ali** - [@hali03](https://github.com/hali03) +- **Jose Luis Sanchez** - [@JoseSanchez1996](https://github.com/JoseSanchez1996) +- **Logan Nelsen** - [@ljn16](https://github.com/ljn16) +- **Mel Koppens** - [@MelKoppens](https://github.com/MelKoppens) +- **Amy Yang** - [@ay7991](https://github.com/ay7991) +- **Eva Ury** - [@evaSUry](https://github.com/evaSUry) +- **Jesse Guerrero** - [@jguerrero35](https://github.com/jguerrero35) +- **Oliver Cho** - [@Oliver-Cho](https://github.com/Oliver-Cho) +- **Ben Margolius** - [@benmarg](https://github.com/benmarg) +- **Eric Yun** - [@ericsngyun](https://github.com/ericsngyun) +- **James Nghiem** - [@jemzir](https://github.com/jemzir) +- **Wilton Lee** - [@wiltonlee948](https://github.com/wiltonlee948) +- **Louis Lam** - [@llam722](https://github.com/llam722) +- **Samuel Tran** - [@leumastr](https://github.com/leumastr) +- **Brian Yang** - [@yangbrian310](https://github.com/yangbrian310) +- **Emin Tahirov** - [@eminthrv](https://github.com/eminthrv) +- **Peng Dong** - [@d28601581](https://github.com/d28601581) +- **Ozair Ghulam** - [@ozairgh](https://github.com/ozairgh) +- **Christina Or** - [@christinaor](https://github.com/christinaor) +- **Khanh Bui** - [@AndyB909](https://github.com/AndyB909) +- **David Kim** - [@codejunkie7](https://github.com/codejunkie7) +- **Robby Tipton** - [@RobbyTipton](https://github.com/RobbyTipton) +- **Kevin HoEun Lee** - [@khobread](https://github.com/khobread) +- **Christopher LeBrett** - [@fscgolden](https://github.com/fscgolden) +- **Joseph Park** - [@joeepark](https://github.com/joeepark) +- **Kris Sorensen** - [@kris-sorensen](https://github.com/kris-sorensen) +- **Daljit Gill** - [@dgill05](https://github.com/dgill05) +- **Ben Michareune** - [@bmichare](https://github.com/bmichare) +- **Dane Corpion** - [@danecorpion](https://github.com/danecorpion) +- **Harry Fox** - [@StackOverFlowWhereArtThou](https://github.com/StackOverFlowWhereArtThou) +- **Nathan Richardson** - [@BagelEnthusiast](https://github.com/BagelEnthusiast) +- **David Bernstein** - [@dangitbobbeh](https://github.com/dangitbobbeh) +- **Joseph Stern** - [@josephiswhere](https://github.com/josephiswhere) +- **Dennis Lopez** - [@DennisLpz](https://github.com/DennisLpz) +- **Cole Styron** - [@colestyron](https://github.com/C-STYR) +- **Ali Rahman** - [@CourageWolf](https://github.com/CourageWolf) +- **Caner Demir** - [@demircaner](https://github.com/demircaner) +- **Kevin Ngo** - [@kev-ngo](https://github.com/kev-ngo) +- **Becca Viner** - [@rtviner](https://github.com/rtviner) +- **Caitlin Chan** - [@caitlinchan23](https://github.com/caitlinchan23) +- **Kim Mai Nguyen** - [@Nkmai](https://github.com/Nkmai) +- **Tania Lind** - [@lind-tania](https://github.com/lind-tania) +- **Alex Landeros** - [@AlexanderLanderos](https://github.com/AlexanderLanderos) +- **Chris Guizzetti** - [@guizzettic](https://github.com/guizzettic) +- **Jason Victor** - [@theqwertypusher](https://github.com/Theqwertypusher) +- **Sanjay Lavingia** - [@sanjaylavingia](https://github.com/sanjaylavingia) +- **Vincent Nguyen** - [@VNguyenCode](https://github.com/VNguyenCode) +- **Haejin Jo** - [@haejinjo](https://github.com/haejinjo) +- **Hien Nguyen** - [@hienqn](https://github.com/hienqn) +- **Jack Crish** - [@JackC27](https://github.com/JackC27) +- **Kevin Fey** - [@kevinfey](https://github.com/kevinfey) +- **Carlos Perez** - [@crperezt](https://github.com/crperezt) +- **Edwin Menendez** - [@edwinjmenendez](https://github.com/edwinjmenendez) +- **Gabriela Jardim Aquino** - [@aquinojardim](https://github.com/aquinojardim) +- **Greg Panciera** - [@gpanciera](https://github.com/gpanciera) +- **Nathanael Wa Mwenze** - [@nmwenz90](https://github.com/nmwenz90) +- **Ryan Dang** - [@rydang](https://github.com/rydang) +- **Bryan Lee** - [@mylee1995](https://github.com/mylee1995) +- **Josh Kim** - [@joshua0308](https://github.com/joshua0308) +- **Sierra Swaby** - [@starkspark](https://github.com/starkspark) +- **Ruth Anam** - [@nusanam](https://github.com/nusanam) +- **David Chai** - [@davidchaidev](https://github.com/davidchai717) +- **Yujin Kang** - [@yujinkay](https://github.com/yujinkay) +- **Andy Wong** - [@andynullwong](https://github.com/andynullwong) +- **Chris Flannery** - [@chriswillsflannery](https://github.com/chriswillsflannery) +- **Rajeeb Banstola** - [@rajeebthegreat](https://github.com/rajeebthegreat) +- **Prasanna Malla** - [@prasmalla](https://github.com/prasmalla) +- **Rocky Lin** - [@rocky9413](https://github.com/rocky9413) +- **Abaas Khorrami** - [@dubalol](https://github.com/dubalol) +- **Ergi Shehu** - [@Ergi516](https://github.com/ergi516) +- **Raymond Kwan** - [@rkwn](https://github.com/rkwn) +- **Joshua Howard** - [@Joshua-Howard](https://github.com/joshua-howard) +- **Lina Shin** - [@rxlina](https://github.com/rxlina) +- **Andy Tsou** - [@andytsou19](https://github.com/andytsou19) +- **Feiyi Wu** - [@FreyaWu](https://github.com/FreyaWu) +- **Viet Nguyen** - [@vnguyen95](https://github.com/vnguyen95) +- **Alex Gomez** - [@alexgomez9](https://github.com/alexgomez9) +- **Edar Liu** - [@liuedar](https://github.com/liuedar) +- **Kristina Wallen** - [@kristinawallen](https://github.com/kristinawallen) +- **Quan Le** - [@Blachfog](https://github.com/Blachfog) +- **Robert Maeda** - [@robmaeda](https://github.com/robmaeda) +- **Lance Ziegler** - [@lanceziegler](https://github.com/lanceziegler) +- **Ngoc Zwolinski** - [@ngoczwolinski](https://github.com/ngoczwolinski) +- **Peter Lam** - [@dev-plam](https://github.com/dev-plam) +- **Zachary Freeman** - [@zacharydfreeman](https://github.com/zacharydfreeman/) +- **Jackie Yuan** - [@yuanjackie1](https://github.com/yuanjackie1) +- **Jasmine Noor** - [@jasnoo](https://github.com/jasnoo) +- **Minzo Kim** - [@minzo-kim](https://github.com/minzo-kim) +- **Mark Teets** - [@MarkTeets](https://github.com/MarkTeets) +- **Nick Huemmer** - [@NickHuemmer](https://github.com/ElDuke717) +- **James McCollough** - [@j-mccoll](https://github.com/j-mccoll) +- **Mike Bednarz** - [@mikebednarz](https://github.com/mikebednarz) +- **Sergei Liubchenko** - [@sergeylvq](https://github.com/sergeylvq) +- **Yididia Ketema** - [@yididiaketema](https://github.com/yididiaketema) +- **Morah Geist** - [@morahgeist](https://github.com/morahgeist) +- **Eivind Del Fierro** - [@EivindDelFierro](https://github.com/EivindDelFierro) +- **Kyle Bell** - [@KyEBell](https://github.com/KyEBell) +- **Sean Kelly** - [@brok3turtl3](https://github.com/brok3turtl3) +- **Christopher Stamper** - [@ctstamper](https://github.com/ctstamper) +- **Jimmy Phy** - [@jimmally](https://github.com/jimmally) +- **Andrew Byun** - [@AndrewByun](https://github.com/AndrewByun) +- **Kelvin Mirhan** - [@kelvinmirhan](https://github.com/kelvinmirhan) +- **Jesse Rosengrant** - [@jrosengrant](https://github.com/jrosengrant) +- **Liam Donaher** - [@leebology](https://github.com/leebology) +- **David Moore** - [@Solodt55](https://github.com/Solodt55) +- **John Banks** - [@Jbanks123](https://github.com/Jbanks123) +
+ +

⚖️ Лицензия

+ +Этот проект распространяется по лицензии MIT — подробности см. в файле [LICENSE](LICENSE). diff --git a/assets/Back_End_Dependency_Chart_v22.jpg b/assets/Back_End_Dependency_Chart_v22.jpg new file mode 100644 index 000000000..c7dfb3511 Binary files /dev/null and b/assets/Back_End_Dependency_Chart_v22.jpg differ diff --git a/assets/DataFlowDiagramV23.png b/assets/DataFlowDiagramV23.png new file mode 100644 index 000000000..f835c0673 Binary files /dev/null and b/assets/DataFlowDiagramV23.png differ diff --git a/assets/Front_End_Dependency_Chart_v22.jpg b/assets/Front_End_Dependency_Chart_v22.jpg new file mode 100644 index 000000000..6f8d26937 Binary files /dev/null and b/assets/Front_End_Dependency_Chart_v22.jpg differ diff --git a/assets/backend-recordSnapshot.png b/assets/backend-recordSnapshot.png new file mode 100644 index 000000000..b3e9f1559 Binary files /dev/null and b/assets/backend-recordSnapshot.png differ diff --git a/assets/backend-timeTravel.png b/assets/backend-timeTravel.png new file mode 100644 index 000000000..eee1a4a23 Binary files /dev/null and b/assets/backend-timeTravel.png differ diff --git a/assets/frontend-diagram.png b/assets/frontend-diagram.png new file mode 100644 index 000000000..84a6b03cb Binary files /dev/null and b/assets/frontend-diagram.png differ diff --git a/assets/gifs/GeneralDemoGif_V26.gif b/assets/gifs/GeneralDemoGif_V26.gif new file mode 100644 index 000000000..cae64e5de Binary files /dev/null and b/assets/gifs/GeneralDemoGif_V26.gif differ diff --git a/assets/gifs/ImportExportGif_V26.gif b/assets/gifs/ImportExportGif_V26.gif new file mode 100644 index 000000000..4947ff1dd Binary files /dev/null and b/assets/gifs/ImportExportGif_V26.gif differ diff --git a/assets/gifs/ProviderConsumer_V26.gif b/assets/gifs/ProviderConsumer_V26.gif new file mode 100644 index 000000000..ff436de8f Binary files /dev/null and b/assets/gifs/ProviderConsumer_V26.gif differ diff --git a/assets/gifs/TimeTravelGif_V26.gif b/assets/gifs/TimeTravelGif_V26.gif new file mode 100644 index 000000000..e1d20bb71 Binary files /dev/null and b/assets/gifs/TimeTravelGif_V26.gif differ diff --git a/assets/gifs/app_main_v26.png b/assets/gifs/app_main_v26.png new file mode 100644 index 000000000..c0d9076fd Binary files /dev/null and b/assets/gifs/app_main_v26.png differ diff --git a/assets/gifs/console.gif b/assets/gifs/console.gif new file mode 100644 index 000000000..0d4e5d312 Binary files /dev/null and b/assets/gifs/console.gif differ diff --git a/assets/gifs/extension-console.gif b/assets/gifs/extension-console.gif new file mode 100644 index 000000000..0652609a4 Binary files /dev/null and b/assets/gifs/extension-console.gif differ diff --git a/assets/gifs/reactime-console.gif b/assets/gifs/reactime-console.gif new file mode 100644 index 000000000..206be2336 Binary files /dev/null and b/assets/gifs/reactime-console.gif differ diff --git a/assets/gifs/reactime-dev-setup.gif b/assets/gifs/reactime-dev-setup.gif new file mode 100644 index 000000000..cbe915af0 Binary files /dev/null and b/assets/gifs/reactime-dev-setup.gif differ diff --git a/assets/logos/blackWhiteSquareIcon128.png b/assets/logos/blackWhiteSquareIcon128.png new file mode 100644 index 000000000..9ff5c33e0 Binary files /dev/null and b/assets/logos/blackWhiteSquareIcon128.png differ diff --git a/assets/logos/blackWhiteSquareIcon48.png b/assets/logos/blackWhiteSquareIcon48.png new file mode 100644 index 000000000..d054e2b6c Binary files /dev/null and b/assets/logos/blackWhiteSquareIcon48.png differ diff --git a/assets/logos/marqueePromoTitle.png b/assets/logos/marqueePromoTitle.png new file mode 100644 index 000000000..7c13a6d7e Binary files /dev/null and b/assets/logos/marqueePromoTitle.png differ diff --git a/assets/logos/smallPromoTitle.png b/assets/logos/smallPromoTitle.png new file mode 100644 index 000000000..0074165eb Binary files /dev/null and b/assets/logos/smallPromoTitle.png differ diff --git a/assets/logos/whiteBlackSquareIcon128.png b/assets/logos/whiteBlackSquareIcon128.png new file mode 100644 index 000000000..3f95221cb Binary files /dev/null and b/assets/logos/whiteBlackSquareIcon128.png differ diff --git a/assets/logos/whiteBlackSquareIcon48.png b/assets/logos/whiteBlackSquareIcon48.png new file mode 100644 index 000000000..6788a2d02 Binary files /dev/null and b/assets/logos/whiteBlackSquareIcon48.png differ diff --git a/assets/obsolete/gifs/GeneralDemoGif_V23.gif b/assets/obsolete/gifs/GeneralDemoGif_V23.gif new file mode 100644 index 000000000..4dc6f8924 Binary files /dev/null and b/assets/obsolete/gifs/GeneralDemoGif_V23.gif differ diff --git a/assets/obsolete/gifs/ImportExportGif_V23.gif b/assets/obsolete/gifs/ImportExportGif_V23.gif new file mode 100644 index 000000000..0b8d9f3da Binary files /dev/null and b/assets/obsolete/gifs/ImportExportGif_V23.gif differ diff --git a/assets/obsolete/gifs/PerformanceGif_V23.gif b/assets/obsolete/gifs/PerformanceGif_V23.gif new file mode 100644 index 000000000..99b9bc1c4 Binary files /dev/null and b/assets/obsolete/gifs/PerformanceGif_V23.gif differ diff --git a/assets/obsolete/gifs/TimeTravelGif_V23.gif b/assets/obsolete/gifs/TimeTravelGif_V23.gif new file mode 100644 index 000000000..4e14f229b Binary files /dev/null and b/assets/obsolete/gifs/TimeTravelGif_V23.gif differ diff --git a/assets/obsolete/logos/blackWhiteSquareIcon.png b/assets/obsolete/logos/blackWhiteSquareIcon.png new file mode 100644 index 000000000..4a15ecb29 Binary files /dev/null and b/assets/obsolete/logos/blackWhiteSquareIcon.png differ diff --git a/assets/obsolete/logos/blackWhiteSquareIcon128_old.png b/assets/obsolete/logos/blackWhiteSquareIcon128_old.png new file mode 100644 index 000000000..2e89a45da Binary files /dev/null and b/assets/obsolete/logos/blackWhiteSquareIcon128_old.png differ diff --git a/assets/obsolete/logos/blackWhiteSquareIcon48_old.png b/assets/obsolete/logos/blackWhiteSquareIcon48_old.png new file mode 100644 index 000000000..298516b15 Binary files /dev/null and b/assets/obsolete/logos/blackWhiteSquareIcon48_old.png differ diff --git a/assets/obsolete/logos/blackWhiteSquareLogo.png b/assets/obsolete/logos/blackWhiteSquareLogo.png new file mode 100644 index 000000000..7203dabbf Binary files /dev/null and b/assets/obsolete/logos/blackWhiteSquareLogo.png differ diff --git a/assets/obsolete/logos/blackWhiteSquareLogoMedium.png b/assets/obsolete/logos/blackWhiteSquareLogoMedium.png new file mode 100644 index 000000000..c283491ea Binary files /dev/null and b/assets/obsolete/logos/blackWhiteSquareLogoMedium.png differ diff --git a/assets/obsolete/logos/blackWhiteSquareSite.png b/assets/obsolete/logos/blackWhiteSquareSite.png new file mode 100644 index 000000000..fc5cbb02a Binary files /dev/null and b/assets/obsolete/logos/blackWhiteSquareSite.png differ diff --git a/assets/obsolete/logos/marqueePromoTitle.png b/assets/obsolete/logos/marqueePromoTitle.png new file mode 100644 index 000000000..e47d77771 Binary files /dev/null and b/assets/obsolete/logos/marqueePromoTitle.png differ diff --git a/assets/obsolete/logos/marqueePromoTitle_old.png b/assets/obsolete/logos/marqueePromoTitle_old.png new file mode 100644 index 000000000..e47d77771 Binary files /dev/null and b/assets/obsolete/logos/marqueePromoTitle_old.png differ diff --git a/assets/obsolete/logos/smallPromoTitle.png b/assets/obsolete/logos/smallPromoTitle.png new file mode 100644 index 000000000..254f85d69 Binary files /dev/null and b/assets/obsolete/logos/smallPromoTitle.png differ diff --git a/assets/obsolete/logos/smallPromoTitle_old.png b/assets/obsolete/logos/smallPromoTitle_old.png new file mode 100644 index 000000000..254f85d69 Binary files /dev/null and b/assets/obsolete/logos/smallPromoTitle_old.png differ diff --git a/assets/obsolete/logos/whiteBlackSquareIcon.png b/assets/obsolete/logos/whiteBlackSquareIcon.png new file mode 100644 index 000000000..70592ee34 Binary files /dev/null and b/assets/obsolete/logos/whiteBlackSquareIcon.png differ diff --git a/assets/obsolete/logos/whiteBlackSquareIcon128.png b/assets/obsolete/logos/whiteBlackSquareIcon128.png new file mode 100644 index 000000000..9749d8d41 Binary files /dev/null and b/assets/obsolete/logos/whiteBlackSquareIcon128.png differ diff --git a/assets/obsolete/logos/whiteBlackSquareIcon48.png b/assets/obsolete/logos/whiteBlackSquareIcon48.png new file mode 100644 index 000000000..b5b1ef559 Binary files /dev/null and b/assets/obsolete/logos/whiteBlackSquareIcon48.png differ diff --git a/assets/obsolete/logos/whiteBlackSquareLogo.png b/assets/obsolete/logos/whiteBlackSquareLogo.png new file mode 100644 index 000000000..c8756bb01 Binary files /dev/null and b/assets/obsolete/logos/whiteBlackSquareLogo.png differ diff --git a/assets/obsolete/logos/whiteBlackSquareSite.png b/assets/obsolete/logos/whiteBlackSquareSite.png new file mode 100644 index 000000000..15b4af999 Binary files /dev/null and b/assets/obsolete/logos/whiteBlackSquareSite.png differ diff --git a/assets/reactime with website svg.svg b/assets/reactime with website svg.svg deleted file mode 100644 index 3356a87b3..000000000 --- a/assets/reactime with website svg.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/assets/readme-logo-svg.svg b/assets/readme-logo-svg.svg deleted file mode 100644 index 3356a87b3..000000000 --- a/assets/readme-logo-svg.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/assets/readme_logo.png b/assets/readme_logo.png deleted file mode 100644 index 618512e11..000000000 Binary files a/assets/readme_logo.png and /dev/null differ diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 677962651..000000000 --- a/babel.config.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - targets: { - node: 'current', - }, - }, - ], - '@babel/preset-react', - ], -}; diff --git a/demo-app-next/.gitignore b/demo-app-next/.gitignore new file mode 100644 index 000000000..20fccdd4b --- /dev/null +++ b/demo-app-next/.gitignore @@ -0,0 +1,30 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local diff --git a/demo-app-next/README.md b/demo-app-next/README.md new file mode 100644 index 000000000..02695bc1d --- /dev/null +++ b/demo-app-next/README.md @@ -0,0 +1 @@ +This is a starter template for [Learn Next.js](https://nextjs.org/learn). \ No newline at end of file diff --git a/demo-app-next/next-env.d.ts b/demo-app-next/next-env.d.ts new file mode 100644 index 000000000..a4a7b3f5c --- /dev/null +++ b/demo-app-next/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. diff --git a/demo-app-next/package.json b/demo-app-next/package.json new file mode 100644 index 000000000..0f7a13bba --- /dev/null +++ b/demo-app-next/package.json @@ -0,0 +1,18 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "18.2.0", + "react-dom": "18.2.0" + }, + "devDependencies": { + "@types/node": "18.15.0", + "@types/react": "18.0.28", + "typescript": "4.9.5" + } +} diff --git a/demo-app-next/public/favicon.ico b/demo-app-next/public/favicon.ico new file mode 100644 index 000000000..4965832f2 Binary files /dev/null and b/demo-app-next/public/favicon.ico differ diff --git a/demo-app-next/public/vercel.svg b/demo-app-next/public/vercel.svg new file mode 100644 index 000000000..fbf0e25a6 --- /dev/null +++ b/demo-app-next/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/demo-app-next/src/components/Board.tsx b/demo-app-next/src/components/Board.tsx new file mode 100644 index 000000000..950f292df --- /dev/null +++ b/demo-app-next/src/components/Board.tsx @@ -0,0 +1,136 @@ +import React, { Component } from 'react'; +import Row from './Row'; +import { BoardContent, Scoreboard, Player } from '../types/types'; + +type BoardState = { + board: BoardContent; + currentPlayer: Player; + gameOver: boolean; + message: string; + scoreboard: Scoreboard; +}; + +class Board extends Component<{}, BoardState> { + constructor(props: unknown) { + super(props); + this.state = { + board: this.newBoard(), + currentPlayer: 'X', + gameOver: false, + message: '', + scoreboard: { X: 0, O: 0 }, + }; + + this.resetBoard = this.resetBoard.bind(this); + this.handleBoxClick = this.handleBoxClick.bind(this); + } + + componentDidUpdate(): void { + this.checkForWinner(); + } + + /** + * @method newBoard + * @description - returns a blank BoardContent array, + * for the start of a new game + */ + newBoard(): BoardContent { + return [ + ['-', '-', '-'], + ['-', '-', '-'], + ['-', '-', '-'], + ]; + } + + /** + * @method resetBoard + * @description - sets to board object to be all '-', + * and sets gameOver and message to default state + */ + resetBoard(): void { + this.setState({ + gameOver: false, + board: this.newBoard(), + message: '', + }); + } + + /** + * @method checkForWinner + * @description - checks to see if either player has filled a row + * if so, ends the game and updates the message to declare winner + */ + checkForWinner(): void { + const { board, gameOver, currentPlayer } = this.state; + + const spacesLeft = (): boolean => { + for (let i of board) { + if (i.includes('-')) return true; + } + return false; + }; + + if (!gameOver) { + // win conditions: matching rows, columns, or diagonals, that are not empty('-') + if ( + (board[0][0] === board[0][1] && board[0][1] === board[0][2] && board[0][2] !== '-') || + (board[1][0] === board[1][1] && board[1][1] === board[1][2] && board[1][2] !== '-') || + (board[2][0] === board[2][1] && board[2][1] === board[2][2] && board[2][2] !== '-') || + (board[0][0] === board[1][0] && board[1][0] === board[2][0] && board[2][0] !== '-') || + (board[0][1] === board[1][1] && board[1][1] === board[2][1] && board[2][1] !== '-') || + (board[0][2] === board[1][2] && board[1][2] === board[2][2] && board[2][2] !== '-') || + (board[0][0] === board[1][1] && board[1][1] === board[2][2] && board[2][2] !== '-') || + (board[2][0] === board[1][1] && board[1][1] === board[0][2] && board[0][2] !== '-') + ) { + // winner is the person who's turn was previous + const winner: Player = currentPlayer === 'X' ? 'O' : 'X'; + + this.setState({ + gameOver: true, + message: `Player ${winner} wins!`, + }); + + // draw condition: no '-' remaining in board without above win condition triggering + } else if (!spacesLeft()) { + this.setState({ + gameOver: true, + message: 'Draw!', + }); + } + } + } + + handleBoxClick(row: number, column: number): void { + const boardCopy: BoardContent = [ + [...this.state.board[0]], + [...this.state.board[1]], + [...this.state.board[2]], + ]; + boardCopy[row][column] = this.state.currentPlayer; + const newPlayer: Player = this.state.currentPlayer === 'X' ? 'O' : 'X'; + this.setState({ board: boardCopy, currentPlayer: newPlayer }); + } + + render(): JSX.Element { + const rows: Array = []; + for (let i = 0; i < 3; i++) { + rows.push( + , + ); + } + // const { X, O }: Scoreboard = this.state.scoreboard; + + return ( +
+

Tic Tac Toe

+ {this.state.gameOver &&

{this.state.message}

} + {rows} + +
+ ); + } +} + +export default Board; diff --git a/demo-app-next/src/components/Box.tsx b/demo-app-next/src/components/Box.tsx new file mode 100644 index 000000000..f44c00d62 --- /dev/null +++ b/demo-app-next/src/components/Box.tsx @@ -0,0 +1,18 @@ +import { BoardText } from '../types/types'; + +type BoxProps = { + value: BoardText; + row: number; + column: number; + handleBoxClick: (row: number, column: number) => void; +}; + +const Box = (props: BoxProps): JSX.Element => { + return ( + + ); +}; + +export default Box; diff --git a/demo-app-next/src/components/Buttons.tsx b/demo-app-next/src/components/Buttons.tsx new file mode 100644 index 000000000..184e30acf --- /dev/null +++ b/demo-app-next/src/components/Buttons.tsx @@ -0,0 +1,19 @@ +import Increment from './Increment'; + +export default function Buttons(): JSX.Element { + const buttons = []; + for (let i = 0; i < 4; i++) { + buttons.push(); + } + + return ( +
+

Stateful Buttons

+

+ These buttons are functional components that each manage their own state with the useState + hook. +

+ {buttons} +
+ ); +} diff --git a/demo-app-next/src/components/Increment.tsx b/demo-app-next/src/components/Increment.tsx new file mode 100644 index 000000000..791f08f23 --- /dev/null +++ b/demo-app-next/src/components/Increment.tsx @@ -0,0 +1,11 @@ +import React, { useState } from 'react'; + +export default function Increment(): JSX.Element { + const [count, setCount] = useState(0); + + return ( + + ); +} diff --git a/demo-app-next/src/components/Row.tsx b/demo-app-next/src/components/Row.tsx new file mode 100644 index 000000000..961a83c73 --- /dev/null +++ b/demo-app-next/src/components/Row.tsx @@ -0,0 +1,27 @@ +import Box from './Box'; +import { BoardText } from '../types/types'; + +type RowProps = { + handleBoxClick: (row: number, column: number) => void; + values: Array; + row: number; +}; + +const Row = (props: RowProps) => { + const boxes: Array = []; + for (let i = 0; i < 3; i++) { + boxes.push( + , + ); + } + + return
{boxes}
; +}; + +export default Row; diff --git a/demo-app-next/src/components/navbar.tsx b/demo-app-next/src/components/navbar.tsx new file mode 100644 index 000000000..b841698ea --- /dev/null +++ b/demo-app-next/src/components/navbar.tsx @@ -0,0 +1,17 @@ +import Link from 'next/link'; + +export default function Navbar(): JSX.Element { + return ( +
+ + About + + + Tic-Tac-Toe + + + Counter + +
+ ); +} diff --git a/demo-app-next/src/pages/_app.tsx b/demo-app-next/src/pages/_app.tsx new file mode 100644 index 000000000..279f38d6b --- /dev/null +++ b/demo-app-next/src/pages/_app.tsx @@ -0,0 +1,6 @@ +import '../../styles/style.css'; +import React from 'react'; + +export default function MyApp({ Component, pageProps }): JSX.Element { + return ; +} diff --git a/demo-app-next/src/pages/buttons/index.tsx b/demo-app-next/src/pages/buttons/index.tsx new file mode 100644 index 000000000..4e0b24237 --- /dev/null +++ b/demo-app-next/src/pages/buttons/index.tsx @@ -0,0 +1,11 @@ +import Buttons from '../../components/Buttons'; +import Navbar from '../../components/navbar'; + +export default function ButtonsPage(): JSX.Element { + return ( +
+ + +
+ ); +} diff --git a/demo-app-next/src/pages/index.tsx b/demo-app-next/src/pages/index.tsx new file mode 100644 index 000000000..5bcd002d3 --- /dev/null +++ b/demo-app-next/src/pages/index.tsx @@ -0,0 +1,29 @@ +import Navbar from '../components/navbar'; +import React from 'react'; + +export default function Home(): JSX.Element { + return ( +
+ +
+

Lorem Ipsum

+

+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum." +

+

+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum." +

+
+
+ ); +} diff --git a/demo-app-next/src/pages/tictactoe/index.tsx b/demo-app-next/src/pages/tictactoe/index.tsx new file mode 100644 index 000000000..d2376d215 --- /dev/null +++ b/demo-app-next/src/pages/tictactoe/index.tsx @@ -0,0 +1,11 @@ +import Board from '../../components/Board'; +import Navbar from '../../components/navbar'; + +export default function BoardPage(): JSX.Element { + return ( +
+ + +
+ ); +} diff --git a/demo-app-next/src/types/types.ts b/demo-app-next/src/types/types.ts new file mode 100644 index 000000000..cc8252e42 --- /dev/null +++ b/demo-app-next/src/types/types.ts @@ -0,0 +1,10 @@ +export type Scoreboard = { + X: number; + O: number; +}; + +export type Player = 'X' | 'O'; + +export type BoardText = 'X' | 'O' | '-'; + +export type BoardContent = Array>; diff --git a/demo-app-next/styles/Home.module.css b/demo-app-next/styles/Home.module.css new file mode 100644 index 000000000..b82575626 --- /dev/null +++ b/demo-app-next/styles/Home.module.css @@ -0,0 +1,91 @@ +.container { + min-height: 100vh; + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.title a { + color: #0070f3; + text-decoration: none; +} + +.title a:hover, +.title a:focus, +.title a:active { + text-decoration: underline; +} + +.title { + margin: 0 0 1rem; + line-height: 1.15; + font-size: 3.6rem; +} + +.title { + text-align: center; +} + +.title, +.description { + text-align: center; +} + + +.description { + line-height: 1.5; + font-size: 1.5rem; +} + +.grid { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + + max-width: 800px; + margin-top: 3rem; +} + +.card { + margin: 1rem; + flex-basis: 45%; + padding: 1.5rem; + text-align: left; + color: inherit; + text-decoration: none; + border: 1px solid #eaeaea; + border-radius: 10px; + transition: color 0.15s ease, border-color 0.15s ease; +} + +.card:hover, +.card:focus, +.card:active { + color: #0070f3; + border-color: #0070f3; +} + +.card h3 { + margin: 0 0 1rem 0; + font-size: 1.5rem; +} + +.card p { + margin: 0; + font-size: 1.25rem; + line-height: 1.5; +} + +.logo { + height: 1em; +} + +@media (max-width: 600px) { + .grid { + width: 100%; + flex-direction: column; + } +} diff --git a/demo-app-next/styles/globals.css b/demo-app-next/styles/globals.css new file mode 100644 index 000000000..6c7e3c462 --- /dev/null +++ b/demo-app-next/styles/globals.css @@ -0,0 +1,149 @@ +body { + margin: 0; + font-family: Arial, Helvetica, sans-serif; + background-color: #fff4f4; +} + +h1, +h4 { + text-align: center; +} + +/* Navbar */ + +.nav { + background-color: #ff6569; + + display: flex; + justify-content: space-evenly; + + padding: 30px; + height: 30px; +} + +.link { + flex-grow: 1; + + font-size: 1.5em; + text-decoration: none; + text-align: center; + + color: #fff4f4; +} + +.link:hover { + font-size: 2em; +} + +/* About */ + +.about { + background-color: #ffffff; + color: #330002; + + padding-top: 1em; + padding-bottom: 2em; + padding-right: 4em; + padding-left: 4em; + margin-top: 2em; + + max-width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +/* Tic-Tac-Toe */ + +.board { + background-color: #ffffff; + color: #330002; + + margin-top: 2em; + + padding-top: 1em; + padding-bottom: 1em; + padding-left: 4em; + padding-right: 4em; + + width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +.box { + background-color: #62d6fb; + border-style: solid; + border-color: #ffffff; + border-radius: 5px; + + height: 100px; + width: 100px; + + font-size: 4em; +} + +#reset { + color: #ff6569; + font-size: 1.5em; + + background-color: #ffffff; + border-style: solid; + border-color: #ff6569; + border-radius: 5px; + + margin-top: 20px; + margin-bottom: 20px; + + width: 100%; + + padding: 0.5em; +} + +#reset:hover { + color: #ffffff; + background-color: #ff6569; +} + +/* Counter */ + +.buttons { + background-color: #ffffff; + color: #330002; + + padding-top: 1em; + padding-bottom: 2em; + padding-right: 4em; + padding-left: 4em; + margin-top: 2em; + + max-width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +.increment { + color: #ffffff; + font-size: 1.5em; + + background-color: #ff6569; + border-style: solid; + border-color: #ffffff; + border-radius: 5px; + + margin-top: 20px; + margin-bottom: 20px; + + width: 100%; + + padding: 0.5em; +} + +.increment:hover { + background-color: #62d6fb; +} diff --git a/demo-app-next/styles/style.css b/demo-app-next/styles/style.css new file mode 100644 index 000000000..6c7e3c462 --- /dev/null +++ b/demo-app-next/styles/style.css @@ -0,0 +1,149 @@ +body { + margin: 0; + font-family: Arial, Helvetica, sans-serif; + background-color: #fff4f4; +} + +h1, +h4 { + text-align: center; +} + +/* Navbar */ + +.nav { + background-color: #ff6569; + + display: flex; + justify-content: space-evenly; + + padding: 30px; + height: 30px; +} + +.link { + flex-grow: 1; + + font-size: 1.5em; + text-decoration: none; + text-align: center; + + color: #fff4f4; +} + +.link:hover { + font-size: 2em; +} + +/* About */ + +.about { + background-color: #ffffff; + color: #330002; + + padding-top: 1em; + padding-bottom: 2em; + padding-right: 4em; + padding-left: 4em; + margin-top: 2em; + + max-width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +/* Tic-Tac-Toe */ + +.board { + background-color: #ffffff; + color: #330002; + + margin-top: 2em; + + padding-top: 1em; + padding-bottom: 1em; + padding-left: 4em; + padding-right: 4em; + + width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +.box { + background-color: #62d6fb; + border-style: solid; + border-color: #ffffff; + border-radius: 5px; + + height: 100px; + width: 100px; + + font-size: 4em; +} + +#reset { + color: #ff6569; + font-size: 1.5em; + + background-color: #ffffff; + border-style: solid; + border-color: #ff6569; + border-radius: 5px; + + margin-top: 20px; + margin-bottom: 20px; + + width: 100%; + + padding: 0.5em; +} + +#reset:hover { + color: #ffffff; + background-color: #ff6569; +} + +/* Counter */ + +.buttons { + background-color: #ffffff; + color: #330002; + + padding-top: 1em; + padding-bottom: 2em; + padding-right: 4em; + padding-left: 4em; + margin-top: 2em; + + max-width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +.increment { + color: #ffffff; + font-size: 1.5em; + + background-color: #ff6569; + border-style: solid; + border-color: #ffffff; + border-radius: 5px; + + margin-top: 20px; + margin-bottom: 20px; + + width: 100%; + + padding: 0.5em; +} + +.increment:hover { + background-color: #62d6fb; +} diff --git a/demo-app-next/tsconfig.json b/demo-app-next/tsconfig.json new file mode 100644 index 000000000..1203d7cb2 --- /dev/null +++ b/demo-app-next/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "target": "ES2017" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/demo-app-remix/.eslintrc.js b/demo-app-remix/.eslintrc.js new file mode 100644 index 000000000..f2faf1470 --- /dev/null +++ b/demo-app-remix/.eslintrc.js @@ -0,0 +1,4 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: ['@remix-run/eslint-config', '@remix-run/eslint-config/node'], +}; diff --git a/demo-app-remix/.gitignore b/demo-app-remix/.gitignore new file mode 100644 index 000000000..3f7bf98da --- /dev/null +++ b/demo-app-remix/.gitignore @@ -0,0 +1,6 @@ +node_modules + +/.cache +/build +/public/build +.env diff --git a/demo-app-remix/README.md b/demo-app-remix/README.md new file mode 100644 index 000000000..a456eb3b6 --- /dev/null +++ b/demo-app-remix/README.md @@ -0,0 +1,51 @@ +# Welcome to Remix! + +- [Remix Docs](https://remix.run/docs) + +## Development + +Start the Remix development asset server and the Express server by running: + +```sh +npm run dev +``` + +This starts your app in development mode, which will purge the server require cache when Remix rebuilds assets so you don't need a process manager restarting the express server. + +## Deployment + +First, build your app for production: + +```sh +npm run build +``` + +Then run the app in production mode: + +```sh +npm start +``` + +Now you'll need to pick a host to deploy it to. + +### DIY + +If you're familiar with deploying express applications you should be right at home just make sure to deploy the output of `remix build` + +- `build/` +- `public/build/` + +### Using a Template + +When you ran `npx create-remix@latest` there were a few choices for hosting. You can run that again to create a new project, then copy over your `app/` folder to the new project that's pre-configured for your target server. + +```sh +cd .. +# create a new project, and pick a pre-configured host +npx create-remix@latest +cd my-new-remix-app +# remove the new project's app (not the old one!) +rm -rf app +# copy your app over +cp -R ../my-old-remix-app/app app +``` diff --git a/demo-app-remix/app/components/Board.tsx b/demo-app-remix/app/components/Board.tsx new file mode 100644 index 000000000..9eeb7ed58 --- /dev/null +++ b/demo-app-remix/app/components/Board.tsx @@ -0,0 +1,137 @@ +import React, { Component } from 'react'; +import Row from './Row'; +import type { BoardContent, Scoreboard, Player } from '../types/types'; +import type { BoardText } from '../types/types'; + +type BoardState = { + board: BoardContent; + currentPlayer: Player; + gameOver: boolean; + message: string; + scoreboard: Scoreboard; +}; + +class Board extends Component<{}, BoardState> { + constructor(props: any) { + super(props); + this.state = { + board: this.newBoard(), + currentPlayer: 'X', + gameOver: false, + message: '', + scoreboard: { X: 0, O: 0 }, + }; + + this.resetBoard = this.resetBoard.bind(this); + this.handleBoxClick = this.handleBoxClick.bind(this); + } + + componentDidUpdate() { + this.checkForWinner(); + } + + /** + * @method newBoard + * @description - returns a blank BoardContent array, + * for the start of a new game + */ + newBoard(): BoardContent { + return [ + ['-', '-', '-'], + ['-', '-', '-'], + ['-', '-', '-'], + ]; + } + + /** + * @method resetBoard + * @description - sets to board object to be all '-', + * and sets gameOver and message to default state + */ + resetBoard(): void { + this.setState({ + gameOver: false, + board: this.newBoard(), + message: '', + }); + } + + /** + * @method checkForWinner + * @description - checks to see if either player has filled a row + * if so, ends the game and updates the message to declare winner + */ + checkForWinner(): void { + const { board, gameOver, currentPlayer } = this.state; + + const spacesLeft = (): boolean => { + for (let i of board) { + if (i.includes('-')) return true; + } + return false; + }; + + if (!gameOver) { + // win conditions: matching rows, columns, or diagonals, that are not empty('-') + if ( + (board[0][0] === board[0][1] && board[0][1] === board[0][2] && board[0][2] !== '-') || + (board[1][0] === board[1][1] && board[1][1] === board[1][2] && board[1][2] !== '-') || + (board[2][0] === board[2][1] && board[2][1] === board[2][2] && board[2][2] !== '-') || + (board[0][0] === board[1][0] && board[1][0] === board[2][0] && board[2][0] !== '-') || + (board[0][1] === board[1][1] && board[1][1] === board[2][1] && board[2][1] !== '-') || + (board[0][2] === board[1][2] && board[1][2] === board[2][2] && board[2][2] !== '-') || + (board[0][0] === board[1][1] && board[1][1] === board[2][2] && board[2][2] !== '-') || + (board[2][0] === board[1][1] && board[1][1] === board[0][2] && board[0][2] !== '-') + ) { + // winner is the person who's turn was previous + const winner: Player = currentPlayer === 'X' ? 'O' : 'X'; + + this.setState({ + gameOver: true, + message: `Player ${winner} wins!`, + }); + + // draw condition: no '-' remaining in board without above win condition triggering + } else if (!spacesLeft()) { + this.setState({ + gameOver: true, + message: 'Draw!', + }); + } + } + } + + handleBoxClick(row: number, column: number): void { + const boardCopy: BoardContent = [ + [...this.state.board[0]], + [...this.state.board[1]], + [...this.state.board[2]], + ]; + boardCopy[row][column] = this.state.currentPlayer; + const newPlayer: Player = this.state.currentPlayer === 'X' ? 'O' : 'X'; + this.setState({ board: boardCopy, currentPlayer: newPlayer }); + } + + render() { + const rows: Array = []; + for (let i = 0; i < 3; i++) { + rows.push( + , + ); + } + const { X, O }: Scoreboard = this.state.scoreboard; + + return ( +
+

Tic Tac Toe

+ {this.state.gameOver &&

{this.state.message}

} + {rows} + +
+ ); + } +} + +export default Board; diff --git a/demo-app-remix/app/components/Box.tsx b/demo-app-remix/app/components/Box.tsx new file mode 100644 index 000000000..038a557f3 --- /dev/null +++ b/demo-app-remix/app/components/Box.tsx @@ -0,0 +1,18 @@ +import type { BoardText } from '../types/types'; + +type BoxProps = { + value: BoardText; + row: number; + column: number; + handleBoxClick: (row: number, column: number) => void; +}; + +const Box = (props: BoxProps) => { + return ( + + ); +}; + +export default Box; diff --git a/demo-app-remix/app/components/Buttons.tsx b/demo-app-remix/app/components/Buttons.tsx new file mode 100644 index 000000000..33f154acb --- /dev/null +++ b/demo-app-remix/app/components/Buttons.tsx @@ -0,0 +1,21 @@ +import Increment from './Increment'; + +function Buttons() { + const buttons = []; + for (let i = 0; i < 4; i++) { + buttons.push(); + } + + return ( +
+

Stateful Buttons

+

+ These buttons are functional components that each manage their own state with the useState + hook. +

+ {buttons} +
+ ); +} + +export default Buttons; diff --git a/demo-app-remix/app/components/Buttons2.tsx b/demo-app-remix/app/components/Buttons2.tsx new file mode 100644 index 000000000..de0161a9b --- /dev/null +++ b/demo-app-remix/app/components/Buttons2.tsx @@ -0,0 +1,38 @@ +// import StateButton from "./StateButton"; +// import EffectButton from "./EffectButton"; +// import RefComponent from "./RefComponent"; +// import ContextButton from "./ContextButton"; +// import { useState, useEffect, useRef, createContext } from "react"; + +// export const ButtonContext: any = createContext(null); + +// export default function Buttons() { +// const [count, setCount] = useState(0); +// const [effectCount, setEffectCount] = useState(0); +// const renders = useRef(-1); + +// useEffect(() => { +// renders.current = renders.current + 1; +// document.title = `You clicked ${effectCount} times`; +// }, [count, effectCount]); + +// return ( +//
+//

React Hook Buttons

+//

+// These buttons are functional components that manage their own state with +// different react hooks. +//

+// {/* {buttons} */} +// +// +//

A box renders once useRef count is 3

+//
= 3 ? "show" : "hide"}> +// +//
+// +// +// +//
+// ); +// } diff --git a/demo-app-remix/app/components/ContextButton.tsx b/demo-app-remix/app/components/ContextButton.tsx new file mode 100644 index 000000000..175a8a44b --- /dev/null +++ b/demo-app-remix/app/components/ContextButton.tsx @@ -0,0 +1,20 @@ +// import { validateHeaderValue } from "http"; +// import { useContext } from "react"; +// import { ButtonContext } from "./Buttons"; + +// export default function ContextButton() { +// //const [count, setCount] = useState(0); + +// // const [count, setCount] = useContext(ButtonContext); +// const { value }: any = useContext(ButtonContext); +// const [count, setCount] = value; + +// return ( +//
+//

This button tests the useContext hook

+// +//
+// ); +// } diff --git a/demo-app-remix/app/components/EffectButton.tsx b/demo-app-remix/app/components/EffectButton.tsx new file mode 100644 index 000000000..b5c242247 --- /dev/null +++ b/demo-app-remix/app/components/EffectButton.tsx @@ -0,0 +1,18 @@ +// import React, { useState, useEffect, useRef } from "react"; + +// export default function StateButton(props: any) { +// //const [count, setCount] = useState(0); + +// return ( +//
+//

This button tests the UseEffect hook

+// +//
+// ); +// } diff --git a/demo-app-remix/app/components/Increment.tsx b/demo-app-remix/app/components/Increment.tsx new file mode 100644 index 000000000..6e6486908 --- /dev/null +++ b/demo-app-remix/app/components/Increment.tsx @@ -0,0 +1,13 @@ +import React, { useState } from 'react'; + +function Increment() { + const [count, setCount] = useState(0); + + return ( + + ); +} + +export default Increment; diff --git a/demo-app-remix/app/components/RefComponent.tsx b/demo-app-remix/app/components/RefComponent.tsx new file mode 100644 index 000000000..938e82908 --- /dev/null +++ b/demo-app-remix/app/components/RefComponent.tsx @@ -0,0 +1,7 @@ +// export default function RefComponent(props: any) { +// return ( +//
+//

{`Render count: ${props.counter}`}

+//
+// ); +// } diff --git a/demo-app-remix/app/components/Row.tsx b/demo-app-remix/app/components/Row.tsx new file mode 100644 index 000000000..011b7a8ae --- /dev/null +++ b/demo-app-remix/app/components/Row.tsx @@ -0,0 +1,27 @@ +import Box from './Box'; +import type { BoardText } from '../types/types'; + +type RowProps = { + handleBoxClick: (row: number, column: number) => void; + values: Array; + row: number; +}; + +const Row = (props: RowProps) => { + const boxes: Array = []; + for (let i = 0; i < 3; i++) { + boxes.push( + , + ); + } + + return
{boxes}
; +}; + +export default Row; diff --git a/demo-app-remix/app/components/StateButton.tsx b/demo-app-remix/app/components/StateButton.tsx new file mode 100644 index 000000000..4b898c87e --- /dev/null +++ b/demo-app-remix/app/components/StateButton.tsx @@ -0,0 +1,17 @@ +// // import React, { useState } from "react"; + +// export default function StateButton(props: any) { +// //const [count, setCount] = useState(0); + +// return ( +//
+//

This button tests the UseState hook

+// +//
+// ); +// } diff --git a/demo-app-remix/app/components/navbar.js b/demo-app-remix/app/components/navbar.js new file mode 100644 index 000000000..885e6f473 --- /dev/null +++ b/demo-app-remix/app/components/navbar.js @@ -0,0 +1,17 @@ +import { Link } from '@remix-run/react'; + +export default function Navbar() { + return ( +
+ + About + + + Tic-Tac-Toe + + + Counter + +
+ ); +} diff --git a/demo-app-remix/app/root.tsx b/demo-app-remix/app/root.tsx new file mode 100644 index 000000000..26ac3df47 --- /dev/null +++ b/demo-app-remix/app/root.tsx @@ -0,0 +1,35 @@ +import type { MetaFunction, LinksFunction } from '@remix-run/node'; +import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'; +import styles from './styles/style.css'; + +export const meta: MetaFunction = () => ({ + charset: 'utf-8', + title: 'New Remix App', + viewport: 'width=device-width,initial-scale=1', +}); + +export const links: LinksFunction = () => { + return [ + { + rel: 'stylesheet', + href: styles, + }, + ]; +}; + +export default function App() { + return ( + + + + + + + + + + + + + ); +} diff --git a/demo-app-remix/app/routes/buttons/index.tsx b/demo-app-remix/app/routes/buttons/index.tsx new file mode 100644 index 000000000..1842d8240 --- /dev/null +++ b/demo-app-remix/app/routes/buttons/index.tsx @@ -0,0 +1,11 @@ +import Button from '../../components/Buttons'; +import Navbar from '../../components/navbar'; + +export default function ButtonsPage() { + return ( +
+ +
+ ); +} diff --git a/demo-app-remix/app/routes/index.tsx b/demo-app-remix/app/routes/index.tsx new file mode 100644 index 000000000..1e26e26d0 --- /dev/null +++ b/demo-app-remix/app/routes/index.tsx @@ -0,0 +1,28 @@ +import Navbar from '../components/navbar.js'; + +export default function Home() { + return ( +
+ +
+

Lorem Ipsum

+

+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum." +

+

+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum." +

+
+
+ ); +} diff --git a/demo-app-remix/app/routes/tictactoe/index.tsx b/demo-app-remix/app/routes/tictactoe/index.tsx new file mode 100644 index 000000000..8cd5fae85 --- /dev/null +++ b/demo-app-remix/app/routes/tictactoe/index.tsx @@ -0,0 +1,13 @@ +import Board from '../../components/Board'; +import Box from '../../components/Box'; +import Row from '../../components/Row'; +import Navbar from '../../components/navbar'; + +export default function Tictactoe() { + return ( +
+ + +
+ ); +} diff --git a/demo-app-remix/app/styles/style.css b/demo-app-remix/app/styles/style.css new file mode 100644 index 000000000..b8a4b09d8 --- /dev/null +++ b/demo-app-remix/app/styles/style.css @@ -0,0 +1,179 @@ +body { + margin: 0; + font-family: Arial, Helvetica, sans-serif; + background-color: #fff4f4; +} + +h1, +h4 { + text-align: center; +} + +/* Navbar */ + +.nav { + background-color: #ff6569; + + display: flex; + justify-content: space-evenly; + + padding: 30px; + height: 30px; +} + +.link { + flex-grow: 1; + + font-size: 1.5em; + text-decoration: none; + text-align: center; + + color: #fff4f4; +} + +.link:hover { + font-size: 2em; +} + +/* About */ + +.about { + background-color: #ffffff; + color: #330002; + + padding-top: 1em; + padding-bottom: 2em; + padding-right: 4em; + padding-left: 4em; + margin-top: 2em; + + max-width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +/* Tic-Tac-Toe */ + +.board { + background-color: #ffffff; + color: #330002; + + margin-top: 2em; + + padding-top: 1em; + padding-bottom: 1em; + padding-left: 4em; + padding-right: 4em; + + width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +.box { + background-color: #62d6fb; + border-style: solid; + border-color: #ffffff; + border-radius: 5px; + + height: 100px; + width: 100px; + + font-size: 4em; +} + +#reset { + color: #ff6569; + font-size: 1.5em; + + background-color: #ffffff; + border-style: solid; + border-color: #ff6569; + border-radius: 5px; + + margin-top: 20px; + margin-bottom: 20px; + + width: 100%; + + padding: 0.5em; +} + +#reset:hover { + color: #ffffff; + background-color: #ff6569; +} + +/* Counter */ + +.buttons { + background-color: #ffffff; + color: #330002; + + padding-top: 1em; + padding-bottom: 2em; + padding-right: 4em; + padding-left: 4em; + margin-top: 2em; + + max-width: 300px; + margin-left: auto; + margin-right: auto; + + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; +} + +.buttonstyle { + color: #ffffff; + font-size: 1.5em; + + background-color: #ff6569; + border-style: solid; + border-color: #ffffff; + border-radius: 5px; + + margin-top: -20px; + margin-bottom: 10px; + + width: 100%; + + padding: 0.5em; +} + +.description { + margin-bottom: 30px; +} + +.increment { + color: #ffffff; + font-size: 1.5em; + + background-color: #FF6569; + border-style: solid; + border-color: #ffffff; + border-radius: 5px; + + margin-top: 20px; + margin-bottom: 20px; + + width: 100%; + + padding: .5em; +} + +.increment:hover { + background-color: #62d6fb; +} + +.hide { + display: none; +} + +.show { + display: block; + background-color: lightblue; +} diff --git a/demo-app-remix/app/types/types.ts b/demo-app-remix/app/types/types.ts new file mode 100644 index 000000000..cc8252e42 --- /dev/null +++ b/demo-app-remix/app/types/types.ts @@ -0,0 +1,10 @@ +export type Scoreboard = { + X: number; + O: number; +}; + +export type Player = 'X' | 'O'; + +export type BoardText = 'X' | 'O' | '-'; + +export type BoardContent = Array>; diff --git a/demo-app-remix/package.json b/demo-app-remix/package.json new file mode 100644 index 000000000..5add33863 --- /dev/null +++ b/demo-app-remix/package.json @@ -0,0 +1,39 @@ +{ + "private": true, + "sideEffects": false, + "scripts": { + "build": "remix build", + "dev": "npm-run-all build --parallel \"dev:*\"", + "dev:node": "cross-env NODE_ENV=development nodemon --require dotenv/config ./server.ts --watch ./server.ts", + "dev:remix": "remix watch", + "start": "cross-env NODE_ENV=production node ./server.ts", + "typecheck": "tsc" + }, + "dependencies": { + "@remix-run/express": "^1.14.1", + "@remix-run/node": "^1.14.1", + "@remix-run/react": "^1.14.1", + "compression": "^1.7.4", + "cross-env": "^7.0.3", + "express": "^4.18.2", + "isbot": "^3.6.5", + "morgan": "^1.10.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "ts-node": "^10.9.2" + }, + "devDependencies": { + "@remix-run/dev": "^1.14.1", + "@remix-run/eslint-config": "^1.14.1", + "@types/react": "^18.0.25", + "@types/react-dom": "^18.0.8", + "dotenv": "^16.0.3", + "eslint": "^8.27.0", + "nodemon": "^2.0.20", + "npm-run-all": "^4.1.5", + "typescript": "^4.8.4" + }, + "engines": { + "node": ">=14" + } +} diff --git a/demo-app-remix/public/favicon.ico b/demo-app-remix/public/favicon.ico new file mode 100644 index 000000000..8830cf682 Binary files /dev/null and b/demo-app-remix/public/favicon.ico differ diff --git a/demo-app-remix/remix.config.js b/demo-app-remix/remix.config.js new file mode 100644 index 000000000..ddeb138df --- /dev/null +++ b/demo-app-remix/remix.config.js @@ -0,0 +1,8 @@ +/** @type {import('@remix-run/dev').AppConfig} */ +module.exports = { + ignoredRouteFiles: ['**/.*'], + // appDirectory: "app", + // assetsBuildDirectory: 'public/build', + // serverBuildPath: "build/index.js", + // publicPath: "/build/", +}; diff --git a/demo-app-remix/remix.env.d.ts b/demo-app-remix/remix.env.d.ts new file mode 100644 index 000000000..dcf8c45e1 --- /dev/null +++ b/demo-app-remix/remix.env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/demo-app-remix/server.ts b/demo-app-remix/server.ts new file mode 100644 index 000000000..1e24b658d --- /dev/null +++ b/demo-app-remix/server.ts @@ -0,0 +1,59 @@ +export {}; //JR: added to fix this error message: 'server.ts' cannot be compiled under '--isolatedModules' because it is considered a global script file. Add an import, export, or an empty 'export {}' statement to make it a module. +const path = require('path'); +const express = require('express'); +const compression = require('compression'); +const morgan = require('morgan'); +const { createRequestHandler } = require('@remix-run/express'); + +const BUILD_DIR = path.join(process.cwd(), 'build'); + +const app = express(); + +app.use(compression()); + +// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header +app.disable('x-powered-by'); + +// Remix fingerprints its assets so we can cache forever. +app.use('/build', express.static('public/build', { immutable: true, maxAge: '1y' })); + +// Everything else (like favicon.ico) is cached for an hour. You may want to be +// more aggressive with this caching. +app.use(express.static('public', { maxAge: '1h' })); + +app.use(morgan('tiny')); + +app.all( + '*', + process.env.NODE_ENV === 'development' + ? (req: any, res: any, next: any) => { + purgeRequireCache(); + + return createRequestHandler({ + build: require(BUILD_DIR), + mode: process.env.NODE_ENV, + })(req, res, next); + } + : createRequestHandler({ + build: require(BUILD_DIR), + mode: process.env.NODE_ENV, + }), +); +const port: number | string = process.env.PORT || 3003; + +app.listen(port, () => { + console.log(`Express server listening on port ${port}`); +}); + +function purgeRequireCache() { + // purge require cache on requests for "server side HMR" this won't let + // you have in-memory objects between requests in development, + // alternatively you can set up nodemon/pm2-dev to restart the server on + // file changes, but then you'll have to reconnect to databases/etc on each + // change. We prefer the DX of this, so we've included it for you by default + for (const key in require.cache) { + if (key.startsWith(BUILD_DIR)) { + delete require.cache[key]; + } + } +} diff --git a/demo-app-remix/tsconfig.json b/demo-app-remix/tsconfig.json new file mode 100644 index 000000000..0de0b2ee4 --- /dev/null +++ b/demo-app-remix/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx", "server.ts"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2019"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "resolveJsonModule": true, + "target": "ES2019", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + + // Remix takes care of building everything in `remix build`. + "noEmit": true + } +} diff --git a/demo-app/.babelrc b/demo-app/.babelrc new file mode 100644 index 000000000..a45d619c1 --- /dev/null +++ b/demo-app/.babelrc @@ -0,0 +1,7 @@ + +{ + "presets": [ + "@babel/preset-env", + "@babel/preset-react" + ] +} \ No newline at end of file diff --git a/demo-app/.gitignore b/demo-app/.gitignore new file mode 100644 index 000000000..40b878db5 --- /dev/null +++ b/demo-app/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/demo-app/package.json b/demo-app/package.json new file mode 100644 index 000000000..4003e82fa --- /dev/null +++ b/demo-app/package.json @@ -0,0 +1,41 @@ +{ + "name": "typescript-module", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "webpack-dev-server", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies": { + "@babel/core": "^7.16.7", + "@babel/plugin-transform-runtime": "^7.25.9", + "@babel/preset-env": "^7.16.7", + "@babel/preset-react": "^7.16.7", + "@types/express": "^4.17.13", + "@types/node": "^17.0.8", + "@types/react": "^17.0.38", + "@types/react-dom": "^17.0.19", + "babel-loader": "^8.2.3", + "copy-webpack-plugin": "^10.2.0", + "core-js": "^3.39.0", + "css-loader": "^6.5.1", + "html-webpack-plugin": "^5.5.0", + "node": "^16.0.0", + "nodemon": "^2.0.15", + "ts-loader": "^9.2.6", + "typescript": "^4.5.4", + "webpack": "^5.65.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "^4.7.2" + }, + "dependencies": { + "@mui/styled-engine-sc": "^5.12.0", + "express": "^4.17.2", + "react": "^18.1.0", + "react-dom": "^18.1.0", + "react-router-dom": "^6.3.0", + "ts-node": "^10.4.0", + "use-immer": "^0.9.0" + } +} diff --git a/demo-app/src/client/Components/Board.tsx b/demo-app/src/client/Components/Board.tsx new file mode 100644 index 000000000..102f2a2bb --- /dev/null +++ b/demo-app/src/client/Components/Board.tsx @@ -0,0 +1,141 @@ +import React, { Component } from 'react'; +import Row from './Row'; +import { BoardContent, Scoreboard, Player } from './../../types'; +//Took away BoardText from import + +//thinking about changing this to an interface +type BoardState = { + board: BoardContent; + currentPlayer: Player; + gameOver: boolean; + message: string; + scoreboard: Scoreboard; +}; + +//changed props to unknown instead of any +class Board extends Component<{}, BoardState> { + constructor(props: unknown) { + super(props); + this.state = { + board: this.newBoard(), + currentPlayer: 'X', + gameOver: false, + message: '', + scoreboard: { X: 0, O: 0 }, + }; + + this.resetBoard = this.resetBoard.bind(this); + this.handleBoxClick = this.handleBoxClick.bind(this); + } + + //added void + componentDidUpdate(): void { + this.checkForWinner() + } + + /** + * @method newBoard + * @description - returns a blank BoardContent array, + * for the start of a new game + */ + newBoard(): BoardContent { + return [ + ['-', '-', '-'], + ['-', '-', '-'], + ['-', '-', '-'], + ]; + } + + /** + * @method resetBoard + * @description - sets to board object to be all '-', + * and sets gameOver and message to default state + */ + resetBoard(): void { + this.setState({ + gameOver: false, + board: this.newBoard(), + message: '', + }); + } + + /** + * @method checkForWinner + * @description - checks to see if either player has filled a row + * if so, ends the game and updates the message to declare winner + */ + checkForWinner(): void { + const { board, gameOver, currentPlayer } = this.state; + + const spacesLeft = (): boolean => { + for (let i of board) { + if (i.includes('-')) return true; + } + return false; + }; + + if (!gameOver) { + // win conditions: matching rows, columns, or diagonals, that are not empty('-') + if ( + (board[0][0] === board[0][1] && board[0][1] === board[0][2] && board[0][2] !== '-') || + (board[1][0] === board[1][1] && board[1][1] === board[1][2] && board[1][2] !== '-') || + (board[2][0] === board[2][1] && board[2][1] === board[2][2] && board[2][2] !== '-') || + (board[0][0] === board[1][0] && board[1][0] === board[2][0] && board[2][0] !== '-') || + (board[0][1] === board[1][1] && board[1][1] === board[2][1] && board[2][1] !== '-') || + (board[0][2] === board[1][2] && board[1][2] === board[2][2] && board[2][2] !== '-') || + (board[0][0] === board[1][1] && board[1][1] === board[2][2] && board[2][2] !== '-') || + (board[2][0] === board[1][1] && board[1][1] === board[0][2] && board[0][2] !== '-') + ) { + // winner is the person who's turn was previous + const winner: Player = currentPlayer === 'X' ? 'O' : 'X'; + + this.setState({ + gameOver: true, + message: `Player ${winner} wins!`, + }); + + // draw condition: no '-' remaining in board without above win condition triggering + } else if (!spacesLeft()) { + this.setState({ + gameOver: true, + message: 'Draw!', + }); + } + } + } + + handleBoxClick(row: number, column: number): void { + const boardCopy: BoardContent = [ + [...this.state.board[0]], + [...this.state.board[1]], + [...this.state.board[2]], + ]; + boardCopy[row][column] = this.state.currentPlayer; + const newPlayer: Player = this.state.currentPlayer === 'X' ? 'O' : 'X'; + this.setState({ board: boardCopy, currentPlayer: newPlayer }); + } + + //added type for render + render(): JSX.Element { + const rows: Array = []; + for (let i = 0; i < 3; i++) { + rows.push( + , + ); + } + // const { X, O }: Scoreboard = this.state.scoreboard; + + return ( +
+

Tic Tac Toe

+ {this.state.gameOver &&

{this.state.message}

} + {rows} + +
+ ); + } +} + +export default Board; diff --git a/demo-app/src/client/Components/Box.tsx b/demo-app/src/client/Components/Box.tsx new file mode 100644 index 000000000..d9c45512e --- /dev/null +++ b/demo-app/src/client/Components/Box.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { BoardText } from '../../types'; + +type BoxProps = { + value: BoardText; + row: number; + column: number; + handleBoxClick: (row: number, column: number) => void; +}; + +const Box = (props: BoxProps): JSX.Element => { + return ( + <> + + + ); +}; + +export default Box; diff --git a/demo-app/src/client/Components/Buttons.tsx b/demo-app/src/client/Components/Buttons.tsx new file mode 100644 index 000000000..60235aa92 --- /dev/null +++ b/demo-app/src/client/Components/Buttons.tsx @@ -0,0 +1,82 @@ +import React, { Component, useState } from 'react'; + +type ButtonProps = { + id: string; + label: string; + color?: string; + initialCount?: number; +}; + +type IncrementClassState = { + count: number; +}; + +class IncrementClass extends Component { + state = { + count: this.props.initialCount || 0, + }; + + handleClick = (): void => { + this.setState((prevState: IncrementClassState) => ({ + count: prevState.count + 1, + })); + }; + + render(): JSX.Element { + return ( +
+ +
+ ); + } +} + +const IncrementFunction = (props: ButtonProps): JSX.Element => { + const [count, setCount] = useState(props.initialCount || 0); + + const handleClick = (): void => { + setCount((prev) => prev + 1); + }; + + return ( +
+ +
+ ); +}; + +class Buttons extends Component { + render(): JSX.Element { + return ( +
+

Mixed State Counter

+

First two buttons use class components, last two use function components.

+ + + + +
+ ); + } +} + +export default Buttons; diff --git a/demo-app/src/client/Components/FunctionalReducerCounter.tsx b/demo-app/src/client/Components/FunctionalReducerCounter.tsx new file mode 100644 index 000000000..85effd454 --- /dev/null +++ b/demo-app/src/client/Components/FunctionalReducerCounter.tsx @@ -0,0 +1,231 @@ +import React, { useState, useReducer } from 'react'; + +type CounterProps = { + initialCount?: number; + step?: number; + title?: string; + theme?: { + backgroundColor?: string; + textColor?: string; + }; +}; + +type CounterState = { + count: number; + history: number[]; + lastAction: string; +}; + +type CounterAction = + | { type: 'INCREMENT' } + | { type: 'DECREMENT' } + | { type: 'DOUBLE' } + | { type: 'RESET' } + | { type: 'ADD'; payload: number } + | { type: 'SET_STATE'; payload: CounterState }; + +type SecondaryCounterState = { + count: number; + multiplier: number; + lastOperation: string; + history: number[]; +}; + +type SecondaryCounterAction = + | { type: 'MULTIPLY' } + | { type: 'DIVIDE' } + | { type: 'SET_MULTIPLIER'; payload: number } + | { type: 'RESET' } + | { type: 'SET_STATE'; payload: SecondaryCounterState }; + +function counterReducer(state: CounterState, action: CounterAction, step: number): CounterState { + switch (action.type) { + case 'INCREMENT': + return { + ...state, + count: state.count + step, + history: [...state.history, state.count + step], + lastAction: 'INCREMENT', + }; + case 'DECREMENT': + return { + ...state, + count: state.count - step, + history: [...state.history, state.count - step], + lastAction: 'DECREMENT', + }; + case 'DOUBLE': + return { + ...state, + count: state.count * 2, + history: [...state.history, state.count * 2], + lastAction: 'DOUBLE', + }; + case 'RESET': + return { + count: 0, + history: [], + lastAction: 'RESET', + }; + case 'ADD': + return { + ...state, + count: state.count + action.payload, + history: [...state.history, state.count + action.payload], + lastAction: `ADD ${action.payload}`, + }; + case 'SET_STATE': + return { + ...action.payload, + lastAction: 'SET_STATE', + }; + default: + return state; + } +} + +function secondaryCounterReducer( + state: SecondaryCounterState, + action: SecondaryCounterAction, +): SecondaryCounterState { + switch (action.type) { + case 'MULTIPLY': + return { + ...state, + count: state.count * state.multiplier, + history: [...state.history, state.count * state.multiplier], + lastOperation: `Multiplied by ${state.multiplier}`, + }; + case 'DIVIDE': + return { + ...state, + count: state.count / state.multiplier, + history: [...state.history, state.count / state.multiplier], + lastOperation: `Divided by ${state.multiplier}`, + }; + case 'SET_MULTIPLIER': + return { + ...state, + multiplier: action.payload, + history: [...state.history], + lastOperation: `Set multiplier to ${action.payload}`, + }; + case 'RESET': + return { + count: 0, + multiplier: 2, + history: [], + lastOperation: 'Reset', + }; + case 'SET_STATE': + return { + ...action.payload, + lastOperation: 'SET_STATE', + }; + default: + return state; + } +} + +function FunctionalReducerCounter({ + initialCount = 0, + step = 1, + title = 'Function-based Reducer Counter', + theme = { + backgroundColor: '#ffffff', + textColor: '#330002', + }, +}: CounterProps): JSX.Element { + const [clickCount, setClickCount] = useState(0); + const [lastClickTime, setLastClickTime] = useState(null); + const [averageTimeBetweenClicks, setAverageTimeBetweenClicks] = useState(0); + + const [state, dispatch] = useReducer( + (state: CounterState, action: CounterAction) => counterReducer(state, action, step), + { + count: initialCount, + history: [], + lastAction: 'none', + }, + ); + + const [secondaryState, secondaryDispatch] = useReducer(secondaryCounterReducer, { + count: initialCount, + multiplier: 2, + history: [], + lastOperation: 'none', + }); + + return ( +
+

{title}

+ +
+

Primary Counter: {state.count}

+
+ +
+ + + + + +
+ +
+

History:

+
+ {state.history.map((value, index) => ( + + {value} + {index < state.history.length - 1 ? ' → ' : ''} + + ))} +
+
+ +
+

Secondary Counter: {secondaryState.count}

+
+ + + + +
+
+

Current Multiplier: {secondaryState.multiplier}

+

History:

+
+ {secondaryState.history.map((value, index) => ( + + {value} + {index < secondaryState.history.length - 1 ? ' → ' : ''} + + ))} +
+
+
+
+ ); +} + +export default FunctionalReducerCounter; diff --git a/demo-app/src/client/Components/FunctionalStateCounter.tsx b/demo-app/src/client/Components/FunctionalStateCounter.tsx new file mode 100644 index 000000000..a49916177 --- /dev/null +++ b/demo-app/src/client/Components/FunctionalStateCounter.tsx @@ -0,0 +1,97 @@ +import React, { useState } from 'react'; + +type CounterProps = { + initialCount?: number; + step?: number; + title?: string; + theme?: { + backgroundColor?: string; + textColor?: string; + }; +}; + +function FunctionalStateCounter({ + initialCount = 0, + step = 1, + title = 'Function-based State Counter', + theme = { + backgroundColor: '#ffffff', + textColor: '#330002', + }, +}: CounterProps): JSX.Element { + const [count, setCount] = useState(initialCount); + const [history, setHistory] = useState([]); + const [lastAction, setLastAction] = useState('none'); + + const handleAction = (type: string, payload?: number) => { + let newCount = count; + switch (type) { + case 'INCREMENT': + newCount = count + step; + setCount(newCount); + setHistory([...history, newCount]); + setLastAction('INCREMENT'); + break; + case 'DECREMENT': + newCount = count - step; + setCount(newCount); + setHistory([...history, newCount]); + setLastAction('DECREMENT'); + break; + case 'DOUBLE': + newCount = count * 2; + setCount(newCount); + setHistory([...history, newCount]); + setLastAction('DOUBLE'); + break; + case 'ADD': + newCount = count + (payload || 0); + setCount(newCount); + setHistory([...history, newCount]); + setLastAction(`ADD ${payload}`); + break; + case 'RESET': + setCount(0); + setHistory([]); + setLastAction('RESET'); + break; + } + }; + + return ( +
+

{title}

+
+

Current Count: {count}

+
+ +
+ + + + + +
+ +
+

History:

+
+ {history.map((value, index) => ( + + {value} + {index < history.length - 1 ? ' → ' : ''} + + ))} +
+
+
+ ); +} + +export default FunctionalStateCounter; diff --git a/demo-app/src/client/Components/Home.tsx b/demo-app/src/client/Components/Home.tsx new file mode 100644 index 000000000..9952f9e29 --- /dev/null +++ b/demo-app/src/client/Components/Home.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { useTheme } from '../../contexts/ThemeContext'; +import { useAuth } from '../../contexts/AuthContext'; + +function Home(): JSX.Element { + const { theme } = useTheme(); + const { user, login, logout } = useAuth(); + + return ( +
+

REACTIME - DEMO APP

+ + {user ? ( +
+

Welcome, {user.username}!

+ +
+ ) : ( +
+

Please log in:

+ + +
+ )} +
+ ); +} +export default Home; diff --git a/demo-app/src/client/Components/Nav.tsx b/demo-app/src/client/Components/Nav.tsx new file mode 100644 index 000000000..274c5ae39 --- /dev/null +++ b/demo-app/src/client/Components/Nav.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import ThemeToggle from './ThemeToggle'; + +function Nav(): JSX.Element { + return ( +
+ + About + + + Tic-Tac-Toe + + + State Counter + + + Reducer Counter + + +
+ ); +} + +export default Nav; diff --git a/demo-app/src/client/Components/ReducerCounter.tsx b/demo-app/src/client/Components/ReducerCounter.tsx new file mode 100644 index 000000000..65b5d6155 --- /dev/null +++ b/demo-app/src/client/Components/ReducerCounter.tsx @@ -0,0 +1,141 @@ +import React, { Component } from 'react'; + +type CounterProps = { + initialCount?: number; + step?: number; + title?: string; + theme?: { + backgroundColor?: string; + textColor?: string; + }; +}; + +type CounterState = { + count: number; + history: number[]; + lastAction: string; +}; + +type CounterAction = + | { type: 'INCREMENT' } + | { type: 'DECREMENT' } + | { type: 'DOUBLE' } + | { type: 'RESET' } + | { type: 'ADD'; payload: number }; + +class ReducerCounter extends Component { + static defaultProps = { + initialCount: 0, + step: 1, + title: 'Class-based Reducer Counter', + theme: { + backgroundColor: '#ffffff', + textColor: '#330002', + }, + }; + + static initialState(initialCount: number): CounterState { + return { + count: initialCount, + history: [], + lastAction: 'none', + }; + } + + static reducer(state: CounterState, action: CounterAction, step: number): CounterState { + switch (action.type) { + case 'INCREMENT': + return { + ...state, + count: state.count + step, + history: [...state.history, state.count + step], + lastAction: 'INCREMENT', + }; + case 'DECREMENT': + return { + ...state, + count: state.count - step, + history: [...state.history, state.count - step], + lastAction: 'DECREMENT', + }; + case 'DOUBLE': + return { + ...state, + count: state.count * 2, + history: [...state.history, state.count * 2], + lastAction: 'DOUBLE', + }; + case 'RESET': + return { + ...ReducerCounter.initialState(0), + lastAction: 'RESET', + }; + case 'ADD': + return { + ...state, + count: state.count + action.payload, + history: [...state.history, state.count + action.payload], + lastAction: `ADD ${action.payload}`, + }; + default: + return state; + } + } + + constructor(props: CounterProps) { + super(props); + this.state = ReducerCounter.initialState(props.initialCount || 0); + this.dispatch = this.dispatch.bind(this); + } + + dispatch(action: CounterAction): void { + this.setState((currentState) => + ReducerCounter.reducer(currentState, action, this.props.step || 1), + ); + } + + render(): JSX.Element { + const { title, theme } = this.props; + + return ( +
+

{title}

+
+

Current Count: {this.state.count}

+
+ +
+ + + + + +
+ +
+

History:

+
+ {this.state.history.map((value, index) => ( + + {value} + {index < this.state.history.length - 1 ? ' → ' : ''} + + ))} +
+
+
+ ); + } +} + +export default ReducerCounter; diff --git a/demo-app/src/client/Components/Row.tsx b/demo-app/src/client/Components/Row.tsx new file mode 100644 index 000000000..1fcc267b2 --- /dev/null +++ b/demo-app/src/client/Components/Row.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import Box from './Box'; +import { BoardText } from '../../types'; + +type RowProps = { + handleBoxClick: (row: number, column: number) => void; + values: Array; + row: number; +}; + +const Row = (props: RowProps): JSX.Element => { + const boxes: Array = []; + for (let i = 0; i < 3; i++) { + boxes.push( + , + ); + } + + return ( + <> +
{boxes}
+ + ); +}; + +export default Row; diff --git a/demo-app/src/client/Components/ThemeToggle.tsx b/demo-app/src/client/Components/ThemeToggle.tsx new file mode 100644 index 000000000..81f203747 --- /dev/null +++ b/demo-app/src/client/Components/ThemeToggle.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { useTheme } from '../../contexts/ThemeContext'; + +const ThemeToggle = (): JSX.Element => { + const { theme, toggleTheme } = useTheme(); + const isDark = theme.backgroundColor === '#1a202c'; + + return ( + + ); +}; + +export default ThemeToggle; diff --git a/demo-app/src/client/Router.tsx b/demo-app/src/client/Router.tsx new file mode 100644 index 000000000..619dfce8b --- /dev/null +++ b/demo-app/src/client/Router.tsx @@ -0,0 +1,59 @@ +// src/client/Router.tsx +import * as React from 'react'; +import { createRoot } from 'react-dom/client'; +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { ThemeProvider } from '../contexts/ThemeContext'; +import { AuthProvider } from '../contexts/AuthContext'; +import Nav from './Components/Nav'; +import Board from './Components/Board'; +import Home from './Components/Home'; +import Buttons from './Components/Buttons'; +import ReducerCounter from './Components/ReducerCounter'; +import FunctionalReducerCounter from './Components/FunctionalReducerCounter'; +import FunctionalStateCounter from './Components/FunctionalStateCounter'; + +const domNode = document.getElementById('root'); +if (!domNode) throw new Error('Root element not found'); +const root = createRoot(domNode); + +const CounterPage = () => ( +
+ + + +
+); + +root.render( + + + +