├── .eslintrc.json ├── .github └── workflows │ └── submit.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── fonts │ └── Ubuntu-Regular.ttf ├── icon.png └── icons │ ├── categories │ ├── cms.svg │ ├── colors.svg │ ├── favorite.svg │ ├── fonts.svg │ ├── forms.svg │ ├── icons.svg │ ├── image-compression.svg │ ├── learning.svg │ ├── logo-creation.svg │ ├── paas.svg │ ├── pictures.svg │ ├── project-management.svg │ ├── web-hosting.svg │ └── web-templates.svg │ └── general │ ├── brand.svg │ ├── burger.svg │ ├── collapse.svg │ ├── expand.svg │ ├── favorite.svg │ ├── link-chain.svg │ ├── logo.svg │ ├── non-favorite.svg │ ├── open.svg │ └── search.svg ├── package.json ├── postcss.config.js ├── src ├── components │ ├── BurgerButton.tsx │ ├── Button.tsx │ ├── Card.tsx │ ├── CategoryCard.tsx │ ├── CategoryCardItem.tsx │ ├── CategoryMenu.tsx │ ├── Footer.tsx │ ├── Header.tsx │ ├── LinkCard.tsx │ ├── MainContainer.tsx │ ├── MenuDivider.tsx │ ├── NavigationMenu │ │ ├── NavigationMenu.tsx │ │ ├── index.ts │ │ └── utils.ts │ ├── SearchBar.tsx │ └── WelcomeInfo.tsx ├── context │ ├── favoriteLinks.ts │ ├── selectedCategory.ts │ └── selectedLink.ts ├── data │ ├── category-icons.ts │ └── links │ │ ├── cms │ │ ├── contentful.ts │ │ ├── index.ts │ │ ├── keystone.ts │ │ ├── saleor.ts │ │ ├── sanity.ts │ │ ├── shopify.ts │ │ ├── strapi.ts │ │ ├── wagtail.ts │ │ └── wordpress.ts │ │ ├── colors │ │ ├── color-hunt.ts │ │ ├── colors-and-fonts.ts │ │ ├── coolors.ts │ │ ├── fffuel.ts │ │ └── index.ts │ │ ├── fonts │ │ ├── 1001-free-fonts.ts │ │ ├── befonts.ts │ │ ├── dafont.ts │ │ ├── font-space.ts │ │ ├── google-fonts.ts │ │ ├── index.ts │ │ ├── myfonts.ts │ │ └── what-font-is.ts │ │ ├── forms │ │ ├── getform.ts │ │ ├── index.ts │ │ └── tally.ts │ │ ├── icons │ │ ├── boxicons.ts │ │ ├── flaticon.ts │ │ ├── font-awesome.ts │ │ ├── freeicons.ts │ │ ├── iconfinder.ts │ │ ├── iconify.ts │ │ ├── icons8.ts │ │ ├── iconscout.ts │ │ ├── iconstore.ts │ │ ├── index.ts │ │ ├── lordicon.ts │ │ ├── reshot.ts │ │ ├── simple-icons.ts │ │ ├── svg-repo.ts │ │ ├── tabler-icons.ts │ │ └── uxwing.ts │ │ ├── image-compression │ │ ├── compressimage-io.ts │ │ ├── compressor.ts │ │ ├── index.ts │ │ ├── svgminify.ts │ │ ├── svgomg.ts │ │ └── tinypng.ts │ │ ├── index.ts │ │ ├── learning │ │ ├── developer-roadmaps.ts │ │ ├── edx.ts │ │ ├── exercism.ts │ │ ├── free-code-camp.ts │ │ ├── fullstack-open.ts │ │ ├── index.ts │ │ ├── mimo.ts │ │ ├── mit-ocw.ts │ │ ├── open-bootcamp.ts │ │ ├── refactoring-guru.ts │ │ ├── scrimba.ts │ │ ├── teach-yourself-computer-science.ts │ │ ├── the-algorithms.ts │ │ ├── the-odin-project.ts │ │ └── w3schools.ts │ │ ├── logo-creation │ │ ├── adobe-express-logo-maker.ts │ │ ├── brandmark.ts │ │ ├── index.ts │ │ ├── logoai.ts │ │ ├── logopony.ts │ │ ├── looka.ts │ │ └── smashing-logo.ts │ │ ├── paas │ │ ├── adaptable.ts │ │ ├── control-plane.ts │ │ ├── cyclic.ts │ │ ├── deno-deploy.ts │ │ ├── deta.ts │ │ ├── fl0.ts │ │ ├── fly-io.ts │ │ ├── glitch.ts │ │ ├── index.ts │ │ ├── northflank.ts │ │ ├── qoddi.ts │ │ ├── railway.ts │ │ └── render.ts │ │ ├── pictures │ │ ├── index.ts │ │ ├── magdeleine.ts │ │ ├── negative-space.ts │ │ ├── pexels.ts │ │ ├── picspree.ts │ │ ├── pixabay.ts │ │ ├── px-here.ts │ │ ├── realistic-shots.ts │ │ ├── undraw.ts │ │ └── unsplash.ts │ │ ├── project-management │ │ ├── azure-devops.ts │ │ ├── index.ts │ │ └── taiga.ts │ │ ├── web-hosting │ │ ├── contabo.ts │ │ ├── digitalocean.ts │ │ ├── free-hosting-no-ads.ts │ │ ├── hetzner.ts │ │ ├── hostinger.ts │ │ ├── index.ts │ │ ├── netlify.ts │ │ ├── pythonanywhere.ts │ │ └── vercel.ts │ │ └── web-templates │ │ ├── astro-themes.ts │ │ ├── index.ts │ │ └── statichunt.ts ├── features │ ├── favoriteLinks.ts │ └── searchLinks.ts ├── options.tsx ├── popup.tsx ├── style.css └── tabs │ └── browse-links.tsx ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "plugin:react/recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | "plugin:react-hooks/recommended" 11 | ], 12 | "parserOptions": { 13 | "ecmaVersion": "latest", 14 | "sourceType": "module", 15 | "project": ["tsconfig.json"] 16 | }, 17 | "plugins": ["react-refresh","react"], 18 | "rules": { 19 | "indent": ["error", 4], 20 | "semi": "error", 21 | "react-refresh/only-export-components": "warn", 22 | "react/jsx-uses-react": "off", 23 | "react/react-in-jsx-scope": "off" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/submit.yml: -------------------------------------------------------------------------------- 1 | name: "Submit to Web Store" 2 | on: 3 | push: 4 | branches: 5 | - 'submit-webstores-action' 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Cache pnpm modules 13 | uses: actions/cache@v3 14 | with: 15 | path: ~/.pnpm-store 16 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} 17 | restore-keys: | 18 | ${{ runner.os }}- 19 | - uses: pnpm/action-setup@v2.2.4 20 | with: 21 | version: latest 22 | run_install: true 23 | - name: Use Node.js 16.x 24 | uses: actions/setup-node@v3.4.1 25 | with: 26 | node-version: 16.x 27 | cache: "pnpm" 28 | - name: Build firefox extension 29 | run: pnpm build --target=firefox-mv2 --zip 30 | - name: Build edge extension 31 | run: pnpm build --target=edge-mv3 --zip 32 | - name: Build chrome extension 33 | run: pnpm build --zip 34 | - name: Browser Platform Publish 35 | uses: PlasmoHQ/bpp@v3 36 | with: 37 | keys: ${{ secrets.SUBMIT_KEYS }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | /node_modules 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | #cache 13 | .turbo 14 | 15 | # misc 16 | .DS_Store 17 | *.pem 18 | 19 | # debug 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | .pnpm-debug.log* 24 | 25 | # local env files 26 | .env* 27 | 28 | out/ 29 | build/ 30 | dist/ 31 | 32 | # plasmo - https://www.plasmo.com 33 | .plasmo 34 | 35 | # bpp - http://bpp.browser.market/ 36 | keys.json 37 | 38 | # typescript 39 | .tsbuildinfo 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 |
4 | Table of Contents 5 |
    6 |
  1. 7 | General information 8 |
  2. 9 |
  3. 10 | Adding new link to a category 11 |
  4. 12 |
  5. 13 | Adding new category 14 |
  6. 15 |
16 |
17 | 18 | ## General information 19 | 20 | For contributing with changes you must: 21 | 22 | 1. Fork Dev Links repository. 23 | 2. Make your changes and push them to the forked repository. 24 | 3. Create a Pull Request in Dev Links repository, expressing the changes you made. Your Pull Request will be reviewed for approval as soon as possible. 25 | 26 | You can contribute giving suggestions and notifying about any bug you have 27 | encountered. 28 | 29 | The project has the following structure: 30 | 31 | ``` 32 | / 33 | ├── assets/ 34 | │ └── icons/ 35 | | ├── categories/ # all icons that appears next to category name 36 | | └── general/ # the rest of the icons used in the extension 37 | | 38 | ├── src/ 39 | │ ├── components/ # All React components 40 | │ ├── context/ # react contexts 41 | | └── data/ # All static data about links and functions related to it 42 | | └── links/ # All links info seperated by category folders 43 | | ├── features/ 44 | | └── tabs/ 45 | | 46 | └── package.json 47 | ``` 48 | 49 | ## Adding new link to a category 50 | 51 | For adding a link to a specific category you have to create a file like `/src/data/links/{category}/{link-name}.ts`. 52 | Where `category` is the existent category folder which your link belongs to, and `link-name` is a TypeScript 53 | file with the name of your link in lowercase and each word being seperated by `-` character. 54 | 55 | As example we will assume we are adding Google Fonts, that belongs to the fonts category. So, we have to create 56 | the link file `/src/data/links/fonts/google-fonts.ts` for Google Font's link. 57 | Inside that file you have to define and export an object representing the link. 58 | 59 | ```typescript 60 | // Import the link type from the index.ts which is inside 61 | // the font category folder. The type will vary 62 | // depending on the category you are adding to 63 | import type { FontsLink } from "./"; 64 | 65 | 66 | const GoogleFontDescription = 67 | ` 68 | Make your website more beautiful, fast and open through great typography. 69 | `; 70 | 71 | // Create the link object using the link type of the category. 72 | // Write the variable name using Pascal Case. 73 | const GoogleFonts: FontsLink = { 74 | name: 'Google Fonts', 75 | category: 'Fonts', 76 | linkUrl: 'https://fonts.google.com/', 77 | description: GoogleFontDescription, 78 | }; 79 | 80 | 81 | export default GoogleFonts; 82 | ``` 83 | 84 | A link object must have: 85 | 1. `name` written in the same way as their creators write it. 86 | 2. `category` its value will be enforced by the link type you imported. 87 | 3. `linkUrl` using the https protocol and with a slash at the end. 88 | 4. `description` give a short description of the link. You could mentions the features you could see in that link, but always try to keep it short. It should not be empty. 89 | 90 | The link variable name must match the link name deleting whitespaces as in the sample code. If it is not possible for syntax issues, you can use underscore characters. For example a variable name for the link `5 Seconds Of Summer` 91 | could be written as `_5SecondsOfSummer`. 92 | 93 | Now you have to add your new link object to the category object it belongs to. You can do this in the 94 | `/src/data/links/fonts/index.ts` file, which is placed inside the category folder. 95 | 96 | ```typescript 97 | import type { Link, LinkCategory, LinkCategoryName } from ".."; 98 | import FontSpace from "./font-space"; 99 | // Import the link object you just created 100 | import GoogleFonts from "./google-fonts"; 101 | 102 | 103 | type FontsCategoryName = Extract; 104 | 105 | 106 | export type FontsLink = Omit & { 107 | category: FontsCategoryName 108 | }; 109 | 110 | 111 | const fontsCategoryLinks: Array = [ 112 | FontSpace, 113 | GoogleFonts, // Add your object in the array of links. Keep the alphabetical order 114 | ]; 115 | 116 | 117 | const fontsCategory: LinkCategory = { 118 | name: 'Fonts', 119 | links: fontsCategoryLinks 120 | }; 121 | 122 | 123 | export default fontsCategory; 124 | ``` 125 | 126 | 127 | After doing all this, you can start the development server to see your new link being shown 128 | in 🚀 Dev Links. If you don't know how to do this, see the [Getting started with development in README.md](/README.md). 129 | 130 | ## Adding new category 131 | 132 | For adding a new category you have to add the category name in the 133 | `LinkCategoryName` type defined in `/src/data/links/index.ts`. 134 | This name will appear later, in the GUI, representing the category. 135 | Let's use as example that you wish to add `Images` category. 136 | 137 | ```typescript 138 | export type LinkCategoryName = 'Fonts' | 'Icons' | 'Images'; 139 | ``` 140 | 141 | The name must be put in alphabetical order inside `LinkCategoryName`. 142 | 143 | Now you must create a new category folder like `/src/data/links/images`. 144 | If the name of your category has more than one word use `-` for seperating 145 | the words. 146 | 147 | Inside the folder you just created, add an `index.ts` file. This file 148 | will be used for defining the category object for the new links, 149 | and will export it as default. In this file you will add code similar 150 | to this, but without the comments: 151 | 152 | ```typescript 153 | import type { Link, LinkCategory, LinkCategoryName } from ".."; 154 | 155 | 156 | // Define the name type of the category 157 | // extracting 'Images' from LinkCategoryName 158 | type ImagesCategoryName = Extract; 159 | 160 | 161 | // Define the type for the links that will be added 162 | // to this new category 163 | export type ImagesLink = Omit & { 164 | category: ImagesCategoryName 165 | }; 166 | 167 | 168 | // This array will contain all links that will be added 169 | // later to this new category 170 | const imagesCategoryLinks: Array = []; 171 | 172 | 173 | // Create the category object 174 | const imagesCategory: LinkCategory = { 175 | name: 'Images', 176 | links: imagesCategoryLinks 177 | }; 178 | 179 | // And finally export as default the category object 180 | export default imagesCategory; 181 | ``` 182 | 183 | Now that you have created a new category object you can import it 184 | to the list of category objects in `/src/data/links/index.ts` like this: 185 | 186 | ```typescript 187 | import fontsCategory from "./fonts"; 188 | import iconsCategory from "./icons"; 189 | // Import the category object you just created 190 | import imagesCategory from "./images"; 191 | 192 | // You added the 'Images' category name some steps ago 193 | export type LinkCategoryName = 'Fonts' | 'Icons' | 'Images'; 194 | 195 | 196 | export type LinkCategory = { 197 | name: LinkCategoryName; 198 | links: Array 199 | }; 200 | 201 | 202 | export type Link = { 203 | name: string; 204 | category: LinkCategoryName; 205 | linkUrl: string; 206 | description: string; 207 | }; 208 | 209 | // The objects must be in alphabetical order within the array 210 | const linksByCategory: Array = [ 211 | fontsCategory, 212 | iconsCategory, 213 | imagesCategory, // add your category object 214 | ]; 215 | 216 | export default linksByCategory; 217 | ``` 218 | 219 | In the last part of this process you will have to add the icon that 220 | represents your new category. For that you have to add the icon 221 | in `.svg` format, this is for keeping Dev Links as lightweight 222 | as possible. The file name of the icon should match the name 223 | of the category folder you created. 224 | You will add the icon as `/assets/icons/categories/images.svg`. 225 | The icon must be `#652F9E` color and have outlined style, this is 226 | for keeping consistency with the other category icons. 227 | 228 | In the end you will have to add your icon in the `getIconForLinkCategory` 229 | function in `/src/data/category-icons.ts`: 230 | 231 | ```typescript 232 | import type { LinkCategoryName } from "./links"; 233 | import FontsCategoryIcon from "data-base64:~assets/icons/categories/fonts.svg"; 234 | import IconsCategoryIcon from "data-base64:~assets/icons/categories/icons.svg"; 235 | import FavoriteIcon from "data-base64:~assets/icons/categories/favorite.svg"; 236 | // Import your icon 237 | import ImagesCategoryIcon from "data-base64:~assets/icons/categories/images.svg"; 238 | 239 | 240 | export const getIconForLinkCategory = (category: LinkCategoryName | 'Favorites') => { 241 | 242 | switch (category) { 243 | 244 | case 'Fonts': 245 | return FontsCategoryIcon; 246 | 247 | case 'Icons': 248 | return IconsCategoryIcon; 249 | 250 | // Add your category and icon in the switch-case 251 | case 'Images': 252 | return ImagesCategoryIcon; 253 | 254 | default: 255 | return FavoriteIcon; 256 | } 257 | 258 | }; 259 | ``` 260 | 261 | Now, your new category will appear in the GUI of 🚀 Dev Links . -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Rolando Rio Garaboa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | Logo 5 |
6 | 7 | 8 |
9 | Table of Contents 10 |
    11 |
  1. 12 | About The Project 13 | 16 |
  2. 17 |
  3. 18 | Download 19 |
  4. 20 |
  5. 21 | Getting started with development 22 | 28 |
  6. 29 |
  7. Contributing
  8. 30 |
  9. License
  10. 31 |
  11. Contact
  12. 32 |
  13. Acknowledgments
  14. 33 |
