👩‍💻 219 файлов с кодом для drag-n-drop

#frontend

Проблема, с которой мы недавно столкнулись — это redux в зависимостях у react-beautiful-dnd. Есть какое-то внутреннее сопротивление тому факту, что кто-то втягивает целый стейт-менеджер в библиотеку, которая должна давать возможность перетащить карточку из одной колонки в другую. Разумеется, я несколько утрирую ситуацию, но в данном случае решение кажется необоснованным.

В моём понимании, библиотека для drag-n-drop, в целом, не нуждается в том, чтобы иметь свой собственный стетйт-менеджер, возможностей локального стейта компоненты для такого решения более, чем достаточно.

В случае, если стор всё-таки требуется, я предлагаю следующее решение: "стор должен поступать внешней зависимостью, согласно типизированного библиотекой интерфейса". То есть, сделать возможность передать стор внутрь библиотеки, создав кастомную обёртку, подходящую для конкретной библиотеки. Понимаю, что от этого сложность может возрасти, но почти наверняка такой подход позволит уменьшить итоговый бандл, хотя и увеличит время и сложность разработки. С другой стороны, это решение разработанное инженерами Atlassian, вряд ли они недостаточно компетентны, хотя по работе в Jira этого иногда и не скажешь.

Возможно, если у меня дойдут руки, то мы соберём отдельное собственное решение для drag-n-drop. В случае, если это произойдёт — я обязательно поделюсь ссылкой на решение.

В целом, ситуация, является лишним доказательством слов про то, что решение, разработанное большой компанией призвано решать проблемы этой самой большой компании, а не ваши.
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Оптимизация запросов в Django ORM

#python #django #backend

Сегодня хочу рассказать про методы select_related и prefetch_related: показать разницу между ними, привести примеры использования. Полная версия с примерами доступна у меня в блоге.

В чём разница?

Если говорить совсем кратко, то: select_related — это JOIN, то есть происходит 1 запрос, в рамках которого связываются указанные сущности и приходят в python уже связанными, в то время, как prefetch_related — делает отдельные запросы по каждой указанной связанной сущности и производит связывание на уровне python.

В документации django сказано, что select_related стоит применять в тех случаях, когда вы используете ForeignKey, если вы используете ManyToManyField, ваш выбор — это prefetch_related.

Подробности и подводные камни

Предположим, что наши модели выглядят следующим образом:

from django.db import models

class Genre(models.Model):
name = models.CharField(max_length=512)

class City(models.Model):
...

class Person(models.Model):
hometown = models.ForeignKey(
City,
)

class Book(models.Model):
name = models.CharField(max_length=512)

author = models.ForeignKey(Person, on_delete=models.CASCADE)

genres = models.ManyToManyField(Genre)


Инвалидация кэша

⚠️ При использовании prefetch_related, важно помнить о такой вещи, как инвалидация кэша на уровне Queryset (инвалидация может быть вызвана любым дополнительным методом в цепочке, который подразумевает другой запрос к базе данных). Например, при использовании фильтрации в следующем примере, будут выполнены отдельные запросы для каждого объекта Book:

for book in Book.objects.prefetch_related("genres"):
print(book.name, ":")
for genre in Book.genres.filter(age_rating="18+"):
print(" ", genre.name)


Объект Prefetch

Также, стоит упомянуть и о существовании объекта Prefetch, который позволяет использовать подготовленный Queryset вместе с prefetch_related. Например, мы хотим отобрать заранее только те жанры, которые имеют возрастной рейтинг "18+" и посчитать количество книг по каждому такому жанру.

Код для решения этой задачи выглядит следующим образом:

genres = Genre.objects.filter(age_rating="18+")
.annotate(books_count=Count("book_set"))

queryset = Book.objects.all().prefetch_related(
Prefetch("genres", queryset=genres)
)


Полная версия с примерами доступна у меня в блоге.

📖 Источники:

1) Статья с хабра, в которой всё доходчиво объясняют: https://habr.com/ru/articles/752574/
2) Документация Django: https://docs.djangoproject.com/en/5.0/ref/models/querysets/
Please open Telegram to view this post
VIEW IN TELEGRAM
📆 Правильное форматирование дат в JS

#frontend

Буквально месяц назад писал свои мысли по поводу таких решений, как moment/day.js и ругал их за то, что они не используют нативные браузерные API.

