├── 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 | 
8 | Основано на [этой статье](https://css-tricks.com/going-from-solid-to-knockout-text-on-scroll/), как пример ипользования:
9 | 
--------------------------------------------------------------------------------
/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 | 
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 | 
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 | 
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 | 
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 | ```
--------------------------------------------------------------------------------