34 |
35 | 36 | ## About the project 37 | 38 | Since I started using social networks, I have been following people of programming world that share excellent resources and tools. I always said 'Wow, what a useful tool! I will use it in my next project'. But, I always forgot the name of the tools I wanted to use, I did not save the information about it. Finally, when I had the need of some tool for an specific task, I started struggling online to find a tool that would fulfill my requirements, and lost a lot of time. I didn't find that needed resource sometimes. 39 | 40 | For that reason I created **Dev Links**. A browser extension for: 41 | 42 | - 🔍 Easily find the tools you need. 43 | - ⭐ Leave the need of remember links and names of tools. 44 | - 🚀 Staying focus and increase your productivity. 45 | 46 | ### Built with 47 | 48 | - [![Plasmo][Plasmo-framework]][Plasmo-url] 49 | - [![React][React.js]][React-url] 50 | - [![Tailwind][Tailwindcss]][Tailwindcss-url] 51 | 52 | ## Download 53 | 54 | Dev Links is available for: 55 | 56 | - 🔗 [Chrome, Opera and Brave](https://chrome.google.com/webstore/detail/eogoekcejgaaaodjgbhnlpmccdmboapb) 57 | - 🔗 [Firefox](https://addons.mozilla.org/addon/dev-links/) 58 | - 🔗 [Edge](https://microsoftedge.microsoft.com/addons/detail/dev-links/aniglhjaambcimldjhiphablecafiifg) 59 | 60 | ## Getting started with development 61 | 62 | ### Prerequisites 63 | 64 | - Node.js 16.14.x or later 65 | - macOS, Windows, or Linux 66 | 67 | ### Installation 68 | 69 | First of all you have to install all dependencies of the project. You can do this as following: 70 | 71 | ``` 72 | yarn install 73 | # OR 74 | npm install 75 | # OR 76 | pnpm install 77 | ``` 78 | 79 | ### Starting dev server 80 | 81 | Execute one of the following commands to start the development server. 82 | This will watch for file changes and regenerate a bundle of the extension in 83 | build/chrome-mv3-dev, and automatically reload the extension in your browser. 84 | 85 | ``` 86 | yarn dev 87 | # OR 88 | npm run dev 89 | # OR 90 | pnpm dev 91 | ``` 92 | 93 | ### Loading the Extension in Chrome 94 | 95 | Open Chrome and write chrome://extensions on the search bar, and press enter. 96 | This will show you the extension page of your Chrome browser. 97 | Then, switch on the *Developer mode*. 98 | 99 | ![Alt text](https://docs.plasmo.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fdeveloper_mode.76f090f7.png&w=750&q=75) 100 | 101 | After that, click on the button "Load Unpacked" and navigate to the project's 102 | build/chrome-mv3-dev directory. 103 | 104 | To see the Dev Links popup, click on the puzzle piece icon on the Chrome toolbar, 105 | and click on Dev Links extension. 106 | You can pin the extension to the Chrome toolbar for easy access by clicking the pin button. 107 | 108 | ![Alt text](https://docs.plasmo.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fpopup_example.30e17ec9.png&w=1200&q=75) 109 | 110 | You can learn more about Plasmo framework [here](http://plasmo.com/). 111 | 112 | ## Contributing 113 | 114 | If you wish to contribute, you can read the guide in [CONTRIBUTING.md](/CONTRIBUTING.md). 115 | 116 | ## License 117 | 118 | This project is under [MIT LICENSE](/LICENSE). 119 | 120 | ## Contact 121 | 122 | You can find my contact info [here](https://r0land013.github.io/). 123 | 124 | ## Acknowledgments 125 | 126 | I wanted to thank to [Javier Alejandro González Casellas](https://github.com/JalexCode) and 127 | [Manuel Ernesto Garcia](https://github.com/manuelernestog) for the help. 128 | 129 | ## Support 130 | 131 | If you found this project useful you can support me [buying me a coffee☕](https://www.buymeacoffee.com/rolandorio). 132 | 133 | Dev Links - All links a developer would need | Product Hunt 134 | 135 | 136 | 137 | [React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB 138 | [React-url]: https://reactjs.org/ 139 | [Tailwindcss]: https://img.shields.io/badge/Tailwind-20232A?style=for-the-badge&logo=tailwindcss&logoColor=61DAFB 140 | [Tailwindcss-url]: https://tailwindcss.com/ 141 | [Plasmo-framework]: https://img.shields.io/badge/Plasmo-20232A?style=for-the-badge&logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgd2lkdGg9IjM0IgogICBoZWlnaHQ9IjQ0IgogICB2aWV3Qm94PSIwIDAgMzQgNDQiCiAgIGNsYXNzPSJ3LTYiCiAgIHZlcnNpb249IjEuMSIKICAgaWQ9InN2ZzgiCiAgIHNvZGlwb2RpOmRvY25hbWU9InBsYXNtby5zdmciCiAgIGlua3NjYXBlOnZlcnNpb249IjEuMS4yICgwYTAwY2Y1MzM5LCAyMDIyLTAyLTA0KSIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMTIiIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJuYW1lZHZpZXcxMCIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlY2hlY2tlcmJvYXJkPSIwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp6b29tPSIxMi44NTY0ODciCiAgICAgaW5rc2NhcGU6Y3g9IjEzLjk2MTgyMyIKICAgICBpbmtzY2FwZTpjeT0iMjMuODQwMTA1IgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDA5IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIwIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIzNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzgiIC8+CiAgPHBhdGgKICAgICBkPSJNMTYuODY2NyAzNS4wODEyQzE5LjI5NjcgMzUuMDgxMiAyMS4zNTEzIDM3LjE2ODQgMjAuMTc4MiAzOS4yOTY1QzIwLjAzODkgMzkuNTQ5MyAxOS44ODY2IDM5Ljc5NjIgMTkuNzIxOCA0MC4wMzYyQzE4LjcxNDYgNDEuNTAzIDE3LjI4MjkgNDIuNjQ2MSAxNS42MDc5IDQzLjMyMTJDMTMuOTMzIDQzLjk5NjIgMTIuMDg5OCA0NC4xNzI4IDEwLjMxMTcgNDMuODI4N0M4LjUzMzUzIDQzLjQ4NDYgNi45MDAxOSA0Mi42MzUxIDUuNjE4MiA0MS4zODc4QzQuMzM2MjIgNDAuMTQwNSAzLjQ2MzE4IDM4LjU1MTMgMy4xMDk0OCAzNi44MjExQzIuNzU1NzkgMzUuMDkxIDIuOTM3MzIgMzMuMjk3NyAzLjYzMTEyIDMxLjY2OEM0LjMyNDkyIDMwLjAzODMgNS40OTk4NCAyOC42NDU0IDcuMDA3MjkgMjcuNjY1M0M3LjI4MjkgMjcuNDg2MiA3LjU2NzM2IDI3LjMyMjMgNy44NTkzIDI3LjE3NEMxMC4wMjYxIDI2LjA3MzkgMTIuMSAyOC4xMzIyIDEyLjEgMzAuNTYyMlYzMC42ODEyQzEyLjEgMzMuMTExMiAxNC4wNyAzNS4wODEyIDE2LjUgMzUuMDgxMkgxNi44NjY3WiIKICAgICBmaWxsPSJjdXJyZW50Q29sb3IiCiAgICAgaWQ9InBhdGgyIgogICAgIHN0eWxlPSJmaWxsOiNmZmZmZmYiIC8+CiAgPHBhdGgKICAgICBkPSJNMjQuNTY2NiAxOC42NzAyQzI0LjU2NjYgMTYuMjQwMSAyNi42NDA1IDE0LjE4MTkgMjguODA3MyAxNS4yODJDMjkuMDk5MiAxNS40MzAyIDI5LjM4MzcgMTUuNTk0MSAyOS42NTkzIDE1Ljc3MzNDMzEuMTY2NyAxNi43NTMzIDMyLjM0MTcgMTguMTQ2MyAzMy4wMzU1IDE5Ljc3NkMzMy43MjkzIDIxLjQwNTcgMzMuOTEwOCAyMy4xOTkgMzMuNTU3MSAyNC45MjkxQzMzLjIwMzQgMjYuNjU5MiAzMi4zMzA0IDI4LjI0ODQgMzEuMDQ4NCAyOS40OTU3QzI5Ljc2NjQgMzAuNzQzMSAyOC4xMzMxIDMxLjU5MjUgMjYuMzU0OSAzMS45MzY3QzI0LjU3NjcgMzIuMjgwOCAyMi43MzM2IDMyLjEwNDIgMjEuMDU4NiAzMS40MjkxQzE5LjM4MzYgMzAuNzU0MSAxNy45NTIgMjkuNjEwOSAxNi45NDQ4IDI4LjE0NDJDMTYuNzc5OSAyNy45MDQyIDE2LjYyNzcgMjcuNjU3MyAxNi40ODgzIDI3LjQwNDVDMTUuMzE1MyAyNS4yNzYzIDE3LjM2OTggMjMuMTg5MSAxOS43OTk5IDIzLjE4OTFMMjAuMTY2NiAyMy4xODkxQzIyLjU5NjYgMjMuMTg5MSAyNC41NjY2IDIxLjIxOTIgMjQuNTY2NiAxOC43ODkxTDI0LjU2NjYgMTguNjcwMloiCiAgICAgZmlsbD0iY3VycmVudENvbG9yIgogICAgIGlkPSJwYXRoNCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmIiAvPgogIDxwYXRoCiAgICAgZD0iTTEyLjIyMjQgMTkuMzgzOEMxMi4yMjI0IDIxLjgxMzggMTAuMTkzNiAyMy44NTYzIDcuOTEyMDggMjMuMDE5OEM3LjA0MzkzIDIyLjcwMTQgNi4yMTE0NCAyMi4yODYzIDUuNDMyMDUgMjEuNzc5NkMzLjQyMjEyIDIwLjQ3MjkgMS44NTU1NyAxOC42MTU3IDAuOTMwNDk0IDE2LjQ0MjdDMC4wMDU0MjM4NiAxNC4yNjk4IC0wLjIzNjYxNyAxMS44Nzg3IDAuMjM0OTc5IDkuNTcxOUMwLjcwNjU3NyA3LjI2NTEgMS44NzA2MyA1LjE0NjE3IDMuNTc5OTQgMy40ODMwNUM1LjI4OTI1IDEuODE5OTQgNy40NjcwNCAwLjY4NzM1IDkuODM3OTIgMC4yMjg0OTlDMTIuMjA4OCAtMC4yMzAzNTMgMTQuNjY2MyAwLjAwNTE0NjgyIDE2Ljg5OTYgMC45MDUyMTZDMTkuMTMyOSAxLjgwNTI5IDIxLjA0MTggMy4zMjk1IDIyLjM4NDggNS4yODUxMUMyMi44ODQ2IDYuMDEzMDMgMjMuMjk3OCA2Ljc4ODU0IDIzLjYxOTUgNy41OTY2NkMyNC41MTgzIDkuODU0MzkgMjIuNDc0NiAxMS44OTE5IDIwLjA0NDYgMTEuODkxOUwxNi42MjI0IDExLjg5MTlDMTQuMTkyMyAxMS44OTE5IDEyLjIyMjQgMTMuODYxOCAxMi4yMjI0IDE2LjI5MTlMMTIuMjIyNCAxOS4zODM4WiIKICAgICBmaWxsPSJjdXJyZW50Q29sb3IiCiAgICAgaWQ9InBhdGg2IgogICAgIHN0eWxlPSJmaWxsOiNmZmZmZmYiIC8+Cjwvc3ZnPgo=&logoColor=61DAFB 142 | [Plasmo-url]: https://plasmo.com/ 143 | -------------------------------------------------------------------------------- /assets/fonts/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R0land013/dev-links/413b5747ba833859955d8e04dc52e9dfd56b15cc/assets/fonts/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R0land013/dev-links/413b5747ba833859955d8e04dc52e9dfd56b15cc/assets/icon.png -------------------------------------------------------------------------------- /assets/icons/categories/cms.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/colors.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/favorite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/fonts.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/forms.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/icons.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/image-compression.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/learning.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/logo-creation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/paas.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/pictures.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/project-management.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/web-hosting.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/categories/web-templates.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/general/brand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 14 | 20 | 21 | 23 | 26 | 30 | 34 | 35 | 40 | 44 | 48 | 52 | 56 | 60 | 64 | 68 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /assets/icons/general/burger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/general/collapse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 38 | 43 | 44 | -------------------------------------------------------------------------------- /assets/icons/general/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 47 | 52 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /assets/icons/general/favorite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/general/link-chain.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/general/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/general/non-favorite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/general/open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/general/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 34 | 39 | 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev-links", 3 | "displayName": "Dev Links", 4 | "version": "0.2.1", 5 | "description": "All links a developer would need.", 6 | "author": "https://github.com/R0land013", 7 | "scripts": { 8 | "dev": "plasmo dev", 9 | "build": "plasmo build", 10 | "package": "plasmo package" 11 | }, 12 | "dependencies": { 13 | "@plasmohq/storage": "^1.7.2", 14 | "minisearch": "^6.1.0", 15 | "plasmo": "0.77.5", 16 | "react": "18.2.0", 17 | "react-dom": "18.2.0" 18 | }, 19 | "devDependencies": { 20 | "@plasmohq/prettier-plugin-sort-imports": "3.6.4", 21 | "@types/chrome": "0.0.239", 22 | "@types/node": "20.3.2", 23 | "@types/react": "18.2.14", 24 | "@types/react-dom": "18.2.6", 25 | "@typescript-eslint/eslint-plugin": "^5.50.0", 26 | "@typescript-eslint/parser": "^5.61.0", 27 | "autoprefixer": "^10.4.14", 28 | "eslint": "^8.0.1", 29 | "eslint-plugin-import": "^2.25.2", 30 | "eslint-plugin-n": "^15.0.0", 31 | "eslint-plugin-promise": "^6.0.0", 32 | "eslint-plugin-react": "^7.32.2", 33 | "eslint-plugin-react-hooks": "^4.6.0", 34 | "eslint-plugin-react-refresh": "^0.4.3", 35 | "postcss": "^8.4.25", 36 | "prettier": "2.8.8", 37 | "tailwindcss": "^3.3.2", 38 | "typescript": "*" 39 | }, 40 | "manifest": { 41 | "permissions": [] 42 | } 43 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('postcss').ProcessOptions} 3 | */ 4 | module.exports = { 5 | plugins: { 6 | tailwindcss: {}, 7 | autoprefixer: {} 8 | } 9 | } -------------------------------------------------------------------------------- /src/components/BurgerButton.tsx: -------------------------------------------------------------------------------- 1 | import BurgerIcon from "data-base64:~assets/icons/general/burger.svg"; 2 | 3 | 4 | interface BurgerButtonProps { 5 | onClick?: () => void; 6 | className?: string; 7 | } 8 | 9 | 10 | const BurgerButton = (props: BurgerButtonProps) => { 11 | return ( 12 | 20 | ); 21 | }; 22 | 23 | export default BurgerButton; -------------------------------------------------------------------------------- /src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | 2 | interface ButtonProps { 3 | children: Array | React.JSX.Element | string; 4 | onClick?: () => void; 5 | className?: string; 6 | } 7 | 8 | const Button = (props: ButtonProps) => { 9 | return ( 10 | 18 | ); 19 | }; 20 | 21 | export default Button; -------------------------------------------------------------------------------- /src/components/Card.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | interface CardProps { 4 | id?: string 5 | className?: string; 6 | children: Array | React.JSX.Element; 7 | } 8 | 9 | const Card = (props: CardProps) => { 10 | return ( 11 |
14 | 15 | {props.children} 16 | 17 |
18 | ); 19 | }; 20 | 21 | export default Card; -------------------------------------------------------------------------------- /src/components/CategoryCard.tsx: -------------------------------------------------------------------------------- 1 | import SelectedLinkCategoryContext from '~context/selectedCategory'; 2 | import Card from './Card'; 3 | import { useContext } from 'react'; 4 | import SelectedLinkContext from '~context/selectedLink'; 5 | import { getIconForLinkCategory } from '~data/category-icons'; 6 | import MenuDivider from './MenuDivider'; 7 | import CategoryCardItem from './CategoryCardItem'; 8 | 9 | 10 | const CategoryCard = () => { 11 | 12 | const { selectedLinkCategory, setSelectedLinkCategory } = useContext(SelectedLinkCategoryContext); 13 | const { setSelectedLink } = useContext(SelectedLinkContext); 14 | 15 | return ( 16 | 17 | 18 |
19 | 20 | 23 | 24 |

25 | {selectedLinkCategory.name} 26 |

27 | 28 |
29 | 30 |
31 | 32 | {selectedLinkCategory.links.map((aLink, index) => ( 33 |
34 | { 37 | setSelectedLinkCategory(undefined); 38 | setSelectedLink(aLink); 39 | }} 40 | onClickOpenLink={() => { 41 | window.open(aLink.linkUrl); 42 | }}/> 43 | 44 | {index != selectedLinkCategory.links.length - 1 && ( 45 | 46 | )} 47 |
48 | ))} 49 | 50 |
51 | 52 |
53 | ); 54 | }; 55 | 56 | export default CategoryCard; -------------------------------------------------------------------------------- /src/components/CategoryCardItem.tsx: -------------------------------------------------------------------------------- 1 | import type { Link } from '~data/links'; 2 | import Button from './Button'; 3 | import LinkChainImage from "data-base64:~assets/icons/general/link-chain.svg"; 4 | import { useState } from 'react'; 5 | 6 | 7 | interface CardCategoryItemProps { 8 | onClick: () => void; 9 | onClickOpenLink: () => void; 10 | link: Link 11 | } 12 | 13 | const CategoryCardItem = (props: CardCategoryItemProps) => { 14 | const [isMouseHovering, setIsMouseHovering] = useState(false); 15 | 16 | return ( 17 |
setIsMouseHovering(true)} 21 | onMouseLeave={() => setIsMouseHovering(false)}> 22 | 23 | 26 | 27 |
28 | 29 |
30 |

31 | {props.link.name} 32 |

33 | 34 | {isMouseHovering && ( 35 | 36 | 50 | )} 51 |
52 | 53 |

54 | {props.link.description} 55 |

56 |
57 | 58 |
59 | ); 60 | }; 61 | 62 | export default CategoryCardItem; -------------------------------------------------------------------------------- /src/components/CategoryMenu.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useContext, useEffect } from 'react'; 2 | import type { LinkCategory } from '~data/links'; 3 | import SelectedLinkContext from '~context/selectedLink'; 4 | import { getIconForLinkCategory } from '~data/category-icons'; 5 | import { hideNavigationMenu } from './NavigationMenu'; 6 | import SelectedLinkCategoryContext from '~context/selectedCategory'; 7 | import ExpandIcon from "data-base64:~assets/icons/general/expand.svg"; 8 | import CollapseIcon from "data-base64:~assets/icons/general/collapse.svg"; 9 | 10 | 11 | interface CategoryMenuProps { 12 | category: LinkCategory 13 | } 14 | 15 | const QUANTITY_OF_ITEMS_TO_SHOW = 5; 16 | 17 | const CategoryMenu = (props: CategoryMenuProps) => { 18 | 19 | const links = props.category.links; 20 | 21 | const selectedLinkContext = useContext(SelectedLinkContext); 22 | const { setSelectedLinkCategory } = useContext(SelectedLinkCategoryContext); 23 | 24 | const [quantityOfShownItems, setQuantityOfShownItems] = useState( 25 | QUANTITY_OF_ITEMS_TO_SHOW > links.length ? links.length : QUANTITY_OF_ITEMS_TO_SHOW 26 | ); 27 | const [isHoveringCategoryName, setIsHoveringCategoryName] = useState(false); 28 | const [isHoveringMenu, setIsHoveringMenu] = useState(false); 29 | 30 | const showMoreItems = () => { 31 | 32 | setQuantityOfShownItems(quantityOfShownItems + QUANTITY_OF_ITEMS_TO_SHOW >= props.category.links.length 33 | ? links.length : quantityOfShownItems + QUANTITY_OF_ITEMS_TO_SHOW 34 | ); 35 | }; 36 | 37 | useEffect(() => { 38 | setQuantityOfShownItems(QUANTITY_OF_ITEMS_TO_SHOW > links.length ? links.length : QUANTITY_OF_ITEMS_TO_SHOW); 39 | }, [links.length]); 40 | 41 | 42 | const expandOrCollapseMenu = () => { 43 | // Collapse 44 | if (quantityOfShownItems === props.category.links.length) { 45 | setQuantityOfShownItems(0); 46 | } 47 | // Expand 48 | else { 49 | setQuantityOfShownItems(props.category.links.length); 50 | } 51 | }; 52 | 53 | return ( 54 |
setIsHoveringMenu(true)} 57 | onMouseLeave={() => setIsHoveringMenu(false)}> 58 | 59 |
60 | 61 | 81 | 82 | {isHoveringMenu && ( 83 | 92 | )} 93 |
94 | 95 | 96 |
97 | 98 | {links.map((aLink, index) => index < quantityOfShownItems && ( 99 | 118 | ))} 119 | 120 |
121 | 122 | 123 | {quantityOfShownItems < links.length && ( 124 | 125 |
126 | 127 | 133 | 134 |
135 | )} 136 | 137 |
138 | ); 139 | }; 140 | 141 | export default CategoryMenu; -------------------------------------------------------------------------------- /src/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface FooterProps { 4 | className?: string 5 | } 6 | 7 | const Footer = (props: FooterProps) => { 8 | return ( 9 |
10 | 11 | Made with 💜 by Rolando Rio Garaboa 12 | 13 | 14 | 15 | Buy me a coffee ☕ 16 | 17 |
18 | ); 19 | }; 20 | 21 | export default Footer; -------------------------------------------------------------------------------- /src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import "./../style.css"; 2 | import BrandIcon from "data-base64:~assets/icons/general/brand.svg"; 3 | import BurgerButton from "./BurgerButton"; 4 | import { showNavigationMenu } from "./NavigationMenu"; 5 | 6 | 7 | const Header = () => { 8 | return ( 9 |
10 | 11 | 14 | 15 |
16 | 17 |
18 | 19 | 20 |
21 | ); 22 | }; 23 | 24 | export default Header; -------------------------------------------------------------------------------- /src/components/LinkCard.tsx: -------------------------------------------------------------------------------- 1 | import Card from "./Card"; 2 | import NonFavoriteIcon from "data-base64:~assets/icons/general/non-favorite.svg"; 3 | import FavoriteIcon from "data-base64:~assets/icons/general/favorite.svg"; 4 | import LinkChainImage from "data-base64:~assets/icons/general/link-chain.svg"; 5 | import Button from "./Button"; 6 | import { type Link } from "~data/links"; 7 | import { useCallback, useContext } from "react"; 8 | import FavoriteLinksContext from "~context/favoriteLinks"; 9 | 10 | 11 | type CardLinkProps = { 12 | link: Link 13 | } 14 | 15 | 16 | 17 | const LinkCard = (props: CardLinkProps) => { 18 | 19 | const {favoriteLinks, addNewFavoriteLink, removeLinkFromFavorites} = useContext(FavoriteLinksContext); 20 | 21 | const isFavoriteLink = useCallback(() => { 22 | const foundLink = favoriteLinks?.find(aLink => aLink.name === props.link.name); 23 | return !!foundLink; 24 | }, [props.link, favoriteLinks]); 25 | 26 | const isFavorite = isFavoriteLink(); 27 | 28 | return ( 29 | 30 | 31 |
32 | 33 |
34 | 35 |

36 | {props.link.name} 37 |

38 |
39 | 40 | 53 | 54 |
55 | 56 | 57 |
58 | 59 | 73 | 74 |
75 | 76 |
77 |

78 | {props.link.description} 79 |

80 |
81 |
82 | ); 83 | }; 84 | 85 | export default LinkCard; -------------------------------------------------------------------------------- /src/components/MainContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from './Header'; 3 | import Footer from './Footer'; 4 | 5 | interface MainContainerProps { 6 | children?: Array | React.JSX.Element; 7 | } 8 | 9 | const MainContainer = (props: MainContainerProps) => { 10 | return ( 11 |
12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | {props.children} 20 |
21 | 22 |
23 | 24 | 25 |
26 |
27 | ); 28 | }; 29 | 30 | export default MainContainer; -------------------------------------------------------------------------------- /src/components/MenuDivider.tsx: -------------------------------------------------------------------------------- 1 | 2 | interface MenuDividerProps { 3 | className?: string; 4 | } 5 | 6 | const MenuDivider = (props: MenuDividerProps) => { 7 | return ( 8 |
9 | ); 10 | }; 11 | 12 | export default MenuDivider; -------------------------------------------------------------------------------- /src/components/NavigationMenu/NavigationMenu.tsx: -------------------------------------------------------------------------------- 1 | import Card from "../Card"; 2 | import CategoryMenu from "../CategoryMenu"; 3 | import MenuDivider from "../MenuDivider"; 4 | import SearchBar from "../SearchBar"; 5 | import linkCategories, { type LinkCategory } from "~data/links"; 6 | import { useContext, useState, useRef, useCallback } from 'react'; 7 | import FavoriteLinksContext from "~context/favoriteLinks"; 8 | import LinkSearcher from "~features/searchLinks"; 9 | 10 | 11 | interface NavigationMenuProps { 12 | id?: string; 13 | className?: string 14 | } 15 | 16 | const NavigationMenu = (props: NavigationMenuProps) => { 17 | 18 | const { favoriteLinks } = useContext(FavoriteLinksContext); 19 | 20 | const [loading, setLoading] = useState(false); 21 | const [linksByCategory, setLinksByCategory] = useState>(linkCategories); 22 | const [visibleFavoriteLinks, setVisibleFavoriteLinks] = useState(true); 23 | 24 | const linkSearcherRef = useRef(new LinkSearcher((searchedLinksByCategory) => { 25 | setLinksByCategory(searchedLinksByCategory); 26 | setLoading(false); 27 | })); 28 | 29 | const searchLinks = useCallback((searchText: string) => { 30 | 31 | if (searchText.trim()) { 32 | setVisibleFavoriteLinks(false); 33 | linkSearcherRef.current.searchLinks(searchText); 34 | setLoading(true); 35 | } 36 | else { 37 | setVisibleFavoriteLinks(true); 38 | setLinksByCategory(linkCategories); 39 | setLoading(false); 40 | } 41 | 42 | 43 | }, [linkSearcherRef]); 44 | 45 | 46 | return ( 47 | 48 | 51 | 52 | 55 | 56 |
57 | 58 | {loading ? ( 59 | 60 |
Loading...
61 | 62 | ) : ( 63 | <> 64 | {visibleFavoriteLinks && favoriteLinks && favoriteLinks.length > 0 && ( 65 |
68 | 69 | 74 | 75 | 76 | 77 |
78 | )} 79 | 80 | {linksByCategory.map((aCategory, index) => ( 81 | 82 |
85 | 86 | 88 | 89 | {index !== linksByCategory.length - 1 ? 90 | 91 | 92 | 93 | : undefined} 94 | 95 |
96 | ))} 97 | 98 | )} 99 | 100 |
101 | 102 |
103 | 104 | ); 105 | }; 106 | 107 | export default NavigationMenu; -------------------------------------------------------------------------------- /src/components/NavigationMenu/index.ts: -------------------------------------------------------------------------------- 1 | import NavigationMenu from "./NavigationMenu"; 2 | import { showNavigationMenu, hideNavigationMenu } from "./utils"; 3 | 4 | export { showNavigationMenu, hideNavigationMenu }; 5 | export default NavigationMenu; -------------------------------------------------------------------------------- /src/components/NavigationMenu/utils.ts: -------------------------------------------------------------------------------- 1 | 2 | export const showNavigationMenu = () => { 3 | const element = document.querySelector('#nav-menu'); 4 | 5 | if(element.classList.contains('hidden-nav-menu')) { 6 | element.classList.toggle('hidden-nav-menu'); 7 | } 8 | 9 | addBackgroundForNavigationMenu(); 10 | element.classList.toggle('shown-nav-menu'); 11 | 12 | }; 13 | 14 | export const hideNavigationMenu = () => { 15 | 16 | const element = document.querySelector('#nav-menu'); 17 | const classList = element.classList; 18 | 19 | if(classList.contains('shown-nav-menu')){ 20 | 21 | element.classList.remove('shown-nav-menu'); 22 | 23 | classList.toggle('hidden-nav-menu'); 24 | removeBackgroundForNavigationMenu(); 25 | } 26 | 27 | 28 | }; 29 | 30 | const addBackgroundForNavigationMenu = () => { 31 | 32 | if(!document.getElementById('nav-menu-container-modal')) { 33 | 34 | const body = document.querySelector('body'); 35 | 36 | const navigationMenuBackground = document.createElement('div'); 37 | navigationMenuBackground.setAttribute('id', 'nav-menu-container-modal'); 38 | navigationMenuBackground.addEventListener('click', () => { 39 | hideNavigationMenu(); 40 | }); 41 | 42 | body.appendChild(navigationMenuBackground); 43 | 44 | } 45 | }; 46 | 47 | const removeBackgroundForNavigationMenu = () => { 48 | if(document.getElementById('nav-menu-container-modal')) { 49 | const element = document.getElementById('nav-menu-container-modal'); 50 | element.remove(); 51 | } 52 | }; -------------------------------------------------------------------------------- /src/components/SearchBar.tsx: -------------------------------------------------------------------------------- 1 | import SearchIcon from "data-base64:~assets/icons/general/search.svg"; 2 | import { useState } from "react"; 3 | 4 | 5 | interface SearchBarProps { 6 | className?: string; 7 | onChangeText: (text: string) => void 8 | } 9 | 10 | const SearchBar = (props: SearchBarProps) => { 11 | 12 | const [isFocused, setIsFocused] = useState(false); 13 | 14 | const focusColor = isFocused ? 'bg-focus-color' : ''; 15 | 16 | return ( 17 |
19 | 20 | setIsFocused(true)} 23 | autoFocus={true} 24 | onBlur={() => setIsFocused(false)} 25 | onChange={(event) => props.onChangeText(event.target.value)} 26 | type="text"/> 27 | 28 | 31 | 32 |
33 | ); 34 | }; 35 | 36 | export default SearchBar; -------------------------------------------------------------------------------- /src/components/WelcomeInfo.tsx: -------------------------------------------------------------------------------- 1 | 2 | interface WelcomeInfoProps { 3 | className?: string; 4 | } 5 | 6 | const WelcomeInfo = (props: WelcomeInfoProps) => { 7 | return ( 8 |
9 | 10 |

11 | Welcome to Dev Links! 12 |

13 | 14 | 15 | All links a developer would need 16 | 17 | 18 |
19 | 20 |
    21 |
  • 22 | 🔗 Browse useful links for developers and designers 23 |
  • 24 | 25 |
  • 26 | ⭐ Select your favorite links for a rapid access 27 |
  • 28 | 29 |
  • 30 | 🚀 Stay focus 31 |
  • 32 |
33 | 34 |
35 | 36 | You can add more links and make suggestions here 37 | 38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default WelcomeInfo; -------------------------------------------------------------------------------- /src/context/favoriteLinks.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | import type { Link } from "~data/links"; 3 | 4 | type FavoriteLinksContextType = { 5 | favoriteLinks?: Array 6 | addNewFavoriteLink: (aLink: Link) => void; 7 | removeLinkFromFavorites: (aLink: Link) => void 8 | } 9 | 10 | const FavoriteLinksContext = createContext(undefined); 11 | 12 | export default FavoriteLinksContext; -------------------------------------------------------------------------------- /src/context/selectedCategory.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | import type { LinkCategory } from "~data/links"; 3 | 4 | type SelectedLinkCategoryContextType = { 5 | selectedLinkCategory?: LinkCategory; 6 | setSelectedLinkCategory?: (selectedLink?: LinkCategory) => void 7 | } 8 | 9 | const SelectedLinkCategoryContext = createContext(undefined); 10 | 11 | export default SelectedLinkCategoryContext; -------------------------------------------------------------------------------- /src/context/selectedLink.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | import type { Link } from "~data/links"; 3 | 4 | type SelectedLinkContextType = { 5 | selectedLink?: Link; 6 | setSelectedLink?: (selectedLink: Link) => void 7 | } 8 | 9 | const SelectedLinkContext = createContext(undefined); 10 | 11 | export default SelectedLinkContext; -------------------------------------------------------------------------------- /src/data/category-icons.ts: -------------------------------------------------------------------------------- 1 | import type { LinkCategoryName } from "./links"; 2 | import CMSCategoryIcon from "data-base64:~assets/icons/categories/cms.svg"; 3 | import ColorsCategoryIcon from "data-base64:~assets/icons/categories/colors.svg"; 4 | import FormsCategoryIcon from "data-base64:~assets/icons/categories/forms.svg"; 5 | import FontsCategoryIcon from "data-base64:~assets/icons/categories/fonts.svg"; 6 | import IconsCategoryIcon from "data-base64:~assets/icons/categories/icons.svg"; 7 | import ImageCompressionCategoryIcon from "data-base64:~assets/icons/categories/image-compression.svg"; 8 | import LearningCategoryIcon from "data-base64:~assets/icons/categories/learning.svg"; 9 | import LogoCreationCategoryIcon from "data-base64:~assets/icons/categories/logo-creation.svg"; 10 | import LogoPaaSCategoryIcon from "data-base64:~assets/icons/categories/paas.svg"; 11 | import PicturesCategoryIcon from "data-base64:~assets/icons/categories/pictures.svg"; 12 | import ProjectManagementCategoryIcon from "data-base64:~assets/icons/categories/project-management.svg"; 13 | import WebHostingCategoryIcon from "data-base64:~assets/icons/categories/web-hosting.svg"; 14 | import WebTemplatesCategoryIcon from "data-base64:~assets/icons/categories/web-templates.svg"; 15 | import FavoriteIcon from "data-base64:~assets/icons/categories/favorite.svg"; 16 | 17 | 18 | export const getIconForLinkCategory = (category: LinkCategoryName) => { 19 | 20 | switch (category) { 21 | 22 | case 'CMS': 23 | return CMSCategoryIcon; 24 | 25 | case 'Colors': 26 | return ColorsCategoryIcon; 27 | 28 | case 'Forms': 29 | return FormsCategoryIcon; 30 | 31 | case 'Fonts': 32 | return FontsCategoryIcon; 33 | 34 | case 'Icons': 35 | return IconsCategoryIcon; 36 | 37 | case 'Image compression': 38 | return ImageCompressionCategoryIcon; 39 | 40 | case 'Learning': 41 | return LearningCategoryIcon; 42 | 43 | case 'Logo creation': 44 | return LogoCreationCategoryIcon; 45 | 46 | case 'PaaS': 47 | return LogoPaaSCategoryIcon; 48 | 49 | case 'Pictures': 50 | return PicturesCategoryIcon; 51 | 52 | case 'Project management': 53 | return ProjectManagementCategoryIcon; 54 | 55 | case 'Web hosting': 56 | return WebHostingCategoryIcon; 57 | 58 | case 'Web templates': 59 | return WebTemplatesCategoryIcon; 60 | 61 | default: 62 | return FavoriteIcon; 63 | } 64 | 65 | }; -------------------------------------------------------------------------------- /src/data/links/cms/contentful.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const ContentfulDescription = 5 | ` 6 | More than a headless CMS, Contentful is the first API 7 | composable content platform for creating, managing and 8 | publishing content to any digital channel. 9 | `; 10 | 11 | 12 | const Contentful: CMSLink = { 13 | name: 'Contentful', 14 | category: 'CMS', 15 | linkUrl: 'https://www.contentful.com/', 16 | description: ContentfulDescription, 17 | }; 18 | 19 | export default Contentful; -------------------------------------------------------------------------------- /src/data/links/cms/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import Contentful from './contentful'; 3 | import Keystone from './keystone'; 4 | import Saleor from './saleor'; 5 | import Sanity from './sanity'; 6 | import Shopify from './shopify'; 7 | import Strapi from './strapi'; 8 | import Wagtail from './wagtail'; 9 | import Wordpress from './wordpress'; 10 | 11 | 12 | type CMSCategoryName = Extract; 13 | 14 | 15 | export type CMSLink = Omit & { 16 | category: CMSCategoryName 17 | }; 18 | 19 | 20 | const CMSCategoryLinks: Array = [ 21 | Contentful, 22 | Keystone, 23 | Saleor, 24 | Sanity, 25 | Shopify, 26 | Strapi, 27 | Wagtail, 28 | Wordpress, 29 | ]; 30 | 31 | 32 | const CMSCategory: LinkCategory = { 33 | name: 'CMS', 34 | links: CMSCategoryLinks 35 | }; 36 | 37 | 38 | export default CMSCategory; -------------------------------------------------------------------------------- /src/data/links/cms/keystone.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const KeystoneDescription = 5 | ` 6 | The Headless-CMS of node.js with super powers for developers. 7 | `; 8 | 9 | 10 | const Keystone: CMSLink = { 11 | name: 'Keystone', 12 | category: 'CMS', 13 | linkUrl: 'https://keystonejs.com/', 14 | description: KeystoneDescription, 15 | }; 16 | 17 | export default Keystone; -------------------------------------------------------------------------------- /src/data/links/cms/saleor.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const SaleorDescription = 5 | ` 6 | A headless eCommerce platform powered by GraphQL that delivers 7 | ultra-fast, dynamic, and personalized shopping experiences. 8 | `; 9 | 10 | 11 | const Saleor: CMSLink = { 12 | name: 'Saleor', 13 | category: 'CMS', 14 | linkUrl: 'https://saleor.io/', 15 | description: SaleorDescription, 16 | }; 17 | 18 | export default Saleor; -------------------------------------------------------------------------------- /src/data/links/cms/sanity.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const SanityDescription = 5 | ` 6 | Create remarkable experiences at scale. Sanity is a customizable 7 | solution that treats content as data to power your digital strategy. 8 | `; 9 | 10 | 11 | const Sanity: CMSLink = { 12 | name: 'Sanity', 13 | category: 'CMS', 14 | linkUrl: 'https://www.sanity.io/', 15 | description: SanityDescription, 16 | }; 17 | 18 | export default Sanity; -------------------------------------------------------------------------------- /src/data/links/cms/shopify.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const ShopifyDescription = 5 | ` 6 | Shopify is an easy-to-use online store builder trusted 7 | by millions of businesses around the world. Fully customizable 8 | store layout with a secure shopping cart. 9 | `; 10 | 11 | 12 | const Shopify: CMSLink = { 13 | name: 'Shopify', 14 | category: 'CMS', 15 | linkUrl: 'https://www.shopify.com/', 16 | description: ShopifyDescription, 17 | }; 18 | 19 | export default Shopify; -------------------------------------------------------------------------------- /src/data/links/cms/strapi.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const StrapiDescription = 5 | ` 6 | Strapi is the next generation, open source, javascript 7 | free front end CMS that enables you to create, manage 8 | and deliver rich content experiences on any digital device. 9 | `; 10 | 11 | 12 | const Strapi: CMSLink = { 13 | name: 'Strapi', 14 | category: 'CMS', 15 | linkUrl: 'https://strapi.io/', 16 | description: StrapiDescription, 17 | }; 18 | 19 | export default Strapi; -------------------------------------------------------------------------------- /src/data/links/cms/wagtail.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const WagtailDescription = 5 | ` 6 | It is a free and open source content management system 7 | written in Python. It is popular with websites that use 8 | the Django framework. 9 | `; 10 | 11 | 12 | const Wagtail: CMSLink = { 13 | name: 'Wagtail', 14 | category: 'CMS', 15 | linkUrl: 'https://wagtail.org/', 16 | description: WagtailDescription, 17 | }; 18 | 19 | export default Wagtail; -------------------------------------------------------------------------------- /src/data/links/cms/wordpress.ts: -------------------------------------------------------------------------------- 1 | import type { CMSLink } from "./"; 2 | 3 | 4 | const WordpressDescription = 5 | ` 6 | Open source software that you can use to easily create 7 | a beautiful website, blog, or app. 8 | `; 9 | 10 | 11 | const Wordpress: CMSLink = { 12 | name: 'Wordpress', 13 | category: 'CMS', 14 | linkUrl: 'https://wordpress.org/', 15 | description: WordpressDescription, 16 | }; 17 | 18 | export default Wordpress; -------------------------------------------------------------------------------- /src/data/links/colors/color-hunt.ts: -------------------------------------------------------------------------------- 1 | import type { ColorsLink } from "./"; 2 | 3 | 4 | const ColorHuntDescription = 5 | ` 6 | Color Hunt is an open collection of beautiful color palettes. 7 | `; 8 | 9 | const ColorHunt: ColorsLink = { 10 | name: 'Color Hunt', 11 | category: 'Colors', 12 | linkUrl: 'https://colorhunt.co/', 13 | description: ColorHuntDescription, 14 | }; 15 | 16 | export default ColorHunt; -------------------------------------------------------------------------------- /src/data/links/colors/colors-and-fonts.ts: -------------------------------------------------------------------------------- 1 | import type { ColorsLink } from "./"; 2 | 3 | 4 | const ColorsAndFontsDescription = 5 | ` 6 | Color and typography inspiration for Web Developers and Digital Designers. 7 | `; 8 | 9 | const ColorsAndFonts: ColorsLink = { 10 | name: 'Colors & fonts', 11 | category: 'Colors', 12 | linkUrl: 'https://www.colorsandfonts.com/', 13 | description: ColorsAndFontsDescription, 14 | }; 15 | 16 | export default ColorsAndFonts; -------------------------------------------------------------------------------- /src/data/links/colors/coolors.ts: -------------------------------------------------------------------------------- 1 | import type { ColorsLink } from "./"; 2 | 3 | 4 | const CoolorsDescription = 5 | ` 6 | The super fast color palette generator! 7 | `; 8 | 9 | const Coolors: ColorsLink = { 10 | name: 'Coolors', 11 | category: 'Colors', 12 | linkUrl: 'https://coolors.co/', 13 | description: CoolorsDescription, 14 | }; 15 | 16 | export default Coolors; -------------------------------------------------------------------------------- /src/data/links/colors/fffuel.ts: -------------------------------------------------------------------------------- 1 | import type { ColorsLink } from "./"; 2 | 3 | 4 | const fffuelDescription = 5 | ` 6 | fffuel is a collection of color tools and free SVG generators 7 | for gradients, patterns, textures, shapes & backgrounds 8 | `; 9 | 10 | const fffuel: ColorsLink = { 11 | name: 'fffuel', 12 | category: 'Colors', 13 | linkUrl: 'https://fffuel.co/', 14 | description: fffuelDescription, 15 | }; 16 | 17 | export default fffuel; -------------------------------------------------------------------------------- /src/data/links/colors/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import ColorHunt from './color-hunt'; 3 | import ColorsAndFonts from './colors-and-fonts'; 4 | import Coolors from './coolors'; 5 | import fffuel from './fffuel'; 6 | 7 | 8 | type ColorsCategoryName = Extract; 9 | 10 | 11 | export type ColorsLink = Omit & { 12 | category: ColorsCategoryName 13 | }; 14 | 15 | 16 | const colorsCategoryLinks: Array = [ 17 | ColorHunt, 18 | ColorsAndFonts, 19 | Coolors, 20 | fffuel, 21 | ]; 22 | 23 | 24 | const colorsCategory: LinkCategory = { 25 | name: 'Colors', 26 | links: colorsCategoryLinks 27 | }; 28 | 29 | 30 | export default colorsCategory; -------------------------------------------------------------------------------- /src/data/links/fonts/1001-free-fonts.ts: -------------------------------------------------------------------------------- 1 | import type { FontsLink } from "./"; 2 | 3 | 4 | const _1001FreeFontsDescription = 5 | ` 6 | Download 75,000 fonts for Windows and Mac. Browse categories like calligraphy, 7 | handwriting, script, serif, and more. Free fonts added daily. 8 | `; 9 | 10 | 11 | const _1001FreeFonts: FontsLink = { 12 | name: '1001 Free Fonts', 13 | category: 'Fonts', 14 | linkUrl: 'https://www.1001freefonts.com/', 15 | description: _1001FreeFontsDescription, 16 | }; 17 | 18 | 19 | export default _1001FreeFonts; -------------------------------------------------------------------------------- /src/data/links/fonts/befonts.ts: -------------------------------------------------------------------------------- 1 | import type { FontsLink } from "."; 2 | 3 | 4 | const BEFontsDescription = 5 | ` 6 | Free font for designers! High-quality design resources for free! And 7 | it helps introduce your products to customers for the first time with 8 | free font downloads and lets them try before they buy. 9 | `; 10 | 11 | 12 | const BEFonts: FontsLink = { 13 | name: 'BEfonts', 14 | category: 'Fonts', 15 | linkUrl: 'https://befonts.com/', 16 | description: BEFontsDescription, 17 | }; 18 | 19 | 20 | export default BEFonts; -------------------------------------------------------------------------------- /src/data/links/fonts/dafont.ts: -------------------------------------------------------------------------------- 1 | import type { FontsLink } from "./"; 2 | 3 | 4 | const DafontDescription = 5 | ` 6 | Free download font file. Search by alphabetical order, by style, by author or by popularity. 7 | `; 8 | 9 | 10 | const Dafont: FontsLink = { 11 | name: 'Dafont', 12 | category: 'Fonts', 13 | linkUrl: 'https://www.dafont.com/', 14 | description: DafontDescription, 15 | }; 16 | 17 | 18 | export default Dafont; -------------------------------------------------------------------------------- /src/data/links/fonts/font-space.ts: -------------------------------------------------------------------------------- 1 | import type { FontsLink } from "./"; 2 | 3 | 4 | const FontSpaceDescription = 5 | ` 6 | Downloads of 100,000+ legally licensed free fonts that are perfect for your design projects. 7 | `; 8 | 9 | 10 | const FontSpace: FontsLink = { 11 | name: 'Font Space', 12 | category: 'Fonts', 13 | linkUrl: 'https://www.fontspace.com/', 14 | description: FontSpaceDescription, 15 | }; 16 | 17 | 18 | export default FontSpace; -------------------------------------------------------------------------------- /src/data/links/fonts/google-fonts.ts: -------------------------------------------------------------------------------- 1 | import type { FontsLink } from "./"; 2 | 3 | 4 | const GoogleFontsDescription = 5 | ` 6 | Make your website more beautiful, fast and open through great typography. 7 | `; 8 | 9 | 10 | const GoogleFonts: FontsLink = { 11 | name: 'Google Fonts', 12 | category: 'Fonts', 13 | linkUrl: 'https://fonts.google.com/', 14 | description: GoogleFontsDescription, 15 | }; 16 | 17 | 18 | export default GoogleFonts; -------------------------------------------------------------------------------- /src/data/links/fonts/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from ".."; 2 | import _1001FreeFonts from "./1001-free-fonts"; 3 | import BEFonts from "./befonts"; 4 | import Dafont from "./dafont"; 5 | import FontSpace from "./font-space"; 6 | import GoogleFonts from "./google-fonts"; 7 | import MyFonts from "./myfonts"; 8 | import WhatFontIs from "./what-font-is"; 9 | 10 | 11 | type FontsCategoryName = Extract; 12 | 13 | 14 | export type FontsLink = Omit & { 15 | category: FontsCategoryName 16 | }; 17 | 18 | 19 | const fontsCategoryLinks: Array = [ 20 | _1001FreeFonts, 21 | BEFonts, 22 | Dafont, 23 | FontSpace, 24 | GoogleFonts, 25 | MyFonts, 26 | WhatFontIs, 27 | ]; 28 | 29 | 30 | const fontsCategory: LinkCategory = { 31 | name: 'Fonts', 32 | links: fontsCategoryLinks 33 | }; 34 | 35 | 36 | export default fontsCategory; -------------------------------------------------------------------------------- /src/data/links/fonts/myfonts.ts: -------------------------------------------------------------------------------- 1 | import type { FontsLink } from "./"; 2 | 3 | 4 | const MyFontsDescription = 5 | ` 6 | MyFonts is the #1 place for web fonts, classic desktop fonts, cool new fonts. It is paid. 7 | `; 8 | 9 | 10 | const MyFonts: FontsLink = { 11 | name: 'MyFonts', 12 | category: 'Fonts', 13 | linkUrl: 'https://www.myfonts.com/', 14 | description: MyFontsDescription, 15 | }; 16 | 17 | 18 | export default MyFonts; -------------------------------------------------------------------------------- /src/data/links/fonts/what-font-is.ts: -------------------------------------------------------------------------------- 1 | import type { FontsLink } from "./"; 2 | 3 | 4 | const WhatFontIsDescription = 5 | ` 6 | Source finder that helps you identify the sources of any image. 7 | Upload the image and choose which font you need. 840,000 8 | free or commercial indexed sources. 9 | `; 10 | 11 | 12 | const WhatFontIs: FontsLink = { 13 | name: 'What Font Is', 14 | category: 'Fonts', 15 | linkUrl: 'https://www.whatfontis.com/', 16 | description: WhatFontIsDescription, 17 | }; 18 | 19 | 20 | export default WhatFontIs; -------------------------------------------------------------------------------- /src/data/links/forms/getform.ts: -------------------------------------------------------------------------------- 1 | import type { FormsLink } from "./"; 2 | 3 | 4 | const GetformDescription = 5 | ` 6 | Smart form endpoints for developers. Collect submissions, 7 | receive emails and automate your form. 8 | `; 9 | 10 | 11 | const Getform: FormsLink = { 12 | name: 'Getform', 13 | category: 'Forms', 14 | linkUrl: 'https://getform.io/', 15 | description: GetformDescription, 16 | }; 17 | 18 | export default Getform; -------------------------------------------------------------------------------- /src/data/links/forms/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import Getform from './getform'; 3 | import Tally from './tally'; 4 | 5 | type FormsCategoryName = Extract; 6 | 7 | 8 | export type FormsLink = Omit & { 9 | category: FormsCategoryName 10 | }; 11 | 12 | 13 | const FormsCategoryLinks: Array = [ 14 | Getform, 15 | Tally, 16 | ]; 17 | 18 | 19 | const formsCategory: LinkCategory = { 20 | name: 'Forms', 21 | links: FormsCategoryLinks 22 | }; 23 | 24 | 25 | export default formsCategory; -------------------------------------------------------------------------------- /src/data/links/forms/tally.ts: -------------------------------------------------------------------------------- 1 | import type { FormsLink } from "./"; 2 | 3 | 4 | const TallyDescription = 5 | ` 6 | The easiest way to create forms. Create forms for all 7 | purposes in seconds. Without knowing how to program and for free! 8 | `; 9 | 10 | 11 | const Tally: FormsLink = { 12 | name: 'Tally', 13 | category: 'Forms', 14 | linkUrl: 'https://tally.so/', 15 | description: TallyDescription, 16 | }; 17 | 18 | export default Tally; -------------------------------------------------------------------------------- /src/data/links/icons/boxicons.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const BoxiconsDescription = 5 | ` 6 | High quality web icons. Carefully designed open source simple 7 | icons for designers and developers. 8 | `; 9 | 10 | 11 | const Boxicons: IconsLink = { 12 | name: 'Boxicons', 13 | category: 'Icons', 14 | linkUrl: 'https://boxicons.com/', 15 | description: BoxiconsDescription, 16 | }; 17 | 18 | export default Boxicons; -------------------------------------------------------------------------------- /src/data/links/icons/flaticon.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const flaticonDescription = 5 | ` 6 | Access 11.4M+ vector icons & stickers. Download Free Icons 7 | and Stickers for your projects. Images made by and for 8 | designers in PNG, SVG, EPS, PSD and CSS formats. 9 | `; 10 | 11 | 12 | const flaticon: IconsLink = { 13 | name: 'flaticon', 14 | category: 'Icons', 15 | linkUrl: 'https://www.flaticon.com/', 16 | description: flaticonDescription, 17 | }; 18 | 19 | export default flaticon; -------------------------------------------------------------------------------- /src/data/links/icons/font-awesome.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const FontAwesomeDescription = 5 | ` 6 | Font Awesome is a CSS and Less based font and icon toolkit. 7 | It's the Internet's icon library and toolkit, used by millions 8 | of designers, developers, and content creators. 9 | `; 10 | 11 | 12 | const FontAwesome: IconsLink = { 13 | name: 'Font Awesome', 14 | category: 'Icons', 15 | linkUrl: 'https://fontawesome.com/', 16 | description: FontAwesomeDescription, 17 | }; 18 | 19 | export default FontAwesome; -------------------------------------------------------------------------------- /src/data/links/icons/freeicons.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const FreeIconsDescription = 5 | ` 6 | The largest collections of premium and free icons. 7 | Download SVG, PNG, AI, EPS and PSD Icons for Free. 8 | `; 9 | 10 | 11 | const FreeIcons: IconsLink = { 12 | name: 'Free Icons', 13 | category: 'Icons', 14 | linkUrl: 'https://freeicons.io/', 15 | description: FreeIconsDescription, 16 | }; 17 | 18 | export default FreeIcons; -------------------------------------------------------------------------------- /src/data/links/icons/iconfinder.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const IconfinderDescription = 5 | ` 6 | Make beautiful designs faster.Millions of graphics 7 | for your design projects. Created by independent designers. 8 | `; 9 | 10 | 11 | const Iconfinder: IconsLink = { 12 | name: 'Iconfinder', 13 | category: 'Icons', 14 | linkUrl: 'https://www.iconfinder.com/', 15 | description: IconfinderDescription, 16 | }; 17 | 18 | export default Iconfinder; -------------------------------------------------------------------------------- /src/data/links/icons/iconify.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const IconifyDescription = 5 | ` 6 | All popular icon sets, one framework. More than 100,000 open source vector icons. 7 | `; 8 | 9 | 10 | const Iconify: IconsLink = { 11 | name: 'Iconify', 12 | category: 'Icons', 13 | linkUrl: 'https://iconify.design/', 14 | description: IconifyDescription, 15 | }; 16 | 17 | export default Iconify; -------------------------------------------------------------------------------- /src/data/links/icons/icons8.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const icons8Description = 5 | ` 6 | 1,265,200 free icons. Large icon packs of over 10k icons, 7 | so you can find visually consistent icons for all your designs. 8 | `; 9 | 10 | 11 | const icons8: IconsLink = { 12 | name: 'icons8', 13 | category: 'Icons', 14 | linkUrl: 'https://icons8.com/icons/', 15 | description: icons8Description, 16 | }; 17 | 18 | export default icons8; -------------------------------------------------------------------------------- /src/data/links/icons/iconscout.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const IconscoutDescription = 5 | ` 6 | Get 2,398,486+ free icons for personal or commercial use. 7 | `; 8 | 9 | 10 | const Iconscout: IconsLink = { 11 | name: 'Iconscout', 12 | category: 'Icons', 13 | linkUrl: 'https://iconscout.com/free-icons', 14 | description: IconscoutDescription, 15 | }; 16 | 17 | export default Iconscout; -------------------------------------------------------------------------------- /src/data/links/icons/iconstore.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const iconStoreDescription = 5 | ` 6 | Free Icons by First-Class Designers. 7 | `; 8 | 9 | 10 | const IconStore: IconsLink = { 11 | name: 'IconStore', 12 | category: 'Icons', 13 | linkUrl: 'https://iconstore.co/', 14 | description: iconStoreDescription, 15 | }; 16 | 17 | export default IconStore; -------------------------------------------------------------------------------- /src/data/links/icons/index.ts: -------------------------------------------------------------------------------- 1 | import Lordicon from "./lordicon"; 2 | import SvgRepo from "./svg-repo"; 3 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 4 | import FontAwesome from "./font-awesome"; 5 | import Boxicons from "./boxicons"; 6 | import Iconify from "./iconify"; 7 | import icons8 from "./icons8"; 8 | import Iconscout from "./iconscout"; 9 | import SimpleIcons from "./simple-icons"; 10 | import TablerIcons from "./tabler-icons"; 11 | import IconStore from "./iconstore"; 12 | import FreeIcons from "./freeicons"; 13 | import Iconfinder from "./iconfinder"; 14 | import flaticon from "./flaticon"; 15 | import uxwing from "./uxwing"; 16 | import reshot from "./reshot"; 17 | 18 | 19 | type IconsCategoryName = Extract; 20 | 21 | 22 | export type IconsLink = Omit & { 23 | category: IconsCategoryName 24 | }; 25 | 26 | 27 | const iconsCategoryLinks: Array = [ 28 | Boxicons, 29 | flaticon, 30 | FontAwesome, 31 | FreeIcons, 32 | Iconfinder, 33 | Iconify, 34 | icons8, 35 | Iconscout, 36 | IconStore, 37 | Lordicon, 38 | reshot, 39 | SimpleIcons, 40 | SvgRepo, 41 | TablerIcons, 42 | uxwing, 43 | ]; 44 | 45 | 46 | const iconsCategory: LinkCategory = { 47 | name: 'Icons', 48 | links: iconsCategoryLinks 49 | }; 50 | 51 | 52 | export default iconsCategory; -------------------------------------------------------------------------------- /src/data/links/icons/lordicon.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const LordiconDescription = 5 | ` 6 | Impressive collection of 8000+ animated icons (1400+ are free to use). 7 | `; 8 | 9 | 10 | const Lordicon: IconsLink = { 11 | name: 'Lordicon', 12 | category: 'Icons', 13 | linkUrl: 'https://lordicon.com/', 14 | description: LordiconDescription, 15 | }; 16 | 17 | export default Lordicon; -------------------------------------------------------------------------------- /src/data/links/icons/reshot.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const reshotDescription = 5 | ` 6 | Free Icons & Illustrations. Design freely with instant 7 | downloads and commercial licenses. 8 | `; 9 | 10 | 11 | const reshot: IconsLink = { 12 | name: 'reshot', 13 | category: 'Icons', 14 | linkUrl: 'https://www.reshot.com/', 15 | description: reshotDescription, 16 | }; 17 | 18 | export default reshot; -------------------------------------------------------------------------------- /src/data/links/icons/simple-icons.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const SimpleIconsDescription = 5 | ` 6 | 2434 Free SVG Icons for popular brands. 7 | `; 8 | 9 | 10 | const SimpleIcons: IconsLink = { 11 | name: 'Simple Icons', 12 | category: 'Icons', 13 | linkUrl: 'https://simpleicons.org/', 14 | description: SimpleIconsDescription, 15 | }; 16 | 17 | export default SimpleIcons; -------------------------------------------------------------------------------- /src/data/links/icons/svg-repo.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const SvgRepoDescription = 5 | ` 6 | Over 500,000 Open Source SVG Vectors and Icons. 7 | `; 8 | 9 | const SvgRepo: IconsLink = { 10 | name: 'SVG Repo', 11 | category: 'Icons', 12 | linkUrl: 'https://www.svgrepo.com', 13 | description: SvgRepoDescription, 14 | }; 15 | 16 | export default SvgRepo; -------------------------------------------------------------------------------- /src/data/links/icons/tabler-icons.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const TablerIconsDescription = 5 | ` 6 | 3,200+ pixel perfect icons for web design. Free and open source 7 | icons designed to make your website or app attractive. 8 | `; 9 | 10 | 11 | const TablerIcons: IconsLink = { 12 | name: 'Tabler Icons', 13 | category: 'Icons', 14 | linkUrl: 'https://tabler-icons.io/', 15 | description: TablerIconsDescription, 16 | }; 17 | 18 | export default TablerIcons; -------------------------------------------------------------------------------- /src/data/links/icons/uxwing.ts: -------------------------------------------------------------------------------- 1 | import type { IconsLink } from "./"; 2 | 3 | 4 | const uxwingDescription = 5 | ` 6 | Exclusive collection of free icons download for commercial 7 | projects without attribution. Well Optimized free SVG and 8 | PNG icons that can use on interface design & Web and Application development. 9 | `; 10 | 11 | 12 | const uxwing: IconsLink = { 13 | name: 'uxwing', 14 | category: 'Icons', 15 | linkUrl: 'https://uxwing.com/', 16 | description: uxwingDescription, 17 | }; 18 | 19 | export default uxwing; -------------------------------------------------------------------------------- /src/data/links/image-compression/compressimage-io.ts: -------------------------------------------------------------------------------- 1 | import type { ImageCompressionLink } from "./"; 2 | 3 | 4 | const compressImageIoDescription = 5 | ` 6 | Compress JPG and PNG images. No Limits. Bulk Conversion. 7 | Convert to WebP. Works Offline. 8 | `; 9 | 10 | 11 | const compressImageIo: ImageCompressionLink = { 12 | name: 'compressImage.io', 13 | category: 'Image compression', 14 | linkUrl: 'https://compressimage.io/', 15 | description: compressImageIoDescription, 16 | }; 17 | 18 | export default compressImageIo; -------------------------------------------------------------------------------- /src/data/links/image-compression/compressor.ts: -------------------------------------------------------------------------------- 1 | import type { ImageCompressionLink } from "./"; 2 | 3 | 4 | const CompressorDescription = 5 | ` 6 | Fast & efficient image compression. 7 | Optimize JPEG, PNG, SVG, GIF and WEBP. 8 | `; 9 | 10 | 11 | const Compressor: ImageCompressionLink = { 12 | name: 'Compressor', 13 | category: 'Image compression', 14 | linkUrl: 'https://compressor.io/', 15 | description: CompressorDescription, 16 | }; 17 | 18 | export default Compressor; -------------------------------------------------------------------------------- /src/data/links/image-compression/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import compressImageIo from './compressimage-io'; 3 | import Compressor from './compressor'; 4 | import SVGminify from './svgminify'; 5 | import SVGOMG from './svgomg'; 6 | import TinyPNG from './tinypng'; 7 | 8 | 9 | type ImageCompressionCategoryName = Extract; 10 | 11 | 12 | export type ImageCompressionLink = Omit & { 13 | category: ImageCompressionCategoryName 14 | }; 15 | 16 | 17 | const ImageCompressionCategoryLinks: Array = [ 18 | compressImageIo, 19 | Compressor, 20 | SVGminify, 21 | SVGOMG, 22 | TinyPNG, 23 | ]; 24 | 25 | 26 | const imageCompressionCategory: LinkCategory = { 27 | name: 'Image compression', 28 | links: ImageCompressionCategoryLinks 29 | }; 30 | 31 | 32 | export default imageCompressionCategory; -------------------------------------------------------------------------------- /src/data/links/image-compression/svgminify.ts: -------------------------------------------------------------------------------- 1 | import type { ImageCompressionLink } from "./"; 2 | 3 | 4 | const SVGminifyDescription = 5 | ` 6 | This tool removes superfluous information, thereby reducing 7 | the size of your SVG files. 8 | `; 9 | 10 | 11 | const SVGminify: ImageCompressionLink = { 12 | name: 'SVGminify', 13 | category: 'Image compression', 14 | linkUrl: 'https://www.svgminify.com/', 15 | description: SVGminifyDescription, 16 | }; 17 | 18 | export default SVGminify; -------------------------------------------------------------------------------- /src/data/links/image-compression/svgomg.ts: -------------------------------------------------------------------------------- 1 | import type { ImageCompressionLink } from "./"; 2 | 3 | 4 | const SVGOMGDescription = 5 | ` 6 | Online SVG optimizer with a lot of settings and possibility 7 | to cut and paste svg code directly in it. 8 | `; 9 | 10 | 11 | const SVGOMG: ImageCompressionLink = { 12 | name: 'SVGOMG', 13 | category: 'Image compression', 14 | linkUrl: 'https://jakearchibald.github.io/svgomg/', 15 | description: SVGOMGDescription, 16 | }; 17 | 18 | export default SVGOMG; -------------------------------------------------------------------------------- /src/data/links/image-compression/tinypng.ts: -------------------------------------------------------------------------------- 1 | import type { ImageCompressionLink } from "./"; 2 | 3 | 4 | const TinyPNGDescription = 5 | ` 6 | Smart PNG and JPEG compression. 7 | `; 8 | 9 | 10 | const TinyPNG: ImageCompressionLink = { 11 | name: 'TinyPNG', 12 | category: 'Image compression', 13 | linkUrl: 'https://tinypng.com/', 14 | description: TinyPNGDescription, 15 | }; 16 | 17 | export default TinyPNG; -------------------------------------------------------------------------------- /src/data/links/index.ts: -------------------------------------------------------------------------------- 1 | import CMSCategory from "./cms"; 2 | import colorsCategory from "./colors"; 3 | import formsCategory from "./forms"; 4 | import fontsCategory from "./fonts"; 5 | import iconsCategory from "./icons"; 6 | import logoCreationCategory from "./logo-creation"; 7 | import webHostingCategory from "./web-hosting"; 8 | import webTemplatesCategory from "./web-templates"; 9 | import projectManagementCategory from "./project-management"; 10 | import picturesCategory from "./pictures"; 11 | import imageCompressionCategory from "./image-compression"; 12 | import learningCategory from "./learning"; 13 | import paaSCategory from "./paas"; 14 | 15 | 16 | export type LinkCategoryName = 'CMS' | 'Colors' | 'Favorites' | 'Forms' | 'Fonts' | 'Icons' | 'Image compression' | 'Learning' | 'Logo creation' | 'PaaS' | 'Pictures' | 'Project management' | 'Web hosting' | 'Web templates'; 17 | 18 | 19 | export type LinkCategory = { 20 | name: LinkCategoryName; 21 | links: Array 22 | }; 23 | 24 | 25 | export type Link = { 26 | name: string; 27 | category: LinkCategoryName; 28 | linkUrl: string; 29 | description: string; 30 | }; 31 | 32 | 33 | const linksByCategory: Array = [ 34 | CMSCategory, 35 | colorsCategory, 36 | formsCategory, 37 | fontsCategory, 38 | iconsCategory, 39 | imageCompressionCategory, 40 | learningCategory, 41 | logoCreationCategory, 42 | paaSCategory, 43 | picturesCategory, 44 | projectManagementCategory, 45 | webHostingCategory, 46 | webTemplatesCategory, 47 | ]; 48 | 49 | 50 | 51 | 52 | export default linksByCategory; -------------------------------------------------------------------------------- /src/data/links/learning/developer-roadmaps.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const DeveloperRoadmapsDescription = 5 | ` 6 | Roadmap.sh is a community effort to create roadmaps, guides, 7 | and other educational content to help guide developers on 8 | the road and guide their learning. 9 | `; 10 | 11 | 12 | const DeveloperRoadmaps: LearningLink = { 13 | name: 'Developer Roadmaps', 14 | category: 'Learning', 15 | linkUrl: 'https://roadmap.sh/', 16 | description: DeveloperRoadmapsDescription, 17 | }; 18 | 19 | export default DeveloperRoadmaps; -------------------------------------------------------------------------------- /src/data/links/learning/edx.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const edXDescription = 5 | ` 6 | Learn from more than 250 leading institutions. 7 | Together with universities and organizations at 8 | the forefront of their fields, edX offers thousands 9 | of job-relevant programs designed to give every 10 | ambitious learner a path to achievement. 11 | `; 12 | 13 | 14 | const edX: LearningLink = { 15 | name: 'edX', 16 | category: 'Learning', 17 | linkUrl: 'https://edx.org/', 18 | description: edXDescription, 19 | }; 20 | 21 | export default edX; -------------------------------------------------------------------------------- /src/data/links/learning/exercism.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const ExercismDescription = 5 | ` 6 | Build fluency in 67 programming languages ​​with our unique 7 | blend of learning, practice, and mentoring. Exercise is 8 | fun, effective, and 100% free, forever. 9 | `; 10 | 11 | 12 | const Exercism: LearningLink = { 13 | name: 'Exercism', 14 | category: 'Learning', 15 | linkUrl: 'https://exercism.org/', 16 | description: ExercismDescription, 17 | }; 18 | 19 | export default Exercism; -------------------------------------------------------------------------------- /src/data/links/learning/free-code-camp.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const FreeCodeCampDescription = 5 | ` 6 | Learn to code for free, build projects and get certified. 7 | `; 8 | 9 | 10 | const FreeCodeCamp: LearningLink = { 11 | name: 'Free Code Camp', 12 | category: 'Learning', 13 | linkUrl: 'https://www.freecodecamp.org/', 14 | description: FreeCodeCampDescription, 15 | }; 16 | 17 | export default FreeCodeCamp; -------------------------------------------------------------------------------- /src/data/links/learning/fullstack-open.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const FullstackOpenDescription = 5 | ` 6 | Learn React, Redux, Node.js, MongoDB, GraphQL, and TypeScript in one go! 7 | `; 8 | 9 | 10 | const FullstackOpen: LearningLink = { 11 | name: 'Fullstack Open', 12 | category: 'Learning', 13 | linkUrl: 'https://fullstackopen.com/', 14 | description: FullstackOpenDescription, 15 | }; 16 | 17 | export default FullstackOpen; -------------------------------------------------------------------------------- /src/data/links/learning/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import DeveloperRoadmaps from './developer-roadmaps'; 3 | import edX from './edx'; 4 | import Exercism from './exercism'; 5 | import FreeCodeCamp from './free-code-camp'; 6 | import FullstackOpen from './fullstack-open'; 7 | import Mimo from './mimo'; 8 | import MITOCW from './mit-ocw'; 9 | import OpenBootcamp from './open-bootcamp'; 10 | import RefactoringGuru from './refactoring-guru'; 11 | import Scrimba from './scrimba'; 12 | import TeachYourselfComputerScience from './teach-yourself-computer-science'; 13 | import TheAlgorithms from './the-algorithms'; 14 | import TheOdinProject from './the-odin-project'; 15 | import W3Schools from './w3schools'; 16 | 17 | 18 | type LearningCategoryName = Extract; 19 | 20 | 21 | export type LearningLink = Omit & { 22 | category: LearningCategoryName 23 | }; 24 | 25 | 26 | const LearningCategoryLinks: Array = [ 27 | DeveloperRoadmaps, 28 | edX, 29 | Exercism, 30 | FreeCodeCamp, 31 | FullstackOpen, 32 | Mimo, 33 | MITOCW, 34 | OpenBootcamp, 35 | RefactoringGuru, 36 | Scrimba, 37 | TeachYourselfComputerScience, 38 | TheAlgorithms, 39 | TheOdinProject, 40 | W3Schools, 41 | ]; 42 | 43 | 44 | const learningCategory: LinkCategory = { 45 | name: 'Learning', 46 | links: LearningCategoryLinks 47 | }; 48 | 49 | 50 | export default learningCategory; -------------------------------------------------------------------------------- /src/data/links/learning/mimo.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const MimoDescription = 5 | ` 6 | Learn to Code Anytime, Anywhere Learning to code can be easy and fun! 7 | `; 8 | 9 | 10 | const Mimo: LearningLink = { 11 | name: 'Mimo', 12 | category: 'Learning', 13 | linkUrl: 'https://mimo.org/', 14 | description: MimoDescription, 15 | }; 16 | 17 | export default Mimo; -------------------------------------------------------------------------------- /src/data/links/learning/mit-ocw.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const MITOCWDescription = 5 | ` 6 | Since 2001, MIT OpenCourseWare has been creating new 7 | opportunities for millions of learners and educators, 8 | sharing Open Educational Resources (OER) from MIT and 9 | helping to lead a global revolution in free access to knowledge. 10 | `; 11 | 12 | 13 | const MITOCW: LearningLink = { 14 | name: 'MIT OCW', 15 | category: 'Learning', 16 | linkUrl: 'https://ocw.mit.edu/', 17 | description: MITOCWDescription, 18 | }; 19 | 20 | export default MITOCW; -------------------------------------------------------------------------------- /src/data/links/learning/open-bootcamp.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const OpenBootcampDescription = 5 | ` 6 | Free online teaching platform and access to the job market. 7 | `; 8 | 9 | 10 | const OpenBootcamp: LearningLink = { 11 | name: 'Open Bootcamp', 12 | category: 'Learning', 13 | linkUrl: 'https://open-bootcamp.com/', 14 | description: OpenBootcampDescription, 15 | }; 16 | 17 | export default OpenBootcamp; -------------------------------------------------------------------------------- /src/data/links/learning/refactoring-guru.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const RefactoringGuruDescription = 5 | ` 6 | Find out everything you need to know about refactoring, design patterns, 7 | SOLID principles, and other smart programming topics. 8 | `; 9 | 10 | 11 | const RefactoringGuru: LearningLink = { 12 | name: 'Refactoring Guru', 13 | category: 'Learning', 14 | linkUrl: 'https://refactoring.guru/', 15 | description: RefactoringGuruDescription, 16 | }; 17 | 18 | export default RefactoringGuru; -------------------------------------------------------------------------------- /src/data/links/learning/scrimba.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const ScrimbaDescription = 5 | ` 6 | Don't spend $15K on a coding bootcamp. Our Frontend Career 7 | Path helps motivated students become hireable developers 8 | for a fraction of the cost. 9 | `; 10 | 11 | 12 | const Scrimba: LearningLink = { 13 | name: 'Scrimba', 14 | category: 'Learning', 15 | linkUrl: 'https://scrimba.com/', 16 | description: ScrimbaDescription, 17 | }; 18 | 19 | export default Scrimba; -------------------------------------------------------------------------------- /src/data/links/learning/teach-yourself-computer-science.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const TeachYourselfComputerScienceDescription = 5 | ` 6 | Computer Science learning route from the hand of great professionals. 7 | `; 8 | 9 | 10 | const TeachYourselfComputerScience: LearningLink = { 11 | name: 'Teach Yourself Computer Science', 12 | category: 'Learning', 13 | linkUrl: 'https://teachyourselfcs.com/', 14 | description: TeachYourselfComputerScienceDescription, 15 | }; 16 | 17 | export default TeachYourselfComputerScience; -------------------------------------------------------------------------------- /src/data/links/learning/the-algorithms.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const TheAlgorithmsDescription = 5 | ` 6 | The largest open source algorithm library on GitHub. 7 | `; 8 | 9 | 10 | const TheAlgorithms: LearningLink = { 11 | name: 'The Algorithms', 12 | category: 'Learning', 13 | linkUrl: 'https://the-algorithms.com/', 14 | description: TheAlgorithmsDescription, 15 | }; 16 | 17 | export default TheAlgorithms; -------------------------------------------------------------------------------- /src/data/links/learning/the-odin-project.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const TheOdinProjectDescription = 5 | ` 6 | The Odin Project is a free, open-source educational platform 7 | for full-stack web development training. The program offers 8 | courses that cover the basics of web development, full-stack 9 | JavaScript, and Ruby on Rails. 10 | `; 11 | 12 | 13 | const TheOdinProject: LearningLink = { 14 | name: 'The Odin Project', 15 | category: 'Learning', 16 | linkUrl: 'https://www.theodinproject.com/', 17 | description: TheOdinProjectDescription, 18 | }; 19 | 20 | export default TheOdinProject; -------------------------------------------------------------------------------- /src/data/links/learning/w3schools.ts: -------------------------------------------------------------------------------- 1 | import type { LearningLink } from "./"; 2 | 3 | 4 | const W3SchoolsDescription = 5 | ` 6 | W3Schools offers free online tutorials, references, 7 | and exercises in all the major languages ​​of the web. 8 | `; 9 | 10 | 11 | const W3Schools: LearningLink = { 12 | name: 'W3Schools', 13 | category: 'Learning', 14 | linkUrl: 'https://www.w3schools.com/', 15 | description: W3SchoolsDescription, 16 | }; 17 | 18 | export default W3Schools; -------------------------------------------------------------------------------- /src/data/links/logo-creation/adobe-express-logo-maker.ts: -------------------------------------------------------------------------------- 1 | import type { LogoCreationLink } from "./"; 2 | 3 | 4 | const AdobeExpressLogoMakerDescription = 5 | ` 6 | Adobe Express Logo Maker quickly gets you quality, AI-generated 7 | logos that can be shared across all your print and digital platforms. 8 | `; 9 | 10 | const AdobeExpressLogoMaker: LogoCreationLink = { 11 | name: 'Adobe Express logo maker', 12 | category: 'Logo creation', 13 | linkUrl: 'https://www.adobe.com/express/create/logo/', 14 | description: AdobeExpressLogoMakerDescription, 15 | }; 16 | 17 | export default AdobeExpressLogoMaker; -------------------------------------------------------------------------------- /src/data/links/logo-creation/brandmark.ts: -------------------------------------------------------------------------------- 1 | import type { LogoCreationLink } from "."; 2 | 3 | 4 | const BrandmarkDescription = 5 | ` 6 | Create a unique and professional logo for your company. 7 | Jumpstart your brand with business card designs, social media graphics, 8 | app icons, letterheads and more. 9 | `; 10 | 11 | const Brandmark: LogoCreationLink = { 12 | name: 'Brandmark', 13 | category: 'Logo creation', 14 | linkUrl: 'https://brandmark.io/', 15 | description: BrandmarkDescription, 16 | }; 17 | 18 | export default Brandmark; -------------------------------------------------------------------------------- /src/data/links/logo-creation/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import AdobeExpressLogoMaker from './adobe-express-logo-maker'; 3 | import Brandmark from './brandmark'; 4 | import Logoai from './logoai'; 5 | import Logopony from './logopony'; 6 | import Looka from './looka'; 7 | import SmashingLogo from './smashing-logo'; 8 | 9 | 10 | type LogoCreationCategoryName = Extract; 11 | 12 | 13 | export type LogoCreationLink = Omit & { 14 | category: LogoCreationCategoryName 15 | }; 16 | 17 | 18 | const logoCreationCategoryLinks: Array = [ 19 | AdobeExpressLogoMaker, 20 | Brandmark, 21 | Logoai, 22 | Logopony, 23 | Looka, 24 | SmashingLogo 25 | ]; 26 | 27 | 28 | const logoCreationCategory: LinkCategory = { 29 | name: 'Logo creation', 30 | links: logoCreationCategoryLinks 31 | }; 32 | 33 | 34 | export default logoCreationCategory; -------------------------------------------------------------------------------- /src/data/links/logo-creation/logoai.ts: -------------------------------------------------------------------------------- 1 | import type { LogoCreationLink } from "."; 2 | 3 | 4 | const LogoaiDescription = 5 | ` 6 | Let the AI-powered logo maker generate your new logo, 7 | create matching stationery, and design a brand you love! 8 | `; 9 | 10 | const Logoai: LogoCreationLink = { 11 | name: 'Logoai', 12 | category: 'Logo creation', 13 | linkUrl: 'https://www.logoai.com/', 14 | description: LogoaiDescription, 15 | }; 16 | 17 | export default Logoai; -------------------------------------------------------------------------------- /src/data/links/logo-creation/logopony.ts: -------------------------------------------------------------------------------- 1 | import type { LogoCreationLink } from "."; 2 | 3 | 4 | const LogoponyDescription = 5 | ` 6 | Make your own beautiful logo. Generate thousands of custom aesthetic 7 | logo designs and instantly create social media designs, business cards, 8 | and other design files for your new branding with just a few clicks. 9 | `; 10 | 11 | const Logopony: LogoCreationLink = { 12 | name: 'Logopony', 13 | category: 'Logo creation', 14 | linkUrl: 'https://www.logopony.com/', 15 | description: LogoponyDescription, 16 | }; 17 | 18 | export default Logopony; -------------------------------------------------------------------------------- /src/data/links/logo-creation/looka.ts: -------------------------------------------------------------------------------- 1 | import type { LogoCreationLink } from "."; 2 | 3 | 4 | const LookaDescription = 5 | ` 6 | Design your own beautiful brand. Use Looka's AI-powered platform 7 | to design a logo and build a brand you love. 8 | `; 9 | 10 | const Looka: LogoCreationLink = { 11 | name: 'Looka', 12 | category: 'Logo creation', 13 | linkUrl: 'https://looka.com/', 14 | description: LookaDescription, 15 | }; 16 | 17 | export default Looka; -------------------------------------------------------------------------------- /src/data/links/logo-creation/smashing-logo.ts: -------------------------------------------------------------------------------- 1 | import type { LogoCreationLink } from "."; 2 | 3 | 4 | const SmashingLogoDescription = 5 | ` 6 | Design a logo you love. No registration required. Start free. 7 | `; 8 | 9 | const SmashingLogo: LogoCreationLink = { 10 | name: 'Smashing Logo', 11 | category: 'Logo creation', 12 | linkUrl: 'https://smashinglogo.com/en/', 13 | description: SmashingLogoDescription, 14 | }; 15 | 16 | export default SmashingLogo; -------------------------------------------------------------------------------- /src/data/links/paas/adaptable.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const AdaptableDescription = 5 | ` 6 | The easiest way to deploy. 7 | Just connect your GitHub repository and let Adaptable handle the rest. 8 | `; 9 | 10 | const Adaptable: PaaSLink = { 11 | name: 'Adaptable', 12 | category: 'PaaS', 13 | linkUrl: 'https://adaptable.io/', 14 | description: AdaptableDescription, 15 | }; 16 | 17 | export default Adaptable; -------------------------------------------------------------------------------- /src/data/links/paas/control-plane.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const ControlPlaneDescription = 5 | ` 6 | Run compute anywhere, combine cloud services, boost developer 7 | productivity, and slash costs. 8 | `; 9 | 10 | const ControlPlane: PaaSLink = { 11 | name: 'ControlPlane', 12 | category: 'PaaS', 13 | linkUrl: 'https://controlplane.com/', 14 | description: ControlPlaneDescription, 15 | }; 16 | 17 | export default ControlPlane; -------------------------------------------------------------------------------- /src/data/links/paas/cyclic.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const CyclicDescription = 5 | ` 6 | Connect your GitHub repo. 7 | We will build, deploy and manage the hosting. 8 | `; 9 | 10 | const Cyclic: PaaSLink = { 11 | name: 'Cyclic', 12 | category: 'PaaS', 13 | linkUrl: 'https://cyclic.sh/', 14 | description: CyclicDescription, 15 | }; 16 | 17 | export default Cyclic; -------------------------------------------------------------------------------- /src/data/links/paas/deno-deploy.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const DenoDeployDescription = 5 | ` 6 | Serverless JavaScript hosting with zero config, worldwide. 7 | `; 8 | 9 | const DenoDeploy: PaaSLink = { 10 | name: 'Deno Deploy', 11 | category: 'PaaS', 12 | linkUrl: 'https://deno.com/deploy', 13 | description: DenoDeployDescription, 14 | }; 15 | 16 | export default DenoDeploy; -------------------------------------------------------------------------------- /src/data/links/paas/deta.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const DetaDescription = 5 | ` 6 | Deta Space is a Cloud Computer that belongs to you, goes with you 7 | anywhere, and gives your apps and data a new home and a new life. 8 | `; 9 | 10 | const Deta: PaaSLink = { 11 | name: 'Deta', 12 | category: 'PaaS', 13 | linkUrl: 'https://www.deta.sh/', 14 | description: DetaDescription, 15 | }; 16 | 17 | export default Deta; -------------------------------------------------------------------------------- /src/data/links/paas/fl0.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const fl0Description = 5 | ` 6 | Deploy backend applications and databases in minutes. 7 | `; 8 | 9 | const fl0: PaaSLink = { 10 | name: 'fl0', 11 | category: 'PaaS', 12 | linkUrl: 'https://www.fl0.com/', 13 | description: fl0Description, 14 | }; 15 | 16 | export default fl0; -------------------------------------------------------------------------------- /src/data/links/paas/fly-io.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const FlyioDescription = 5 | ` 6 | Run your full stack apps (and databases!) all over the world. No ops required. 7 | `; 8 | 9 | const Flyio: PaaSLink = { 10 | name: 'Fly.io', 11 | category: 'PaaS', 12 | linkUrl: 'https://fly.io/', 13 | description: FlyioDescription, 14 | }; 15 | 16 | export default Flyio; -------------------------------------------------------------------------------- /src/data/links/paas/glitch.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const GlicthDescription = 5 | ` 6 | Glitch is the friendly place where everyone builds the web. 7 | Start a new blog, play with React, or build new worlds with WebXR. 8 | `; 9 | 10 | const Glitch: PaaSLink = { 11 | name: 'Glitch', 12 | category: 'PaaS', 13 | linkUrl: 'https://glitch.com/', 14 | description: GlicthDescription, 15 | }; 16 | 17 | export default Glitch; -------------------------------------------------------------------------------- /src/data/links/paas/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import Adaptable from './adaptable'; 3 | import ControlPlane from './control-plane'; 4 | import Cyclic from './cyclic'; 5 | import DenoDeploy from './deno-deploy'; 6 | import Deta from './deta'; 7 | import fl0 from './fl0'; 8 | import Flyio from './fly-io'; 9 | import Glitch from './glitch'; 10 | import Northflank from './northflank'; 11 | import Qoddi from './qoddi'; 12 | import Railway from './railway'; 13 | import Render from './render'; 14 | 15 | 16 | type PaaSCategoryName = Extract; 17 | 18 | 19 | export type PaaSLink = Omit & { 20 | category: PaaSCategoryName 21 | }; 22 | 23 | 24 | const paaSCategoryLinks: Array = [ 25 | Adaptable, 26 | ControlPlane, 27 | Cyclic, 28 | DenoDeploy, 29 | Deta, 30 | fl0, 31 | Flyio, 32 | Glitch, 33 | Northflank, 34 | Qoddi, 35 | Railway, 36 | Render, 37 | ]; 38 | 39 | 40 | const paaSCategory: LinkCategory = { 41 | name: 'PaaS', 42 | links: paaSCategoryLinks 43 | }; 44 | 45 | 46 | export default paaSCategory; -------------------------------------------------------------------------------- /src/data/links/paas/northflank.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const NorthflankDescription = 5 | ` 6 | Northflank is the comprehensive developer platform designed for scale. 7 | Seamlessly deploy and release any project on our cloud or in yours. 8 | `; 9 | 10 | const Northflank: PaaSLink = { 11 | name: 'Northflank', 12 | category: 'PaaS', 13 | linkUrl: 'https://northflank.com/', 14 | description: NorthflankDescription, 15 | }; 16 | 17 | export default Northflank; -------------------------------------------------------------------------------- /src/data/links/paas/qoddi.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const QoddiDescription = 5 | ` 6 | Push, Deploy, Scale. 7 | We'll do the rest. 8 | Everything included. 9 | Don't pay for bandwidth, backups, and load balancers. 10 | Deploy in seconds from Git, work with unlimited team members at 11 | no additional cost, and instantly scale to millions of daily users. 12 | `; 13 | 14 | const Qoddi: PaaSLink = { 15 | name: 'Qoddi', 16 | category: 'PaaS', 17 | linkUrl: 'https://qoddi.com/', 18 | description: QoddiDescription, 19 | }; 20 | 21 | export default Qoddi; -------------------------------------------------------------------------------- /src/data/links/paas/railway.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const RailwayDescription = 5 | ` 6 | Bring your code, 7 | we'll handle the rest. 8 | Made for any language, for projects big and small. 9 | Railway is the cloud that takes the complexity out 10 | of shipping software. 11 | `; 12 | 13 | const Railway: PaaSLink = { 14 | name: 'Railway', 15 | category: 'PaaS', 16 | linkUrl: 'https://railway.app/', 17 | description: RailwayDescription, 18 | }; 19 | 20 | export default Railway; -------------------------------------------------------------------------------- /src/data/links/paas/render.ts: -------------------------------------------------------------------------------- 1 | import type { PaaSLink } from "."; 2 | 3 | 4 | const RenderDescription = 5 | ` 6 | The fastest way to host all. Render is a unified cloud to build and 7 | run all your apps and websites with TLS certificates, a global CDN, 8 | DDos protection, private networks, and autodeploys from Git. 9 | `; 10 | 11 | const Render: PaaSLink = { 12 | name: 'Render', 13 | category: 'PaaS', 14 | linkUrl: 'https://render.com/', 15 | description: RenderDescription, 16 | }; 17 | 18 | export default Render; -------------------------------------------------------------------------------- /src/data/links/pictures/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import Magdeleine from './magdeleine'; 3 | import NegativeSpace from './negative-space'; 4 | import Pexels from './pexels'; 5 | import Picspree from './picspree'; 6 | import pixabay from './pixabay'; 7 | import PxHere from './px-here'; 8 | import RealisticShots from './realistic-shots'; 9 | import unDraw from './undraw'; 10 | import Unsplash from './unsplash'; 11 | 12 | type PicturesCategoryName = Extract; 13 | 14 | 15 | export type PicturesLink = Omit & { 16 | category: PicturesCategoryName 17 | }; 18 | 19 | 20 | const PicturesCategoryLinks: Array = [ 21 | Magdeleine, 22 | NegativeSpace, 23 | Pexels, 24 | Picspree, 25 | pixabay, 26 | PxHere, 27 | RealisticShots, 28 | unDraw, 29 | Unsplash, 30 | ]; 31 | 32 | 33 | const picturesCategory: LinkCategory = { 34 | name: 'Pictures', 35 | links: PicturesCategoryLinks 36 | }; 37 | 38 | 39 | export default picturesCategory; -------------------------------------------------------------------------------- /src/data/links/pictures/magdeleine.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const MagdeleineDescription = 5 | ` 6 | Gallery & free high-resolution photo everyday. 7 | `; 8 | 9 | 10 | const Magdeleine: PicturesLink = { 11 | name: 'Magdeleine', 12 | category: 'Pictures', 13 | linkUrl: 'https://magdeleine.co/', 14 | description: MagdeleineDescription, 15 | }; 16 | 17 | export default Magdeleine; -------------------------------------------------------------------------------- /src/data/links/pictures/negative-space.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const NegativeSpaceDescription = 5 | ` 6 | High-Resolution Free Stock Photos. 7 | `; 8 | 9 | 10 | const NegativeSpace: PicturesLink = { 11 | name: 'Negative Space', 12 | category: 'Pictures', 13 | linkUrl: 'https://negativespace.co/', 14 | description: NegativeSpaceDescription, 15 | }; 16 | 17 | export default NegativeSpace; -------------------------------------------------------------------------------- /src/data/links/pictures/pexels.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const PexelsDescription = 5 | ` 6 | Free stock photos shared by talented creators. 7 | `; 8 | 9 | 10 | const Pexels: PicturesLink = { 11 | name: 'Pexels', 12 | category: 'Pictures', 13 | linkUrl: 'https://www.pexels.com/', 14 | description: PexelsDescription, 15 | }; 16 | 17 | export default Pexels; -------------------------------------------------------------------------------- /src/data/links/pictures/picspree.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const PicspreeDescription = 5 | ` 6 | Royalty free images, stock photos, illustrations, and vectors. 7 | `; 8 | 9 | 10 | const Picspree: PicturesLink = { 11 | name: 'Picspree', 12 | category: 'Pictures', 13 | linkUrl: 'https://www.freeimages.com/?ref=picspree/', 14 | description: PicspreeDescription, 15 | }; 16 | 17 | export default Picspree; -------------------------------------------------------------------------------- /src/data/links/pictures/pixabay.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const pixabayDescription = 5 | ` 6 | Over 4.1 million+ high quality stock images, videos and music shared by our talented community. 7 | `; 8 | 9 | 10 | const pixabay: PicturesLink = { 11 | name: 'pixabay', 12 | category: 'Pictures', 13 | linkUrl: 'https://pixabay.com/', 14 | description: pixabayDescription, 15 | }; 16 | 17 | export default pixabay; -------------------------------------------------------------------------------- /src/data/links/pictures/px-here.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const PxHereDescription = 5 | ` 6 | Free Images & Free stock photos. Free of copyrights under CC0. Do whatever you want. 7 | `; 8 | 9 | 10 | const PxHere: PicturesLink = { 11 | name: 'PxHere', 12 | category: 'Pictures', 13 | linkUrl: 'https://pxhere.com/', 14 | description: PxHereDescription, 15 | }; 16 | 17 | export default PxHere; -------------------------------------------------------------------------------- /src/data/links/pictures/realistic-shots.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const RealisticShotsDescription = 5 | ` 6 | Free stock photos (high resolution) for personal and commercial use. 7 | 7 new photos every week. Just make something creative! 8 | `; 9 | 10 | 11 | const RealisticShots: PicturesLink = { 12 | name: 'Realistic Shots', 13 | category: 'Pictures', 14 | linkUrl: 'https://realisticshots.com/', 15 | description: RealisticShotsDescription, 16 | }; 17 | 18 | export default RealisticShots; -------------------------------------------------------------------------------- /src/data/links/pictures/undraw.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const unDrawDescription = 5 | ` 6 | Create better designed websites, products and applications. 7 | Browse to find the images that fit your messaging, 8 | automagically customise the color to match your brand and 9 | use it as a normal image, embedded code or directly in your 10 | design workflow. 11 | `; 12 | 13 | 14 | const unDraw: PicturesLink = { 15 | name: 'unDraw', 16 | category: 'Pictures', 17 | linkUrl: 'https://undraw.co/', 18 | description: unDrawDescription, 19 | }; 20 | 21 | export default unDraw; -------------------------------------------------------------------------------- /src/data/links/pictures/unsplash.ts: -------------------------------------------------------------------------------- 1 | import type { PicturesLink } from "./"; 2 | 3 | 4 | const UnsplashDescription = 5 | ` 6 | The internet’s source of freely usable images. 7 | `; 8 | 9 | 10 | const Unsplash: PicturesLink = { 11 | name: 'Unsplash', 12 | category: 'Pictures', 13 | linkUrl: 'https://unsplash.com/', 14 | description: UnsplashDescription, 15 | }; 16 | 17 | export default Unsplash; -------------------------------------------------------------------------------- /src/data/links/project-management/azure-devops.ts: -------------------------------------------------------------------------------- 1 | import type { ProjectManagementLink } from "."; 2 | 3 | 4 | const AzureDevOpsDescription = 5 | ` 6 | Azure DevOps is an end-to-end software development platform that offers 7 | an assortment of capabilities intended to organize and accelerate development 8 | efforts across the entire application lifecycle, like requirements management, 9 | project management, Version control, Automated builds, Reporting, testing and 10 | release management. 11 | `; 12 | 13 | const AzureDevOps: ProjectManagementLink = { 14 | name: 'Azure DevOps', 15 | category: 'Project management', 16 | linkUrl: 'https://azure.microsoft.com/en-us/products/devops/', 17 | description: AzureDevOpsDescription, 18 | }; 19 | 20 | export default AzureDevOps; -------------------------------------------------------------------------------- /src/data/links/project-management/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import AzureDevOps from './azure-devops'; 3 | import Taiga from './taiga'; 4 | 5 | 6 | type ProjectManagementCategoryName = Extract; 7 | 8 | 9 | export type ProjectManagementLink = Omit & { 10 | category: ProjectManagementCategoryName 11 | }; 12 | 13 | 14 | const ProjectManagementCategoryLinks: Array = [ 15 | AzureDevOps, 16 | Taiga, 17 | ]; 18 | 19 | 20 | const projectManagementCategory: LinkCategory = { 21 | name: 'Project management', 22 | links: ProjectManagementCategoryLinks 23 | }; 24 | 25 | 26 | export default projectManagementCategory; -------------------------------------------------------------------------------- /src/data/links/project-management/taiga.ts: -------------------------------------------------------------------------------- 1 | import type { ProjectManagementLink } from "."; 2 | 3 | 4 | const TaigaDescription = 5 | ` 6 | The free and open-source project management tool. 7 | Agile project management made easy, intuitive and effective. 8 | `; 9 | 10 | const Taiga: ProjectManagementLink = { 11 | name: 'Taiga', 12 | category: 'Project management', 13 | linkUrl: 'https://taiga.io/', 14 | description: TaigaDescription, 15 | }; 16 | 17 | export default Taiga; -------------------------------------------------------------------------------- /src/data/links/web-hosting/contabo.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const ContaboDescription = 5 | ` 6 | Get the cloud instances or dedicated servers you want, for one great price. 7 | `; 8 | 9 | const Contabo: WebHostingLink = { 10 | name: 'Contabo', 11 | category: 'Web hosting', 12 | linkUrl: 'https://contabo.com/en/', 13 | description: ContaboDescription, 14 | }; 15 | 16 | export default Contabo; -------------------------------------------------------------------------------- /src/data/links/web-hosting/digitalocean.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const DigitalOceanDescription = 5 | ` 6 | Simple, scalable cloud computing solutions built for 7 | startups and small and medium-sized businesses. 8 | `; 9 | 10 | const DigitalOcean: WebHostingLink = { 11 | name: 'DigitalOcean', 12 | category: 'Web hosting', 13 | linkUrl: 'https://www.digitalocean.com/', 14 | description: DigitalOceanDescription, 15 | }; 16 | 17 | export default DigitalOcean; -------------------------------------------------------------------------------- /src/data/links/web-hosting/free-hosting-no-ads.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const FreeHostingNoAdsDescription = 5 | ` 6 | Free hosting with PHP 5, Perl, CGI, MySQL, FTP, file manager, 7 | POP email, free subdomains, free domain hosting, DNS zone editor, 8 | website statistics, FREE online support and many more. 9 | `; 10 | 11 | const FreeHostingNoAds: WebHostingLink = { 12 | name: 'FreeHostingNoAds', 13 | category: 'Web hosting', 14 | linkUrl: 'https://freehostingnoads.net/', 15 | description: FreeHostingNoAdsDescription, 16 | }; 17 | 18 | export default FreeHostingNoAds; -------------------------------------------------------------------------------- /src/data/links/web-hosting/hetzner.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const HetznerDescription = 5 | ` 6 | Maximize operations, minimize costs. Discover the 7 | powerful and sustainable Arm64 architecture. 8 | `; 9 | 10 | const Hetzner: WebHostingLink = { 11 | name: 'Hetzner', 12 | category: 'Web hosting', 13 | linkUrl: 'https://www.hetzner.com/cloud/', 14 | description: HetznerDescription, 15 | }; 16 | 17 | export default Hetzner; -------------------------------------------------------------------------------- /src/data/links/web-hosting/hostinger.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const HostingerDescription = 5 | ` 6 | Hostinger offers shared, cloud and VPS hosting services with MySQL, 7 | FTP and PHP as well as easy integration with WordPress, Shopify, 8 | Drupal, etc. 9 | `; 10 | 11 | const Hostinger: WebHostingLink = { 12 | name: 'Hostinger', 13 | category: 'Web hosting', 14 | linkUrl: 'https://www.hostinger.com/', 15 | description: HostingerDescription, 16 | }; 17 | 18 | export default Hostinger; -------------------------------------------------------------------------------- /src/data/links/web-hosting/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import Contabo from './contabo'; 3 | import DigitalOcean from './digitalocean'; 4 | import FreeHostingNoAds from './free-hosting-no-ads'; 5 | import Hetzner from './hetzner'; 6 | import Hostinger from './hostinger'; 7 | import Netlify from './netlify'; 8 | import pythonanywhere from './pythonanywhere'; 9 | import Vercel from './vercel'; 10 | 11 | 12 | type WebHostingCategoryName = Extract; 13 | 14 | 15 | export type WebHostingLink = Omit & { 16 | category: WebHostingCategoryName 17 | }; 18 | 19 | 20 | const webHostingCategoryLinks: Array = [ 21 | Contabo, 22 | DigitalOcean, 23 | FreeHostingNoAds, 24 | Hetzner, 25 | Hostinger, 26 | Netlify, 27 | pythonanywhere, 28 | Vercel, 29 | ]; 30 | 31 | 32 | const webHostingCategory: LinkCategory = { 33 | name: 'Web hosting', 34 | links: webHostingCategoryLinks 35 | }; 36 | 37 | 38 | export default webHostingCategory; -------------------------------------------------------------------------------- /src/data/links/web-hosting/netlify.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const NetlifyDescription = 5 | ` 6 | Accelerate the time to implement your websites and applications. 7 | Bring your integrations and APIs together in one powerful 8 | serverless platform. Get started for free! 9 | `; 10 | 11 | const Netlify: WebHostingLink = { 12 | name: 'Netlify', 13 | category: 'Web hosting', 14 | linkUrl: 'https://www.netlify.com/', 15 | description: NetlifyDescription, 16 | }; 17 | 18 | export default Netlify; -------------------------------------------------------------------------------- /src/data/links/web-hosting/pythonanywhere.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const pythonanywhereDescription = 5 | ` 6 | Host, run, and program Python in the cloud! You can deploy 7 | and host your website or any other code directly from your 8 | browser without having to install software or manage your own server. 9 | `; 10 | 11 | const pythonanywhere: WebHostingLink = { 12 | name: 'pythonanywhere', 13 | category: 'Web hosting', 14 | linkUrl: 'https://www.pythonanywhere.com/', 15 | description: pythonanywhereDescription, 16 | }; 17 | 18 | export default pythonanywhere; -------------------------------------------------------------------------------- /src/data/links/web-hosting/vercel.ts: -------------------------------------------------------------------------------- 1 | import type { WebHostingLink } from "."; 2 | 3 | 4 | const VercelDescription = 5 | ` 6 | Build, deploy, and host web apps with free SSL, global CDN, 7 | and unique preview URLs every time you git push. Perfect for 8 | Next.js and other static site builders. 9 | `; 10 | 11 | const Vercel: WebHostingLink = { 12 | name: 'Vercel', 13 | category: 'Web hosting', 14 | linkUrl: 'https://vercel.com/', 15 | description: VercelDescription, 16 | }; 17 | 18 | export default Vercel; -------------------------------------------------------------------------------- /src/data/links/web-templates/astro-themes.ts: -------------------------------------------------------------------------------- 1 | import type { WebTemplatesLink } from "./"; 2 | 3 | 4 | const AstroThemesDescription = 5 | ` 6 | Start your next project with an Astro theme. 7 | `; 8 | 9 | 10 | const AstroThemes: WebTemplatesLink = { 11 | name: 'Astro Themes', 12 | category: 'Web templates', 13 | linkUrl: 'https://astro.build/themes/', 14 | description: AstroThemesDescription, 15 | }; 16 | 17 | export default AstroThemes; -------------------------------------------------------------------------------- /src/data/links/web-templates/index.ts: -------------------------------------------------------------------------------- 1 | import type { Link, LinkCategory, LinkCategoryName } from '../index'; 2 | import AstroThemes from './astro-themes'; 3 | import Statichunt from './statichunt'; 4 | 5 | 6 | type WebTemplatesCategoryName = Extract; 7 | 8 | 9 | export type WebTemplatesLink = Omit & { 10 | category: WebTemplatesCategoryName 11 | }; 12 | 13 | 14 | const WebTemplatesCategoryLinks: Array = [ 15 | AstroThemes, 16 | Statichunt, 17 | ]; 18 | 19 | 20 | const webTemplatesCategory: LinkCategory = { 21 | name: 'Web templates', 22 | links: WebTemplatesCategoryLinks 23 | }; 24 | 25 | 26 | export default webTemplatesCategory; -------------------------------------------------------------------------------- /src/data/links/web-templates/statichunt.ts: -------------------------------------------------------------------------------- 1 | import type { WebTemplatesLink } from "./"; 2 | 3 | 4 | const StatichuntDescription = 5 | ` 6 | Statichunt is an open source directory that includes hundreds 7 | of themes and resources for static site generators submitted 8 | by the community. 9 | `; 10 | 11 | 12 | const Statichunt: WebTemplatesLink = { 13 | name: 'Statichunt', 14 | category: 'Web templates', 15 | linkUrl: 'https://statichunt.com/', 16 | description: StatichuntDescription, 17 | }; 18 | 19 | export default Statichunt; -------------------------------------------------------------------------------- /src/features/favoriteLinks.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@plasmohq/storage"; 2 | import { type Link } from '../data/links/index'; 3 | 4 | 5 | const storage = new Storage(); 6 | const FAVORITE_LINKS = 'FAVORITE_LINKS'; 7 | 8 | 9 | export const getFavoriteLinks = async (): Promise> => { 10 | const value = await storage.get(FAVORITE_LINKS); 11 | 12 | if(value) { 13 | return JSON.parse(value) as Array; 14 | } 15 | 16 | return []; 17 | }; 18 | 19 | export const addNewFavoriteLink = async (aLink: Link) => { 20 | 21 | const links = await getFavoriteLinks(); 22 | links.push(aLink); 23 | 24 | const sortedLinks = links.sort((a, b) => a.name > b.name ? 1 : -1); 25 | 26 | await storage.set(FAVORITE_LINKS, JSON.stringify(sortedLinks)); 27 | 28 | }; 29 | 30 | 31 | export const removeFavoriteLink = async (linkToRemove: Link) => { 32 | 33 | const links = await getFavoriteLinks(); 34 | const linksWithAfterRemoval = links.filter(aLink => aLink.name !== linkToRemove.name); 35 | 36 | await storage.set(FAVORITE_LINKS, JSON.stringify(linksWithAfterRemoval)); 37 | }; -------------------------------------------------------------------------------- /src/features/searchLinks.ts: -------------------------------------------------------------------------------- 1 | import MiniSearch, { type SearchResult} from 'minisearch'; 2 | import linksByCategory, { type Link, type LinkCategory } from "~data/links"; 3 | 4 | 5 | const searchIndex = new MiniSearch({ 6 | fields: ['name', 'description', 'category'], 7 | storeFields: ['name', 'description', 'category', 'linkUrl'], 8 | searchOptions: { 9 | prefix: true, 10 | combineWith: 'AND' 11 | } 12 | }); 13 | 14 | 15 | 16 | linksByCategory.forEach((aCategory) => { 17 | 18 | aCategory.links.forEach((aLink) => { 19 | searchIndex.add({...aLink, id: aLink.name}); 20 | }); 21 | }); 22 | 23 | 24 | 25 | /** 26 | * This class allows to search links by name or description. 27 | * The constructor receives a callback that will be called 28 | * after a search have successfully done. 29 | * 30 | * For searching, the instance method 'searchLinks' can be used. 31 | */ 32 | class LinkSearcher { 33 | 34 | private foundCallback: (foundLinksByCategory: Array) => void; 35 | private lastSearchPromise: Promise>; 36 | 37 | constructor(foundCallback: (foundLinksByCategory: Array) => void) { 38 | this.foundCallback = foundCallback; 39 | } 40 | 41 | /** 42 | * This method recieves the user input for searching links. 43 | * You can call this every time the user modified the search input. 44 | * If some searches are being executed, at the same time, for avoiding 45 | * race conditions and rare behaviors, only the last search request will 46 | * trigger the call to the callback passed to LinkSearcher constructor. 47 | * 48 | * @param searchText the user input for searching links 49 | */ 50 | async searchLinks(searchText: string) { 51 | const currentSearchPromise = new Promise>((resolve) => { 52 | const searchedLinksByCategory = searchLinks(searchText); 53 | resolve(searchedLinksByCategory); 54 | }); 55 | this.lastSearchPromise = currentSearchPromise; 56 | 57 | const searchedLinksByCategory = await currentSearchPromise; 58 | 59 | if (currentSearchPromise === this.lastSearchPromise) { 60 | this.foundCallback(searchedLinksByCategory); 61 | } 62 | 63 | } 64 | 65 | } 66 | 67 | 68 | const searchLinks = (searchText: string) => { 69 | 70 | const query = constructQueryString(searchText); 71 | 72 | const results = searchIndex.search(query); 73 | 74 | const resultLinks = results.map((aResult: SearchResult & Link) => { 75 | return { 76 | name: aResult.name, 77 | category: aResult.category, 78 | linkUrl: aResult.linkUrl, 79 | description: aResult.description, 80 | }; 81 | }); 82 | 83 | return groupLinksByCategory(resultLinks); 84 | 85 | }; 86 | 87 | 88 | const constructQueryString = (searchText: string) => { 89 | const words = searchText.split(' '); 90 | let queryString = ''; 91 | 92 | words.forEach((word) => { 93 | if (word) { 94 | queryString = `${queryString} ${word}`; 95 | } 96 | }); 97 | 98 | return queryString.trim(); 99 | }; 100 | 101 | 102 | const groupLinksByCategory = (links: Array) => { 103 | 104 | const linkCategories: Array = []; 105 | const foundCategories: Set = new Set(); 106 | 107 | links.forEach((aLink) => { 108 | 109 | if (!foundCategories.has(aLink.category)) { 110 | foundCategories.add(aLink.category); 111 | linkCategories.push({ 112 | name: aLink.category, 113 | links: [], 114 | }); 115 | } 116 | 117 | const categoryForAddingLink = linkCategories.find(category => category.name === aLink.category); 118 | categoryForAddingLink.links.push(aLink); 119 | 120 | }); 121 | 122 | // Sort links alphabetically within each category 123 | linkCategories.forEach((aCategory) => { 124 | aCategory.links = aCategory.links.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1); 125 | }); 126 | 127 | // Sort categories alphabetically by name 128 | const sortedCategories = linkCategories.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1); 129 | 130 | return sortedCategories; 131 | }; 132 | 133 | 134 | export default LinkSearcher; -------------------------------------------------------------------------------- /src/options.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | function IndexOptions() { 4 | 5 | return ( 6 |

Coming soon!

7 | ); 8 | } 9 | 10 | export default IndexOptions; 11 | -------------------------------------------------------------------------------- /src/popup.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import Button from "~components/Button"; 3 | import OpenIcon from "data-base64:~assets/icons/general/open.svg"; 4 | import LogoIcon from "data-base64:~assets/icons/general/logo.svg"; 5 | import "./style.css"; 6 | 7 | 8 | function IndexPopup() { 9 | 10 | useEffect(() => { 11 | if(!navigator.userAgent.toLowerCase().includes('fox')) { 12 | window.open('./tabs/browse-links.html'); 13 | } 14 | }, []); 15 | 16 | return (
17 | 18 |
19 | 20 |
21 | 22 | 25 | 26 |

Dev Links

27 | 28 |
29 | 30 | 31 | All links a developer would need 32 | 33 | 34 |
35 | 36 | 50 |
); 51 | } 52 | 53 | export default IndexPopup; 54 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | @tailwind variants; 5 | 6 | html, 7 | body { 8 | @apply bg-app-background; 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | html { 14 | min-height: 100%; 15 | display: flex; 16 | flex-direction: column; 17 | font-family: 'Ubuntu'; 18 | } 19 | 20 | body { 21 | min-height: 100%; 22 | display: flex; 23 | flex-direction: column; 24 | flex-grow: 1; 25 | } 26 | 27 | #__plasmo { 28 | min-height: 100%; 29 | display: flex; 30 | flex-direction: column; 31 | flex-grow: 1; 32 | } 33 | 34 | @font-face { 35 | font-family: 'Ubuntu'; 36 | src: url('data-base64:~assets/fonts/Ubuntu-Regular.ttf'); 37 | } 38 | 39 | 40 | /* Scroll Bar styles*/ 41 | 42 | 43 | 44 | ::-webkit-scrollbar { 45 | width: 10px; 46 | } 47 | 48 | ::-webkit-scrollbar-track { 49 | @apply bg-focus-color rounded-md 50 | } 51 | 52 | 53 | ::-webkit-scrollbar-thumb { 54 | @apply bg-action-color rounded-md 55 | } 56 | 57 | ::-webkit-scrollbar-thumb:hover { 58 | @apply bg-hover-action-color rounded-md 59 | } 60 | 61 | h1, 62 | h2, 63 | h3, 64 | h4, 65 | h5 { 66 | @apply text-header-color 67 | } 68 | 69 | li { 70 | @apply text-header-color text-base 71 | } 72 | 73 | a { 74 | @apply text-hover-action-color 75 | } 76 | 77 | 78 | /* Navigation Menu animation*/ 79 | 80 | @keyframes show-nav-menu { 81 | from { 82 | right: 105vw; 83 | } 84 | 85 | to { 86 | right: 30vw; 87 | } 88 | } 89 | 90 | .shown-nav-menu { 91 | display: flex; 92 | animation-fill-mode: forwards; 93 | animation-name: show-nav-menu; 94 | animation-duration: 0.75s; 95 | } 96 | 97 | 98 | 99 | @keyframes hide-nav-menu { 100 | from { 101 | right: 30vw; 102 | @apply flex; 103 | } 104 | 105 | to { 106 | right: 105vw; 107 | } 108 | } 109 | 110 | .hidden-nav-menu { 111 | animation-fill-mode: forwards; 112 | animation-name: hide-nav-menu; 113 | animation-duration: 0.75s; 114 | } 115 | 116 | #nav-menu-container-modal { 117 | @apply w-[100vw] h-[100vh] bg-modal-bg-container-color fixed right-0 top-0 left-0 bottom-0; 118 | @apply md:hidden z-[9]; 119 | } -------------------------------------------------------------------------------- /src/tabs/browse-links.tsx: -------------------------------------------------------------------------------- 1 | import "./../style.css"; 2 | import LinkCard from "~components/LinkCard"; 3 | import SelectedLinkContext from "~context/selectedLink"; 4 | import { useState, useEffect } from "react"; 5 | import type { Link, LinkCategory } from "~data/links"; 6 | import FavoriteLinksContext from "~context/favoriteLinks"; 7 | import { addNewFavoriteLink, getFavoriteLinks, removeFavoriteLink } from "~features/favoriteLinks"; 8 | import WelcomeInfo from "~components/WelcomeInfo"; 9 | import NavigationMenu from "~components/NavigationMenu"; 10 | import MainContainer from "~components/MainContainer"; 11 | import SelectedLinkCategoryContext from "~context/selectedCategory"; 12 | import CategoryCard from "~components/CategoryCard"; 13 | 14 | 15 | const BrowseLinks = () => { 16 | 17 | const [selectedLinkCategory, setSelectedLinkCategory] = useState(); 18 | const [selectedLink, setSelectedLink] = useState(); 19 | const [favoriteLinks, setFavoriteLinks] = useState | undefined>(); 20 | 21 | useEffect(() => { 22 | getFavoriteLinks().then((links) => setFavoriteLinks(links)); 23 | }); 24 | 25 | const addFavoriteLink = async (aLink: Link) => { 26 | await addNewFavoriteLink(aLink); 27 | const favoriteLinks = await getFavoriteLinks(); 28 | setFavoriteLinks(favoriteLinks); 29 | }; 30 | 31 | const removeLinkFromFavorites = async (aLink: Link) => { 32 | await removeFavoriteLink(aLink); 33 | const favoriteLinks = await getFavoriteLinks(); 34 | setFavoriteLinks(favoriteLinks); 35 | }; 36 | 37 | 38 | 39 | return ( 40 | 46 | 47 | setSelectedLinkCategory(category) 50 | }}> 51 | 52 | setSelectedLink(aSelectedLink), 55 | }}> 56 | 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | {!selectedLink && !selectedLinkCategory && ( 65 | 66 | 67 | )} 68 | 69 | {selectedLinkCategory && ( 70 | 71 | )} 72 | 73 | {selectedLink && ( 74 | 75 | )} 76 | 77 | 78 | 79 |
80 |
81 |
82 | 83 |
84 | ); 85 | }; 86 | 87 | export default BrowseLinks; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | mode: "jit", 4 | darkMode: "class", 5 | content: ["./**/*.tsx"], 6 | plugins: [], 7 | theme: { 8 | extend: { 9 | colors: { 10 | 'header-color': '#652F9E', 11 | 'app-background': '#F1ECEC', 12 | 'action-color': '#8C63CB', 13 | 'hover-action-color': '#AE8AE3', 14 | 'focus-color': '#EDE3FA', 15 | 'text-color': '#3C125A', 16 | 'divider-color': '#B1B2B5', 17 | 'modal-bg-container-color': 'rgb(206, 197, 212, 0.5)', 18 | }, 19 | }, 20 | }, 21 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "plasmo/templates/tsconfig.base", 3 | "exclude": [ 4 | "node_modules" 5 | ], 6 | "include": [ 7 | ".plasmo/index.d.ts", 8 | "./**/*.ts", 9 | "./**/*.tsx" 10 | ], 11 | "compilerOptions": { 12 | "paths": { 13 | "~*": [ 14 | "./src/*" 15 | ] 16 | }, 17 | "baseUrl": "." 18 | } 19 | } --------------------------------------------------------------------------------