И вот, моя мечта сбылась. На неделе я сидел и листал тематические каналы в поисках чего-то нового и интересного и наткнулся на пост про библиотеку tempo. В основе это решение использует браузерный объект Date и Intl API. При этом, API самой библиотеки достаточно простое и имеет довольно подробную документацию.
Please open Telegram to view this post
VIEW IN TELEGRAM
Как менялось моё рабочее место 2021-2024

Делитесь своими фотографиями в комментариях👇
👩‍💻 Вычисляем дату с учётом рабочих дней на python

#python #backend

Полная версия с разбором решения доступна у меня в блоге.

Сегодня на работе столкнулся с задачей, когда надо было посчитать дату оплаты счёта, с учётом праздников и выходных (чтобы выводилась дата по условию "не позднее, чем через 5 рабочих дней"). Ранее подобных вещей я не делал, поэтому пошёл гуглить.

Ищем готовое решение на python

На этапе ресёрча по задаче нашёл такое решение:

import datetime
import numpy as np

start = datetime.date(2022, 2, 15)
end = datetime.date(2022, 3, 16)

# include holidays in a list
days = np.busday_count(start, end, holidays=['2022-02-21'])
print('Number of business days is:', days)


На том же сайте было упоминание библиотеки holidays, чтобы проставить сразу корректный список всех праздников, относительно определённой страны. Принцип работы простой:

from datetime import date
import holidays

ru_holidays = holidays.RU()

date(2024, 3, 8) in ru_holidays # True
date(2024, 3, 7) in ru_holidays # False


К сожалению, нельзя сказать, что это решение покрывает все кейсы. К примеру, в 2024м году майские праздники в России по производственному календарю продлятся с 9 по 12 мая. Библиотека же, считает не так:

>>> date(2024, 5, 9) in ru_holidays
True
>>> date(2024, 5, 10) in ru_holidays
False


В этом примере, 9 мая ещё входит в список праздников, а вот 10 мая уже нет. Возможно, что numpy будет определять корректно?

>>> import datetime
>>> import numpy as np
>>> start = datetime.date(2024, 5, 6)
>>> end = datetime.date(2024, 5, 12)
>>> days = np.busday_count(start, end)
>>> days
5


Как видим, без параметра holidays, numpy считает, что вся неделя с 6 по 12 рабочая, кроме 2 выходных.

Ищем открытое API

Следующее, на что я наткнулся, было открытое API производственного календаря для России и Казахстана. Работа с ним максимально простая. К примеру, чтобы получить список всех "отклонений" от обычного календаря для России, достаточно выполнить GET-запрос по адресу: https://production-calendar.ru/get/ru/2024/json?compact=1. Результат полностью совпадает с производственным календарём на сайте КонсультантПлюс.

Мастерим решение

С учётом всех вводных реализуем решение (полная версия с его разбором у меня в блоге):

import datetime
import numpy as np

# глобальные константы откуда-то из settings/django constance
HOLIDAYS = ['2024-03-08', ...]
BUSINESS_DAYS_DELTA = 5

def found_business_days_date(step = 5, _start_date = None):
start_date = _start_date or datetime.datetime.now().date()
end_date = start_date + datetime.timedelta(days=step)

days = np.busday_count(start_date, end_date, holidays=HOLIDAYS)

if days == BUSINESS_DAYS_DELTA:
return end_date

return found_business_days_date(step + 1, start_date)


Результат работы этой функции для сегодняшней даты (04.03.2024): 2024-03-12. Соответственно, функция работает корректно.

Заключение

Решение подобных базовых задач очень позитивно складывается на опыте начинающих и продолжающих разработчиков.

Полная версия с разбором решения доступна у меня в блоге.

💬 Поделитесь, сталкивались ли вы с подобными задачами? Какое решение в итоге находили?
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Как мы docker с VPN дружили

#docker #backend

В прошлом году столкнулся с задачей по настройке VPN-подключения внутри docker для обеспечения доступа к API, находящемуся во внутренней сети предприятия. Недавно с подобной проблемой столкнулся и мой друг, но ситуации у нас несколько разные: я настраивал связку для openfortivpn, а ему нужно было настроить для openvpn.

Основная проблема, которая возникает при настройке VPN-соединения для нескольких контейнеров заключается в необходимости настройки сетевых параметров и особенностях работы docker. Без контейнеризации всё было бы намного проще: вы запускаете VPN-подключение и все ваши запросы к соответствующим хостам внутри VPN-сети проходят через это подключение.

Как работает сеть в docker и docker-compose

