├── CSS └── README.md ├── JavaScript ├── README.md └── libraries │ └── README.md ├── README.md ├── React ├── README.md ├── components │ ├── ClientOnly │ │ ├── ClientOnly.tsx │ │ └── README.md │ └── README.md ├── hooks │ ├── README.md │ ├── useEffectDebugger │ │ ├── README.md │ │ └── useEffectDebugger.tsx │ ├── useIsSsr │ │ ├── README.md │ │ └── isSsrContext.tsx │ ├── usePreloader │ │ ├── README.md │ │ └── usePreloader.tsx │ └── useReadFileAsDataUrl │ │ ├── README.md │ │ └── useReadFileAsDataURL.ts └── libraries │ └── README.md └── TypeScript ├── README.md ├── functions ├── README.md ├── declOfNum │ └── declOfNum.ts └── fixedWithoutRounding │ └── fixedWithoutRound.ts └── typing ├── README.md ├── process-env └── README.md └── svg └── README.md /CSS/README.md: -------------------------------------------------------------------------------- 1 | # База знаний CSS 2 | 3 | Здесь вы можете делиться знаниями, связанными с CSS. 4 | 5 | - [Кнопка с градиентной закруглённой границей и прозрачным фоном](https://codepen.io/fanmanutd/pen/dyjxzOB) 6 | - [Переход и смена блока между разными частями страницы.](https://codepen.io/fanmanutd/pen/LYXPJEL) 7 | ![1](https://github.com/fullstack-development/front-end-knowledge-base/assets/90761929/504b5eba-1de7-4799-946d-3891e63f49e7) 8 | Основано на [этой статье](https://css-tricks.com/going-from-solid-to-knockout-text-on-scroll/), как пример ипользования: 9 | ![2](https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/01/background-clip-final-1.gif?resize=1000%2C569&ssl=1) -------------------------------------------------------------------------------- /JavaScript/README.md: -------------------------------------------------------------------------------- 1 | # База знаний JavaScript 2 | 3 | Здесь вы можете делиться знаниями, связанными с JavaScript. Библиотеки, кейсы и пр. 4 | 5 | ## Разделы 6 | 7 | 1. [Библиотеки](./libraries/) -------------------------------------------------------------------------------- /JavaScript/libraries/README.md: -------------------------------------------------------------------------------- 1 | # Библиотеки JavaScript 2 | 3 | ## Содержание 4 | 5 | 1. [Работа с датой](#1) 6 | 7 | 8 | 9 | ## Работа с датой 10 | 11 | - [dayjs](https://day.js.org/) - мощная и лёгкая библиотека для работы с датами, полностью совместима с TypeScript. Работа с разными форматами, парсинг, форматирование, i18n, огромное количество утилит. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # База знаний Frontend 2 | 3 | Это база знаний фронтенд разработки, основанная на опыте наших разработчиков. 4 | 5 | Главная цель этого репозитория - обмен знаниями. Вы можете поделиться вашей любимой библиотекой или рассказать о сложном кейсе, который вам пришлось решить на одном из проектов. Рассказать о подходах и хитростях, которые вы применили или помогли вам в разработке. 6 | 7 | ## Разделы 8 | 9 | 1. [React](./React/) 10 | 1. [TypeScript](./TypeScript/) 11 | 1. [JavaScript](./JavaScript/) 12 | 1. [CSS](./CSS/) 13 | 14 | ## Цель 15 | 16 | Почему мы должны обмениваться знаниями? 17 | 18 | 1. Обмен опытом - один из самых эффективных способов получать новые знания. 19 | 2. Описание решения проблемы помогает структурировать свои мысли. Этот процесс может натолкнуть на новые выводы и глубже разобраться в проблеме. 20 | 3. Это позволяет посмотреть на решение проблемы под разными углами. Возможно вы уже решили какую-то из описанных здесь проблем на своём проекте, но сделали это по другому. Теперь вы знаете, что решений может быть несколько и возможно стоит выбрать, какое из них лучше. Предложите своё, если оно вам кажется лучше или пользуйтесь в будущем тем, что описано здесь. 21 | 4. Это повышает общий уровень экспертизы у всех разработчиков. 22 | 5. Это сокращает временные затраты на поиск решения той или иной проблемы, по скольку эта проблема уже была решена и описана кем-то другим. 23 | 24 | 6. Это уменьшает количество ситуаций, когда мы наталкиваемся на классную библиотеку/решение и жалеем, что не узнали о ней раньше. 25 | 26 | Стоит отметить, что в нашей команде мы пишем фронтенд на React + TypeScript. Поэтому большая часть знаний в этом репозитории будут вокруг этого стека. 27 | 28 | ## Contributing 29 | 30 | Мы рады любым предложениям и улучшениям в нашей базе знаний. Вы можете добавлять новые разделы по своему усмотрению, главное придерживаться понятной и логичной структуры. Если вам есть чем поделиться, создайте issue для обсуждения или pull request, если уверены в своём предложении. Так же можно создавать pull request для исправления опечаток или орфографических ошибок. -------------------------------------------------------------------------------- /React/README.md: -------------------------------------------------------------------------------- 1 | # База знаний React 2 | 3 | Здесь вы можете делиться знаниями, связанными с React. Ваши любимые библиотеки, полезные хуки и утилиты. Подходы, которые помогли решить какую-то проблему, не стандартные use cases. 4 | 5 | ## Разделы 6 | 7 | 1. [Библиотеки](./libraries/) 8 | 2. [Хуки](./hooks/) 9 | 3. [Компоненты](./components/) -------------------------------------------------------------------------------- /React/components/ClientOnly/ClientOnly.tsx: -------------------------------------------------------------------------------- 1 | import { FC, ReactNode } from 'react'; 2 | 3 | import { useIsSsr } from 'shared/context/useIsSsr'; 4 | 5 | export type Props = { 6 | children: ReactNode; 7 | fallback?: ReactNode; 8 | }; 9 | 10 | export const ClientOnly: FC = ({ children, fallback = null }) => { 11 | const isSsr = useIsSsr(); 12 | 13 | return <>{!isSsr ? children : fallback}; 14 | }; 15 | -------------------------------------------------------------------------------- /React/components/ClientOnly/README.md: -------------------------------------------------------------------------------- 1 | # ClientOnly 2 | 3 | ## Описание 4 | 5 | Компонент, предотвращающий ошибки гидратации, то есть разницу между серверным и первым клиентским рендером, путём рендера своего содержимого только на стороне клиента. Помимо этого можно явно задать содержимое серверного и первого клиентского рендера. 6 | 7 | **Важно**: не злоупотребляйте этим компонентом - он предназначен только для случаев, когда ошибок гидратации избежать невозможно. Старайтесь всегда решать проблемы гидратации без обходных путей. 8 | 9 | ## Использование 10 | 11 | Оберните содержимое, которое необходимо отрендерить только на клиенте, в компонент `ClientOnly`. Это содержимое отрендерится только на клиенте, после первого вызова `useEffect`. 12 | 13 | Вы можете передать проп `fallback`, который отрендерится на сервере и во время первого рендера на клиенте. Затем он заменится на содержимое `children`. Проп `fallback` полезен для предотвращения скачков в лэйауте и минимализации разницы между первым и вторым рендером на клиенте. 14 | 15 | По умолчанию равен `null`, следовательно на сервере ничего не отрендерится. 16 | 17 | ## Пример 18 | 19 | ![image](https://user-images.githubusercontent.com/90761929/224033635-67df6c9b-bdf2-4b0c-ab28-da0012262a14.png) 20 | 21 | Здесь переменная `address` не доступна на сервере, но определена на 1м клиентском рендере. Без `ClientOnly` текст кнопки и наличие `iconAfter` у компонента `Button` будут отличаться и возникнет ошибка гидратации. Мы передаём проп `fallback`, который отрендерит запасную кнопку на сервере и при первом рендере на клиенте, а затем будет рендерится содержимое `ClientOnly`. 22 | 23 | ## Зависимости 24 | 25 | `react`, хук [useIsSsr](../../hooks/useIsSsr/) 26 | 27 | ## Как это работает? 28 | 29 | Вы можете узнать, как работает хук [useIsSsr](../../hooks/useIsSsr/). 30 | -------------------------------------------------------------------------------- /React/components/README.md: -------------------------------------------------------------------------------- 1 | # React components 2 | 3 | Поделитесь полезными компонентами, которые вы написали для упрощения разработки или для решения какой-то проблемы. Предоставьте код компонента, подробно опишите для чего он нужен и как его использовать. Опишите зависимости, если они есть. 4 | 5 | - [ClientOnly](./ClientOnly/) - компонент, позволяющий избежать ошибок гидратации. -------------------------------------------------------------------------------- /React/hooks/README.md: -------------------------------------------------------------------------------- 1 | # React hooks 2 | 3 | Поделитесь полезными хуками, которые вы написали для упрощения разработки или для решения какой-то проблемы. Предоставьте код хука, подробно опишите для чего он нужен и как его использовать. Опишите зависимости, если они есть. 4 | 5 | - [useIsSsr](./useIsSsr/) контекст - позволяет определить, на чьей стороне происходит рендер - на сервере или клиенте, а также предотвратить ошибки гидратации. Для проектов, использующих SSR. 6 | 7 | - [useEffectDebugger](./useEffectDebugger/) - позволяет узнать, почему срабатывает ваш эффект. 8 | 9 | - [usePreloader](./usePreloader/) - управление статусом ваших асинхронных действий. 10 | 11 | - [useReadFileAsDataUrl](./useReadFileAsDataUrl/) - небольшой хук принимающий `Blob` или `File` и возвращающий строку в формате `base64`. Если это изображение, строку можно передать атрибуту `src` тега `img`. -------------------------------------------------------------------------------- /React/hooks/useEffectDebugger/README.md: -------------------------------------------------------------------------------- 1 | # useEffectDebugger 2 | 3 | ## Описание 4 | 5 | Иногда ваш эффект может работать неожиданным образом, а из-за большого количества зависимостей сложно разобраться почему так происходит. В таком случае можно воспользоваться этим хуком, что бы посмотреть какая из зависимостей вызывает срабатывание эффекта. 6 | 7 | ## Использование 8 | 9 | Просто замените ваш `useEffect` на `useEffectDebugger`, больше ничего не нужно. Теперь при каждом срабатывании эффекта в консоль будут выводиться старые и новые значения переменных из массива зависимостей. 10 | 11 | ## Пример 12 | 13 | Before: 14 | ```tsx 15 | useEffect(() => { 16 | // useEffect code here... 17 | }, [dep1, dep2]) 18 | ``` 19 | 20 | After: 21 | ```tsx 22 | useEffectDebugger(() => { 23 | // useEffect code here... 24 | }, [dep1, dep2]) 25 | ``` 26 | Console output: 27 | ```js 28 | { 29 | 1: { 30 | before: 'foo', 31 | after: 'bar' 32 | } 33 | } 34 | ``` 35 | Ключ объекта `1` означает индекс переменной из массива зависимостей, которая изменилась. В данном примере изменилось значение `dep2`. 36 | 37 | Вы можете передать необязательный 3й параметр, что бы дать имена ключам объекта: 38 | 39 | Before: 40 | ```tsx 41 | useEffect(() => { 42 | // useEffect code here... 43 | }, [dep1, dep2]) 44 | ``` 45 | After: 46 | ```tsx 47 | useEffectDebugger(() => { 48 | // useEffect code here... 49 | }, [dep1, dep2], ['dep1', 'dep2']) 50 | ``` 51 | Console output: 52 | ```js 53 | { 54 | dep2: { 55 | before: 'foo', 56 | after: 'bar' 57 | } 58 | } 59 | ``` 60 | 61 | ## Зависимости 62 | 63 | `react`, хук `usePrevious` 64 | -------------------------------------------------------------------------------- /React/hooks/useEffectDebugger/useEffectDebugger.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, DependencyList, EffectCallback } from 'react'; 2 | 3 | const usePrevious = (value: T, initialValue: T) => { 4 | const ref = useRef(initialValue); 5 | useEffect(() => { 6 | ref.current = value; 7 | }); 8 | return ref.current; 9 | }; 10 | 11 | const useEffectDebugger = (effectHook: EffectCallback, dependencies: DependencyList, dependencyNames = []) => { 12 | const previousDeps = usePrevious(dependencies, []); 13 | 14 | const changedDeps = dependencies.reduce((accum: any, dependency: any, index: number) => { 15 | if (dependency !== previousDeps[index]) { 16 | const keyName = dependencyNames[index] || index; 17 | return { 18 | ...accum, 19 | [keyName]: { 20 | before: previousDeps[index], 21 | after: dependency, 22 | }, 23 | }; 24 | } 25 | 26 | return accum; 27 | }, {}); 28 | 29 | if (Object.keys(changedDeps).length) { 30 | console.log('[use-effect-debugger] ', changedDeps); 31 | } 32 | 33 | useEffect(effectHook, dependencies); 34 | }; 35 | 36 | export { useEffectDebugger, usePrevious }; 37 | -------------------------------------------------------------------------------- /React/hooks/useIsSsr/README.md: -------------------------------------------------------------------------------- 1 | # useIsSsr 2 | 3 | ## Описание 4 | 5 | Если в вашем проекте используется SSR, иногда может быть полезным знать, на чьей стороне происходит рендер - сервер (server-side) или клиент (client-side). Например для предотвращения ошибок гидратации. Данное решение предоставляет контекст `IsSsrProvider` и хук `useIsSsr`, который берёт булевое значение из контекста. 6 | 7 | Используется компонентом [ClientOnly](../../components/ClientOnly/). 8 | 9 | **Важно**: не злоупотребляйте этим хуком - он предназначен только для случаев, когда ошибок гидратации избежать невозможно. Старайтесь всегда решать проблемы гидратации без обходных путей. 10 | 11 | ## Использование 12 | 13 | 1. Оберните ваше приложение в компонент `IsSsrProvider`. Обычно это делается в `App.tsx` файле. 14 | 2. Вызовите хук `useIsSsr` в нужном компоненте. Хук вернёт булевое значение, где `true` - server-side, `false` - client-side. 15 | 16 | 17 | ## Зависимости 18 | 19 | `react` 20 | 21 | ## Как это работает? 22 | 23 | В общем-то говоря, что бы узнать, на чьей стороне сейчас выполняется код, достаточно сделать проверку `typeof window === 'undefined'`. На сервере нет объекта `window`, следовательно если проверка вернёт `true` - значит мы на сервере. Тогда зачем нам использовать контекст и хук? Ответ - для предовтращения ошибок гидратации, то есть разницы между тем, что отрендерил сервер и **первым** клиентским рендером. 24 | 25 | Обратите внимание на хук `useCheckIsSsr`, в котором происходит две простые вещи - 1) объявляется состояние `isSsr` с начальным значением `true`; 2) Это состояние сбрасывается в `false` в `useEffect`. Хук `useEffect` выполняется **после** первого рендера и только на клиенте, следовательно значение состояния `isSsr` на сервере и во время **первого** рендера будет одинаковым - `true`. А значит, мы можем отрендерить одинаковое содержимое по значению `isSsr === true`. 26 | 27 | Примечание: хук `useCheckIsSsr` не предназначен для прямого использования в компонентах, поэтому не экспортируется наружу. При вызове в компоненте, его поведение не будет соответствовать поведению SSR. Server-side отрабатывает только при первой загрузке страницы, а затем при навигации по сайту происходит клиентский раутинг, то есть server-side не участвует. Предположим вы вызвали `useCheckIsSsr` на странице. Страница будет монтироваться/размонтироваться при навигации по сайту и состояние `useCheckIsSsr` будет сбрасываться, хотя server-side уже никак не участвует. -------------------------------------------------------------------------------- /React/hooks/useIsSsr/isSsrContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, ReactNode, useContext, useEffect, useState } from 'react'; 2 | 3 | function useCheckIsSsr() { 4 | const [isSsr, setIsSsr] = useState(true); 5 | 6 | useEffect(() => { 7 | setIsSsr(false); 8 | }, []); 9 | 10 | return isSsr; 11 | } 12 | 13 | const IsSsrContext = createContext(undefined); 14 | 15 | function IsSsrProvider({ children }: { children: ReactNode }) { 16 | const isSsr = useCheckIsSsr(); 17 | 18 | return {children}; 19 | } 20 | 21 | function useIsSsr(): boolean { 22 | const isSsr = useContext(IsSsrContext); 23 | 24 | if (typeof isSsr === 'undefined') 25 | throw new Error('useIsSsr should be called inside IsSsrContext'); 26 | 27 | return isSsr; 28 | } 29 | 30 | export { IsSsrProvider, useIsSsr }; 31 | -------------------------------------------------------------------------------- /React/hooks/usePreloader/README.md: -------------------------------------------------------------------------------- 1 | # usePreloader 2 | 3 | ## Описание 4 | 5 | Хук, позволяющий управлять статусом ваших асинхронных действий внутри компонента, будь-то асинхронные функции или асинхронные эффекты. 6 | 7 | ## Использование 8 | 9 | `usePreloader` возвращает массив из 3х значений: 10 | 11 | 1) `isPending` - булевое значение, указывающее на статус асинхронного действия. 12 | 2) `error` - содержит ошибку. По умолчанию `string`, можно типизировать через дженерик при вызове `usePreloader`. 13 | 3) `setLoadingStatus` - функция, устанавливающая оба предыдущих значения. Принимает объект со свойствами `isPending` и `error`. 14 | 15 | `usePreloader` принимает один необязательный дженерик и 2 аргумента. 16 | 17 | 1) `TError` - аргумент типа, в который можно передать тип ожидаемой ошибки. По умолчанию `string`. 18 | 2) `initialError` - начальное значение для `error`, тип зависит от `TError`. 19 | 3) `initialIsPending` - начальное значение для `isPending`, по умолчанию `false`. Полезно установить `true` если надо при 1м рендере показать какой-то прелоадер. 20 | 21 | ## Пример 22 | 23 | ![image](https://user-images.githubusercontent.com/90761929/226887419-a7c82f3d-498c-432f-b9fe-ebf4b80955bb.png) 24 | 25 | 26 | ## Зависимости 27 | 28 | `react`, хук `useIsMounted` 29 | 30 | ## Как это работает? 31 | 32 | Является небольшой обёрткой над обычным `useState`. Так как речь идёт об асинхронных действиях, хук `useIsMounted` используется для проверки, что бы не вызывать `setState` на размонтированном компоненте. -------------------------------------------------------------------------------- /React/hooks/usePreloader/usePreloader.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useState, useRef, useEffect } from 'react'; 2 | 3 | function useIsMounted(): React.MutableRefObject { 4 | const isMounted = useRef(false); 5 | useEffect(() => { 6 | isMounted.current = true; 7 | return () => { 8 | isMounted.current = false; 9 | }; 10 | }, []); 11 | return isMounted; 12 | } 13 | 14 | export type SetLoadingStatus = React.Dispatch< 15 | React.SetStateAction<{ 16 | isPending: boolean; 17 | error: TError; 18 | }> 19 | >; 20 | 21 | function usePreloader( 22 | initialError: TError, 23 | initialIsPending = false, 24 | ): [boolean, TError, SetLoadingStatus] { 25 | const isMounted = useIsMounted(); 26 | const [{ isPending, error }, setLoadingStatus] = useState<{ isPending: boolean; error: TError }>({ 27 | isPending: initialIsPending, 28 | error: initialError, 29 | }); 30 | const checkAndSetLoadingStatus: SetLoadingStatus = useCallback( 31 | (value) => { 32 | if (isMounted.current) setLoadingStatus(value); 33 | }, 34 | [isMounted], 35 | ); 36 | return [isPending, error, checkAndSetLoadingStatus]; 37 | } 38 | 39 | export { usePreloader, useIsMounted }; 40 | -------------------------------------------------------------------------------- /React/hooks/useReadFileAsDataUrl/README.md: -------------------------------------------------------------------------------- 1 | # useReadFileAsDataURL 2 | 3 | ## Описание 4 | 5 | Небольшой хук принимающий `Blob` или `File` и возвращающий строку в формате `base64`. Если это изображение, строку можно передать атрибуту `src` тега `img`. 6 | 7 | ## Использование 8 | 9 | Передайте `File` или `Blob`, хук вернёт строку в формате `base64`. 10 | 11 | ## Пример 12 | 13 | ![image](https://github.com/fullstack-development/front-end-best-practices/assets/90761929/5207fff0-4e6e-44f0-839b-e9396cdb1db7) 14 | 15 | 16 | ## Зависимости 17 | 18 | `react` 19 | -------------------------------------------------------------------------------- /React/hooks/useReadFileAsDataUrl/useReadFileAsDataURL.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export const useReadFileAsDataURL = (file?: File | Blob) => { 4 | const [dataURL, setDataURL] = useState(); 5 | 6 | useEffect(() => { 7 | const fileReader = new FileReader(); 8 | 9 | fileReader.onload = () => { 10 | const { result } = fileReader; 11 | 12 | if (typeof result === 'string') setDataURL(result); 13 | }; 14 | 15 | if (file) fileReader.readAsDataURL(file); 16 | else setDataURL(undefined); 17 | 18 | return () => { 19 | fileReader.onload = null; 20 | }; 21 | }, [file]); 22 | 23 | return dataURL; 24 | }; 25 | -------------------------------------------------------------------------------- /React/libraries/README.md: -------------------------------------------------------------------------------- 1 | # React libraries 2 | 3 | Поделитесь вашими любимыми библиотеками или теми, с помощью которых удалось решить необычную проблему. Следует указать название библиотеки и краткое её описание и/или проблему, которую она помогла решить. 4 | 5 | ## Содержание 6 | 7 | 1. [UI-библиотеки](#1) 8 | 1. [UI-компоненты](#2) 9 | - [Скелетон](#2.1) 10 | - [Datepickers](#2.2) 11 | - [Инпут для номера телефона](#2.3) 12 | - [Range-slider](#2.4) 13 | - [Всплывающие подсказки(Tooltip)](#2.5) 14 | - [Компонент обратного отсчёта (Countdown)](#2.6) 15 | - [Всплывающие уведомления](#2.7) 16 | - [Зона перетаскивания файлов (Dropzone)](#2.8) 17 | 1. [Логические компоненты](#3) 18 | - [Хук useDraggable (перетаскивание внутри контейнера)](#3.1) 19 | - [Бесконечный скролл](#3.2) 20 | - [Анимация высоты для height: auto](#3.3) 21 | 1. [Web3](#4) 22 | 1. [Мультиязычность](#5) 23 | 1. [Wysiwyg](#6) 24 | 25 | 26 | 27 | ## UI-библиотеки 28 | 29 | - [Material UI](https://mui.com/material-ui/getting-started/overview/) - самая популярная UI библиотека для React. Большое количество компонентов, кастомизация покрывающая большую часть кейсов. Интеграция с популярными CSS-решениями (CSS modules, css-in-js, tailwind, др.). Под капотом решены большое количество пограничных кейсов, о которых вы даже могли не знать. Например дизейбл скролла при появлении модалки и добавления паддинга на место, где находится скролл, чтобы избежать скачков ширины контента. 30 | 31 | Недостатки: громоздкость, большое API, в котором порой тяжело найти то, что нужно. Навороченное стилевое оформление из коробки, которое приходится переопределять при наличии дизайна на проекте (хотя можно использовать безстилевые варианты компонент). 32 | 33 | - [@headlessui/react](https://headlessui.com/) - набор базовых ui-компонентов. Легко кастомизируется за счет композабельности, минималистичного API и отсутствия лишней стилизации. Так же из коробки имеет все преимущества для a11y. Есть своя реализация транзишна перед дестроем компонента ([Transition-компонент](https://headlessui.com/react/transition)). Минусы - небольшое количество компонентов. 34 | 35 | 36 | 37 | ## UI-компоненты 38 | 39 | 40 | 41 | ### Скелетон 42 | - [react-content-loader](https://github.com/danilowoz/react-content-loader) - красивый запасной интерфейс, пока загружаются данные. Простая библиотека построенная на SVG. 43 | 44 | 45 | 46 | ### Datepickers 47 | 48 | - [@rehokify/datepicker](https://github.com/rehookify/datepicker) - набор декларативных хуков для создания дейтпикеров для тех, кому надоело переопределять дефолтные стили и разметку у компонентов, предоставляемых ui-kit'ми. Из минусов - библиотека относительно молодая, маленькое комьюнити. Из-за этого может быть много неотловленных эдж-кейсов. 49 | 50 | - [react-date-picker](https://github.com/wojtekmaj/react-date-picker) - обычный календарь. Плюсы: интегрирован с React, есть всё необходимое API. Минусы: откровенно так себе начальный внешний вид, который придётся переопределять. 51 | 52 | 53 | 54 | ### Инпут для номера телефона 55 | 56 | - [react-phone-number-input](https://gitlab.com/catamphetamine/react-phone-number-input) - прекрасная библиотека с кастомизацией, автоформатированием, валидацией, визуальным отображением и автоопределением страны. Так же есть полезные утилиты для валидации. 57 | 58 | 59 | 60 | 61 | ### Range-slider 62 | 63 | - [noUiSlider](https://refreshless.com/nouislider/) - библиотека для range-slider на чистом JS и без зависимостей. Из плюсов можно отметить полноту документации и большое API, которое может покрыть специфические кейсы. Пример использования с React [здесь](https://codesandbox.io/s/nouislider-34qmz4). 64 | 65 | P.S. В целом как буд-то выбор не велик, если ищите готовое решение для range-slider. Практически все библиотеки либо мертворождённые, либо сильно недоработанные и со слабым API. Нормальных реакт библиотек я вообще не встретил. А ещё многие библиотеки написаны на jQuery, который точно не хочется тянуть. Если знаете альтернативу, ждём предложений. 66 | 67 | 68 | 69 | ### Всплывающие подсказки (Tooltip) 70 | 71 | - [react-tooltip](https://react-tooltip.com/) - реакт-библиотека для всплывающих подсказок при наведении. 72 | 73 | 74 | 75 | 76 | ### Компонент обратного отсчёта (Countdown) 77 | 78 | - [react-countdown](https://react-tooltip.com/) - безстилевой реакт-компонент для отображения обратного отсчёта. [Пример.](https://codesandbox.io/s/react-countdown-34qlgl). Внешний вид настраивается прокидыванием собственного JSX-кода. 79 | 80 | 81 | 82 | ### Всплывающие уведомления 83 | 84 | - [react-notifications-component](https://github.com/teodosii/react-notifications-component) - простая библиотека для всплывающих уведомлений с хорошей кастомизацией. 85 | - [react-toastify](https://github.com/fkhadra/react-toastify) - библиотека с расширенным функционалом и более красивым внешним видом из коробки. 86 | 87 | 88 | 89 | ### Зона перетаскивания файлов (Dropzone) 90 | 91 | - [react-dropzone](https://github.com/react-dropzone/react-dropzone/) - библиотека для перетаскивания файлов в определённую область с очень простым API. 92 | 93 | 94 | 95 | ## Логические компоненты 96 | 97 | 98 | 99 | ### Хук useDraggable (перетаскивание внутри контейнера) 100 | 101 | - [react-use-draggable-scroll](https://github.com/rfmiotto/react-use-draggable-scroll/) - Маленький хук, активирующий перетаскивание внутри контейнера. Горизонтальное, вертикальное или в обоих направлениях сразу. Работает на десктопах и мобилках. Реализован через прослушивание событий. Есть опция, позволяющая управлять количеством пикселей, через которое движение мышью будет считаться перетаскиванием, а не кликом. Демо можно посмотреть [здесь](https://stackblitz.com/edit/nextjs-tg52v4?file=README.md). 102 | 103 | 104 | 105 | ### Бесконечный скролл 106 | 107 | - [react-infinite-scroll-component](https://github.com/ankeetmaini/react-infinite-scroll-component) - небольшая библиотека для удобной реализации бесконечного скролла. Пока каких-то проблем не было, но к сожалению уже давно не поддерживается и имеются накопленные ишьюсы. Если вы знаете альтернативу - поделитесь. 108 | 109 | 110 | 111 | ### Анимация высоты для height-auto 112 | 113 | - [react-animate-height](https://github.com/Stanko/react-animate-height) - реакт-компонент, позволяющий анимировать высоту элемента для значения `height: auto` (и любых других). Демо [здесь](https://muffinman.io/react-animate-height/). 114 | 115 | Немного контекста: к сожалению нет нативного способа анимировать `height: auto`, но есть 3 "хака" - 2 из которых выглядят костыльно, а 3й работает как надо, но нужен JS-скрипт. Собственно в этом компоненте как раз написан такой скрипт. Подробнее о проблеме и 3х вариантов её решения можно почитать [здесь](https://css-tricks.com/using-css-transitions-auto-dimensions/). 116 | 117 | 118 | 119 | ## Web3 120 | 121 | - [wagmi](https://github.com/wagmi-dev/wagmi) - библиотека для удобной работы с web3 на React. Большое количество разнообразных хуков для взаимодействия с контрактами, наличие самых популярных коннекторов (MetaMask, Coinbase, WalletConnect), поддержка мультичейн и многое другое. Есть возможность использовать императивный API, без хуков. Недостатки: неполнота документации, отсутствие 100% надёжности. Периодически можно натолкнуться на баги или расхождения с документацией. 122 | - [useDApp](https://github.com/TrueFiEng/useDApp) - во многом похожа на wagmi, в том числе и недостатками. Немного отличается API. Имеет большее количество коннекторов, несколько полезных хелперов и удобный хук для подтягивания данных из агрегатора CoinGecko. 123 | 124 | 125 | 126 | ## Мультиязычность 127 | 128 | - [react-i18next](https://github.com/i18next/react-i18next) - библиотека для добавления мультиязычности в ваше React приложение. 129 | - [next-i18next](https://github.com/i18next/next-i18next) - обёртка над `react-i18next` для интеграции с [NextJS](https://nextjs.org/). 130 | 131 | 132 | 133 | ## Wysiwyg 134 | 135 | - [remirror](https://remirror.io/) - библиотека для конструирования wysiwyg редакторов любой разновидности и сложности. Имеет очень гибкое API с возможностью добавления расширений (есть как builtin-решения, так и возможность сделать свой кастомный вариант). 136 | -------------------------------------------------------------------------------- /TypeScript/README.md: -------------------------------------------------------------------------------- 1 | # База знаний TypeScript 2 | 3 | Здесь вы можете делиться знаниями, связанными с TypeScript. Утилиты, функции, библиотеки, проблемы типизации и др. 4 | 5 | ## Разделы 6 | 7 | 1. [Типизация](./typing/) 8 | 1. [Функции](./functions/) -------------------------------------------------------------------------------- /TypeScript/functions/README.md: -------------------------------------------------------------------------------- 1 | # Функции Typescript 2 | 3 | - [Функция выбора правильного окончания слова в зависимости от числа](./process-env/). 4 | 5 | - [Функция обрезания дробной части числа без округления](./svg/). -------------------------------------------------------------------------------- /TypeScript/functions/declOfNum/declOfNum.ts: -------------------------------------------------------------------------------- 1 | export const declOfNum = (number: number, titles: [string, string, string]): string => { 2 | const numOfEnding = [2, 0, 1, 1, 1, 2]; 3 | 4 | const isLastEnding = number % 100 > 4 && number % 100 < 20; 5 | const correctEnding = number % 10 < 5 ? number % 10 : 5; 6 | 7 | return titles[isLastEnding ? 2 : numOfEnding[correctEnding]]; 8 | }; 9 | 10 | declOfNum(1, ['комната', 'комнаты', 'комнат']); // комната 11 | declOfNum(2, ['комната', 'комнаты', 'комнат']); // комнаты 12 | declOfNum(5, ['комната', 'комнаты', 'комнат']); // комнат 13 | 14 | declOfNum(1, ['room', 'rooms', 'rooms']); // room 15 | declOfNum(2, ['room', 'rooms', 'rooms']); // rooms 16 | declOfNum(5, ['room', 'rooms', 'rooms']); // rooms 17 | -------------------------------------------------------------------------------- /TypeScript/functions/fixedWithoutRounding/fixedWithoutRound.ts: -------------------------------------------------------------------------------- 1 | export function fixedWithoutRound(num: number, fractionDigits: number): number { 2 | return Math.trunc(num * 10 ** fractionDigits) / 10 ** fractionDigits; 3 | } 4 | 5 | 10.126.toFixed(2); // 10.13 - метод toFixed округляет. 6 | fixedWithoutRound(10.126, 2); // 10.12 - метод fixedWithoutRound не округляет. 7 | -------------------------------------------------------------------------------- /TypeScript/typing/README.md: -------------------------------------------------------------------------------- 1 | # Проблемы типизации Typescript 2 | 3 | - [Типизация переменных окружения (process.env)](./process-env/). 4 | 5 | - [Типизация SVG компонентов](./svg/). -------------------------------------------------------------------------------- /TypeScript/typing/process-env/README.md: -------------------------------------------------------------------------------- 1 | # Типизация переменных окружения 2 | 3 | Для типизации переменных окружения необходимо: 4 | 5 | 1) Иметь установленный `@types/node` пакет в вашем проекте 6 | 2) Создать в корне проекта файл `environment.d.ts` (название не имеет значения) со следующим содержимым: 7 | ``` 8 | declare global { 9 | namespace NodeJS { 10 | interface ProcessEnv { 11 | GITHUB_AUTH_TOKEN: string; 12 | NODE_ENV: 'development' | 'production'; 13 | NEXT_APP_MY_ENV: string; 14 | PORT?: string; 15 | } 16 | } 17 | } 18 | 19 | // If this file has no import/export statements (i.e. is a script) 20 | // convert it into a module by adding an empty export statement. 21 | export {} 22 | ``` -------------------------------------------------------------------------------- /TypeScript/typing/svg/README.md: -------------------------------------------------------------------------------- 1 | # Типизация SVG компонентов при использовании SVGR 2 | 3 | Во многих фреймворках встроен SVGR, который позволяет подключать svg-файлы как реакт-компоненты. Проблема в том, что у такого компонента напрочь отсутствует типизация, ему можно передать любые пропсы. Решить эту проблему можно следующим образом: 4 | 1. создайте файл `svg.d.ts.` (название не имеет значения) со следующим содержимым: 5 | ```ts 6 | declare module '*.svg' { 7 | import * as React from 'react'; 8 | 9 | export const ReactComponent: React.FunctionComponent< 10 | React.SVGProps & { title?: string } 11 | >; 12 | 13 | const src: string; 14 | export default src; 15 | } 16 | ``` 17 | 2. Добавьте путь к файлу в массив `include` в вашем `tsconfig.json` (в примере файл находится в корне проекта): 18 | ![image](https://user-images.githubusercontent.com/90761929/222449468-db502f0b-1997-445a-91b9-e9ee8e96d8ee.png) 19 | 3. Далее при импорте svg - файла вам надо будет писать: 20 | ```ts 21 | import { ReactComponent as DisableIcon } from './icons/disable.svg'; 22 | import { ReactComponent as EnableIcon } from './icons/enable.svg'; 23 | ``` 24 | Но эту запись можно улучшить. Создайте файл `index.ts` в папке `icons` и сделайте там реэкспорт ваших svg-файлов: 25 | ```ts 26 | export { ReactComponent as DisableIcon } from './icons/disable.svg'; 27 | export { ReactComponent as EnableIcon } from './icons/enable.svg'; 28 | ``` 29 | Теперь у вас будет красивый импорт и типизированные svg: 30 | ```ts 31 | import { DisableIcon, EnableIcon } from './icons'; 32 | ``` --------------------------------------------------------------------------------