По умолчанию, все наши сервисы объединяются в рамках сети default. При необходимости мы можем выделять отдельные сети для разных сервисов, чтобы изолировать их друг от друга. Кроме прочего, мы можем использовать внешнюю docker-сеть, чтобы объединить сервисы из разных приложений (отделённых своими compose-файлами) между собой.

Нетрудно догадаться, что для обеспечения VPN-подключения внутри docker-сети, нам потребуется уметь чуть больше, чем просто создание docker-сети. Тут на помощь приходят сетевые драйверы и network_mode.

Сетевые драйверы

В официальной документации описано целых 7 различных сетевых драйверов. Начнём с того, что сам по себе сетевой драйвер нужен для расширения возможностей сети. Рассмотрим какие сетевые драйверы существуют (адаптированная информация из документации):

- bridge — драйвер по умолчанию, необходим для того, чтобы сервисы могли общаться друг с другом внутри docker-сети;
- host — драйвер, который позволяет использовать сеть хоста напрямую, убирая изоляцию контейнера от хоста;
- overlay — драйвер, позволяющий соединить несколько хостов, где находятся docker-контейнеры вместе, чтобы обеспечить взаимодействие между ними (такой вариант отлично подходит для обеспечения коммуникации между нодами в swarm mode);
- ipvlan — драйвер, предоставляющий полный контроль над адресацией IPv4 и IPv6;
- macvlan — драйвер, позволяющий назначить MAC-адрес для контейнера, делая его доступным в виде физического устройства внутри вашей сети;
- none — драйвер, позволяющий изолировать сеть контейнера от хостовой сети и сети других контейнеров;
- network plugins — возможность использования в качестве сетевого драйвера любого внешнего решения.

network_mode

network_mode это атрибут сервиса внутри конфигурации docker-compose, необходимый для более тонкой настройки. Существует несколько значений у атрибута newtork_mode:

- bridge — дефолтный способ для связи между контейнерами, работает по умолчанию;
- host — использование единого сетевого пространства между выбранным сервисом и хостом;
- none — отсутствие сетевого взаимодействия для выбранного сервиса;
- service:[service_name] — использование сетевого пространства выбранного сервиса;
- container:[container_name/id] — использование сетевого пространства выбранного контейнера (отличается от прошлого необходимость указания именно container_name/container_id, а не service_name).

К сожалению, в официальной документации compose, подробно ничего не сказано об этом атрибуте, кроме ссылки на то, что значения могут быть те же самые, что и у парамерта --network, но также добавляется специальная форма service:[service name]. Возможно, это сделано так именно потому, что все значения по смыслу похожи.

Пример compose-файла

Разницы для конфигурации между openfortivpn и openvpn особо нет, поэтому рассмотрим на примере openfortivpn:

version: '3'

services:
vpn:
container_name: openfortivpn
restart: unless-stopped
build: .
privileged: true
volumes:
- ./config:/etc/openfortivpn/config.conf
command: ["openfortivpn", "--config=/etc/openfortivpn/config.conf"]

some-service:
image: your-container-image
network_mode: service:vpn
Please open Telegram to view this post
VIEW IN TELEGRAM
Маска для номера телефона

#frontend #браузеры #стандартизация

Проблема стандартизации форматирования и валидации поля ввода номера телефона абсолютно не новая. Даже в MDN говорят о том, что <input type="tel"> в отличие от <input type="email"> не имеет никакой автоматической валидации, потому что "форматы телефонных номеров сильно раличаются по всему миру".

Решение, которое мы можем без труда соорудить стандартными средствами браузера, не используя дополнительных библиотек выглядит примерно так:

<div>
<input
type="tel"
pattern="(^8|7|\+7)([0-9]{10})"
required
>
</div>
<div>
<small>Формат: +79999999999</small>
</div>


С таким решением мы будем строго принимать один обозначенный формат (пример сильно упрощён, более комплексный вариант регулярного выражения можно подглядеть тут) с общей подсказкой для пользователя, без маски для ввода. Как давно вы в последний раз видели поле ввода для номера телефона без маски?

Можно долго дискутировать о том, нужна ли маска для ввода в данном случае, но пользователи уже к этому привыкли и вы явно их не отучите, только добавите неудобств.

Вернёмся к основной теме. Без добавления JS-кода маску мы не получим. Можно поискать готовое решение, но с этим тоже есть проблема. Решения, которые сразу будут хорошо работать из коробки найти сложно. Довольно часто, многие из этих решений уже содержат в себе стилизацию, которую не всегда легко переделать под нужный дизайн.

На мой взгляд, всё, что должно быть в хорошем решении подобной задачи:

1) возможность управления паттерном форматирования, валидации;
2) отсутствие лишних зависимостей и любой стилизации, она должна поступать извне, либо легко кастомизироваться;
3) интернационализация.

Почти идеальным решением, на мой взгляд, является imask. Он предоставляет только функциональную часть с очень простыми API. К примеру, для российского номера телефона код будет выглядеть так:

IMask(
document.getElementById('phone-mask'),
{
mask: '+{7}(000)000-00-00'
}
)


Кроме прочего, решение имеет адаптацию под популярные веб-фреймворки (react, vue, angular, svelte) и даже неплохой задел на интернационализацию за счёт использования динамических масок. Пожалуй, единственным минусом этого решения является необходимость написания большого количества кода для более тонкой настройки. С другой стороны, это даёт возможность широкой кастомизации и позволяет менять паттерны и маски так, как вам требуется, не отнимая возможности стилизовать свои поля ввода так, как вы считаете нужным.
👩‍💻 Вычисляемые свойства во Vue сломаны?

#frontend #vue #производительность

На reddit наткнулся на пост в комьюнити Vue.JS с описанием проблемы с производительностью у вычисляемых свойств (computed). Заключается проблема в том, что судя по тестам использованием watch + ref на 50% более эффективно, чем использованием computed.

Добавим немного контекста. Тест проводился на дропдауне с 1 000 000 элементов. Задача теста состояла в том, чтобы написать функцию, которая будет конвертировать объекты (или строки) с типом selectOption в объект selectOptionsObject.

Типы выглядят так:

export type selectOption = selectOptionObject | string;  
export type selectOptionObject = {
id: string | number;
render: string;
raw?: any;
};


Код функции normaliseOptions для конвертации:

const normaliseOptions = (
options?: selectOption[]
): normalisedOptionObject[] => {
if (!options) return [];

// We will use a straight for loop for performance
const normalisedOptions = [];
for (let i = 0; i < options.length; i++) {
const option = options[i];
if (typeof option === "string") {
normalisedOptions.push({
id: option,
render: option,
});
continue;
}
normalisedOptions.push({
id: option.id.toString(),
render: option.render,
disabled: option.disabled || false,
raw: option.raw,
});
}
return normalisedOptions;
};


Код, который использовался в тесте:

1) С использованием computed:

const normalisedOptions = computed(() => {
return normaliseOptions(props.options);
});


2) С использованием watch + ref:

// Using the pattern below rather than a computed value gives us a 2x performance improvement
const normalisedOptions = ref(normaliseOptions(props.options));
const recomputeOptions = () => {
normalisedOptions.value = normaliseOptions(props.options);
};

watch(
() => props.options,
() => {
recomputeOptions();
}
);


Самые умные и опытные уже догадались, в чём может заключаться проблема конкретно в данном случае (а я не успел самостоятельно подумать и наткнулся на ответ в комментариях). Проблема заключается в том, что computed отслеживает каждую вложенную зависимость. Поэтому он будет работать медленнее, если props.options не является плоской структурой, состоящей из примитивов, потому что требуется провести кратно больше вычислений. Таким образом, корректный код для данного случая выглядит так:

const recomputedOptions = computed(() => normaliseOptions(toRaw(props.options)));


Метод toRaw извлекает необработанный объект из proxy-объекта, созданного Vue, давая нам возможность поработать с оригинальным объектом. Подробнее про него можно почитать в документации.
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Браузерные ОС

#браузеры #linux #wasm

На одной из прошлых работ гендиректор сказал мне, что считает, что операционные системы со временем перетекут в браузер, в качестве аргумента он упомянул Electron, который является базой для большого числа современных десктоп-приложений. Я запомнил это, но не придавал значения его словам, хотя они с каждым днём всё ближе к реальности.

Пару лет назад я писал про WebVM, который представляет из себя linux без графической оболочки, работающий через WASM. Пару недель назад видел несколько постов про InternetOS puter. По сути, она представляет из себя графическую оболочку рабочего стола, работающую на веб-технологиях (js + jQuery).

В отличие от WebVM, puter это только GUI, операции происходят на сервере (заметно, что на каждое действие, отправляется запрос в API). Документации для API-сервера пока что нет. Его исходного кода мне также не удалось найти (допускаю, что плохо искал, хотя и заглянул в каждый репозиторий). Разработчики обещают опубликовать документацию по API-серверу к концу марта, чего я буду ждать с нетерпением.

Как вы думаете, есть ли будущее у браузерных операционных систем? Смогут ли они со временем стать заменой привычным десктопным оболочкам?

Мне кажется, что определённый тренд в этом направлении есть и во многом это происходит уже сегодня, правда в облачном гейминге. Количество сервисов, предоставляющих подобную услугу, уже довольно большое, значит, это находит некий отклик среди аудитории. Думаю, в ближайшие годы мы будем наблюдать всё больше развития в этой сфере.
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Что нужно знать python backend developer для устройства на работу

#django #python #backend #fastapi

Пару лет назад я уже писал подобный пост. Тогда он был основан на моём личном опыте в найме (текущий, в общем-то тоже), пришло время актуализировать список. Если считаете, что я что-то упустил, то жду ваших предложений в комментариях.

👩‍💻 Фреймворки:

- Django (+ DRF),
- FastAPI/Flask (+ alembic, sqlalchemy).

👩‍💻 Базы данных:

- redis,
- postgresql.

👩‍💻 Основы:

- HTTP-протокол, HTTP-методы,
- что такое REST, REST API, RESTful,
- что такое CORS и как его победить,
- linux/unix на уровне понимания работы по SSH, права доступа,
- понимание концепции MVC и принципа работы паттерна репозиторий (больше нужен для работы с SQLAlchemy),
- вопросы для подготовки к интервью (список там довольно большой, обращайте внимание на те моменты, по которым вы совсем ничего не знаете).

👩‍💻 Что нужно знать в django:

Обязательно:

- работа с django ORM: https://docs.djangoproject.com/en/5.0/topics/db/queries/,
- миграции в django ORM: https://docs.djangoproject.com/en/5.0/topics/migrations/,
- менеджеры моделей: https://docs.djangoproject.com/en/5.0/topics/db/managers/,
- сериализаторы, представления и права доступа из DRF: https://www.django-rest-framework.org/tutorial/quickstart/,
- работа с фильтрами через django-filter: https://django-filter.readthedocs.io/en/stable/guide/usage.html.

Опционально:

- формы: https://docs.djangoproject.com/en/5.0/ref/forms/,
- admin actions: https://docs.djangoproject.com/en/5.0/ref/contrib/admin/actions/,
- оптимизация запросов в Django ORM: https://www.tg-me.com/davidobryakov/1195,
- отправка почтовых уведомлений: https://docs.djangoproject.com/en/5.0/topics/email/.

На уровне концепции:

- работа с админкой: https://docs.djangoproject.com/en/5.0/ref/contrib/admin/,
- сигналы: https://docs.djangoproject.com/en/5.0/topics/signals/.

Полезные библиотеки и ссылки для django:

- библиотека для установки настроек CORS: https://github.com/adamchainz/django-cors-headers,
- библиотека для авторизации и регистрации через DRF: https://djoser.readthedocs.io/en/latest/,
- библиотека для создания динамической конфигурации приложения, хранимой в БД: https://django-constance.readthedocs.io/en/latest/,
- библиотека, добавляющая хуки жизненного цикла для моделей Django: https://rsinger86.github.io/django-lifecycle/,
- библиотека, предоставляющая кастомную админку с большим количеством настроек: https://django-jazzmin.readthedocs.io/,
- практические советы для начинающих Django-разработчиков: https://www.tg-me.com/davidobryakov/1184,
- подборка лучших библиотек для Django: https://www.tg-me.com/davidobryakov/1139,
- курс по Django на MDN: https://developer.mozilla.org/ru/docs/Learn/Server-side/Django,
- мой шаблон для Django-проектов: https://github.com/kantegory/django-template.
- работа с ORM SQLAlchemy: https://docs.sqlalchemy.org/en/20/intro.html,
- работа с alembic: https://alembic.sqlalchemy.org/en/latest/,
- понимание async/await: https://docs.python.org/3/library/asyncio-task.html.

👩‍💻 Что нужно знать/уметь в FastAPI/Flask:

- не поместилось по лимиту в ТГ, читайте полную версию поста в блоге.

Общие требования:

- работа с очередями задач с помощью Celery + Redis/Celery + RabbitMQ (выполнение периодических или отложенных задач, например, отправка электронной почты);
- настройка общения между сервисами посредством RabbitMQ

👩‍💻 Инструменты:

- git (тренажёр: https://learngitbranching.js.org/?locale=ru_RU),
- docker, docker compose (на уровне: могу прочесть конфиг, могу запустить проект, могу написать свой простой конфиг),
- gunicorn/uvicorn (иметь представление о том что это и для чего используется),
- nginx (понимание на уровне директив location и upstream).
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Компонент для номера телефона

#frontend #react

Около месяца назад я писал пост про библиотеку imask, чтобы стандартизировать ввод номера телефона, с помощью использования маски. Тогда же я упомянул, что основным минусом, на мой взгляд, является то, что нужно написать много дополнительного кода, чтобы достичь желаемого результата.

Пару недель назад я как раз столкнулся с подобной задачей в рамках проекта на react и захотел попробовать реализовать это с помощью imask, но столкнулся с проблемой: нужно отображать список стран, с привязанным к ним кодом. В imask есть возможность решить это, но нет готового набора кодов номеров телефонов. Я вспомнил о решении, которое мои сотрудники находили. Попробовал его адаптировать и понял, что я особо не могу его кастомизировать (по дизайну требовалось выводить двухбуквенное название страны, а не флаг), несмотря на его описание, — "Advanced, highly customizable phone input component for Ant Design."

Поняв, что нахожусь в сложной ситуации, я пошёл искать библиотеки, которые предоставляют просто список телефонных кодов по странам, чтобы реализовать это с помощью imask и нашёл такое решение. По сути, это простая библиотечка, которая возвращает нам объект из пар ключ-значение в том формате, в котором нам необходимо. Казалось бы, всё замечательно. Можно брать динамически маски в imask, генерировать их с помощью этих самых кодов и идти спокойно пить чай. Но в этот момент я осознал, что у разных стран, буквально, могут быть разные маски для ввода номера телефона и это окончательно закопало мою прекрасную идею об использовании imask для решения этой задачи.

Пример с самого сайта imask, где наглядно видно, что маски вообще не подчиняются единым правилам и могут выглядеть абсолютно как угодно:

[
{
mask: '+00 {21} 0 000 0000',
startsWith: '30',
lazy: false,
country: 'Greece'
},
{
mask: '+0 000 000-00-00',
startsWith: '7',
lazy: false,
country: 'Russia'
},
{
mask: '+00-0000-000000',
startsWith: '91',
lazy: false,
country: 'India'
},
{
mask: '0000000000000',
startsWith: '',
country: 'unknown'
}
]
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 Компонент для номера телефона (продолжение)

#frontend #react

Ресёрч пришлось продолжать. Я нашёл библиотеку, которая позволяла валидировать номер телефона относительно двухбуквенного кода страны, к которой этот номер относится. Кроме прочего, в этой библиотеке есть и возможность форматирования номера телефона, как раз под маску, принятую в конкретной стране. В описании библиотеки также упоминался react-компонент, который использует эту самую библиотеку. И на основе уже этого компонента, я собрал своё решение с использованием ant-design (разумеется, оно не идеальное, но достаточно хорошее, чтобы использовать в условном продакшне):


import PhoneInput from 'react-phone-number-input';
import PropTypes from 'prop-types';
import { getCountries } from 'react-phone-number-input';
import { Select as AntdSelect } from 'antd';

const phoneCountryLabels = () => {
const countryLabels = {};

getCountries().forEach((country) => (countryLabels[country] = country));

return countryLabels;
};

const CountrySelect = ({ value, onChange, labels, variant = 'filled', ...rest }) => (
<AntdSelect
{...rest}
value={value}
showSearch
optionFilterProp="label"
onChange={onChange}
variant={variant}
size="large"
style={{ '--ant-select-single-item-height-lg': '3rem' }}
/>
);

CountrySelect.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
labels: PropTypes.objectOf(PropTypes.string).isRequired,
variant: PropTypes.string,
};

const PhoneNumberInput = ({ onChange, country = 'US' }) => {
return (
<div className="base-phone-number">
<PhoneInput
onChange={onChange}
defaultCountry={country}
international
limitMaxLength
labels={phoneCountryLabels()}
countrySelectComponent={CountrySelect}
numberInputProps={{
className: 'ant-input ant-input-filled css-var-r1 ant-input-css-var base-input',
}}
/>
</div>
);
};

PhoneNumberInput.propTypes = {
onChange: PropTypes.func.isRequired,
country: PropTypes.string,
};

export default PhoneNumberInput;


А как вы решали подобные задачи? Делитесь, будет интересно узнать.

P. S. как появится немного времени, планирую тоже самое адаптировать под vue-компонент и поделиться результатом
Please open Telegram to view this post
VIEW IN TELEGRAM
2024/05/09 04:01:57
Back to Top
HTML Embed Code: