برنامه نویسی

بیایید ورودی شماره تلفن بین المللی را با React ، Tailwind CSS و UI بدون سر ایجاد کنیم

در روز ، کاربران مجبور بودند برای اتصال به یک شماره تلفن یا آدرس به یک کارمند شرکت تلفن فریاد بزنند. اگرچه امروزه ، این رویکرد دوباره در بین سیستم های مدیریت تماس خودکار محبوبیت پیدا کرد ، اما ورودی های تلفنی پیشرفت بزرگی را کسب کردند و از معاملات پلاستیکی دور با 10 رقم تا رابط های مدرن روی صفحه نمایش حاصل شد.

نسخه ی نمایشی ورودی تلفن

امروز می خواهیم یکی از آنها را بسازیم. این ورودی قادر خواهد بود:

  • ادغام کد بین المللی کشوربشر ما یک انتخاب کننده کد کشور واضح و به راحتی در دسترس را ارائه خواهیم داد ، که برای پایگاه های جهانی کاربر ضروری است. این ویژگی قالب بندی دقیق شماره را تضمین می کند.

  • پوشش ورودیبشر استفاده از ماسک های ورودی با هدایت کاربران از طریق فرمت شماره صحیح ، روند ورود را ساده می کند. این باعث کاهش خطاها و بهبود خوانایی می شود.

  • ناوبری لیست کشور در دسترسبشر ما قصد داریم تا از چندین کشور انتخابی را به کاربران ارائه دهیم. بنابراین آنها باید بتوانند بدون زحمت در لیست حرکت کنند و فیلتر کنند.

یک مجموعه فیلد تلفن ایجاد کنید

در اینجا تجزیه و تحلیل کل مؤلفه شماره تلفن بین المللی FieldSet ارائه شده است.

تجزیه قطعات

CountryCodeInput برای انتخاب کدهای کشور ، کشویی جعبه لیست را فراهم می کند. PhoneNumberInput با اعتبار سنجی فرمت از ورود شماره تلفن مراقبت می کند. Field از UI بدون سر ساختار درستی را فراهم می کند ، اما ما نمی توانیم از کتابخانه استفاده کنیم Label مؤلفه ، بنابراین ما باید اساسی را پیاده سازی کنیم HTMLLabelElementبشر

import { Field } from '@headlessui/react';

import { countryList } from './countryList.ts';
import { CountryCodeInput } from './CountryCodeInput';
import { PhoneNumberInput } from './PhoneNumberInput';

const App = () => {
  // ...
  return (
    <Field className="flex flex-col gap-2">
      <label className="cursor-pointer text-sm/6 font-medium" htmlFor={id}>
        Phone number:
      label>
      <div className="flex gap-3">
        <CountryCodeInput
          countryList={countryList}
          value={countryCode}
          onChange={handleCodeChange}
        />
        <PhoneNumberInput
          id={id}
          onChange={handlePhoneChange}
          value={phoneNumber}
        />
      div>
    Field>
  );
};

export default App;
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مؤلفه اصلی (src/App.tsx) دو متغیر حالت را حفظ می کند:
phoneNumber که شماره تلفن وارد شده کاربر را ذخیره می کند
countryCode که کد انتخاب شده کشور را ردیابی می کند (پیش فرض به "+1").

import { useCallback, useState } from 'react';
import { countryList, CountryConfig } from './countryList.ts';

// phone number logic
const [phoneNumber, setPhoneNumber] = useState('');
const handlePhoneChange = useCallback((value: string) => {
  setPhoneNumber(value);
}, []);

// country code logic
const [countryCode, setCountryCode] = useState<CountryConfig['code']>('+1');

const handleCodeChange = useCallback((nextCode?: CountryConfig['code']) => {
  if (nextCode) {
    setCountryCode(nextCode);
  }
}, []);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

پیکربندی کدهای کشور

درون src/countryList.ts ما داده های لازم را برای انتخاب انتخاب بین کدهای تلفن چند کشور و فرمت ها تعریف خواهیم کرد.

CountryConfig نوع ساختار برای هر کشور را با: تعریف می کند:

  • flag: نمایندگی ایموجی پرچم یونیکد (به عنوان مثال ، “🇫🇷” برای فرانسه).
  • code: کد شماره گیری کشور (به عنوان مثال ، “+33” برای فرانسه). یک الگوی تحت اللفظی که تضمین می کند همه کدهای کشور با یک نماد + شروع می شوند و به دنبال آن اعداد یا شخصیت های دیگر.
  • name: نام کامل کشور.
  • mask: الگوی قالب بندی اختیاری برای شماره تلفن. اطلاعات بیشتر در مورد این بعداً
type Code = `+${number | string}`;

export type CountryConfig = {
  flag: string;
  code: Code;
  name: string;
  mask?: string;
};
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ورودی شماره تلفن

ورودی اصلی قسمت تلفن در src/PhoneNumberInput/PhoneNumberInput.tsxبشر مؤلفه چهار غرفه را می پذیرد: id: یک شناسه منحصر به فرد برای عنصر ورودی ؛ mask: یک رشته اختیاری که ماسک ورودی را تعریف می کند. onChange: یک تابع پاسخ به تماس برای رسیدگی به تغییرات در مقدار ورودی. value مقدار فعلی ورودی است.

ما از کلاسهای زیر Tailwind CSS استفاده خواهیم کرد: tabular-nums: عرض شخصیت شماره سازگار ، focus:outline-none data-[focus]:outline-2 ...: تنظیم مجدد طرح پیش فرض و اضافه کردن یک سفارشی.

import { ChangeEvent, FC, useCallback } from 'react';
import classNames from 'classnames';
import { Input } from '@headlessui/react';
import { useMask } from './useMask.ts';

export type Props = {
  id: string;
  mask?: string;
  onChange: (value: string) => void;
  value: string;
};

export const PhoneNumberInput: FC<Props> = ({
  id,
  mask = '______________',
  onChange,
  value: valueProp,
}) => {
  const { options, hasEmptyMask, inputRef } = useMask({ mask });


  return (
    <Input
      placeholder={hasEmptyMask ? 'Phone number' : mask}
      ref={inputRef}
      id={id}
      className={classNames(
        'w-36 rounded-md bg-stone-100 px-2 py-1.5 text-sm/6 tabular-nums text-black',
        'focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-black/25',
      )}
      type="tel"
      onChange={handleChange}
      value={value}
    />
  );
};
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ماسک ورودی تلفن

شماره تلفن ماسک نمایش نسخه ی نمایشی

ماسک ورودی کاربر را راهنمایی می کند تا شماره تلفن خود را با فرمت سازگار و صحیح وارد کند. به عنوان نوع کاربر ، قسمت ورودی به طور خودکار کاراکترهایی مانند پرانتز ، هافن و فضاها را در موقعیت های مناسب وارد می کند.

درون PhoneNumberInput ما از format ابزار از کتابخانه @React-Input/Mask برای تمیز کردن مقدار خروجی از نمادهای ماسک.

import { format } from '@react-input/mask';

// ...

const value = format(valueProp, options);

const handleChange = useCallback(
  (event: ChangeEvent<HTMLInputElement>) => {
    onChange(format(event.target.value, options));
  },
  [onChange, options],
);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در src/PhoneNumberInput/useMask.ts از قلاب سفارشی برای اجرای منطق ماسک ورودی استفاده می شود. برمی گردد options برای ماسک ، یک بولی hasEmptyMask برای بررسی اینکه آیا ماسک خالی است ، و inputRef برای مراجعه به عنصر ورودی.

// src/PhoneNumberInput/PhoneNumberInput.tsx
const { options, hasEmptyMask, inputRef } = useMask({ mask });
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

قلاب یک پروانه واحد را می پذیرد (mask) ، که یک رشته است که الگوی ماسک ورودی مورد نظر را تعریف می کند. این الگوی مشخص می کند که چگونه ورودی باید فرمت شود ، با تأکیدات ()_) نمایندگان متقاضیان رقمی.

درون قلاب ، useMemo برای ایجاد گزینه های ماسک استفاده می شود. این تضمین می کند که آنها فقط در صورت محاسبه مجدد محاسبه می شوند mask تغییر عملکرد ، بهینه سازی عملکرد. شیء جایگزینی در گزینه ها مشخص می کند که باید زیر نظر را با رقم جایگزین کرد /\d/بشر

در useMaskVanilla عملکرد از @react-input/mask کتابخانه با گزینه های ماسک فراخوانی می شود تا مرجع عنصر ورودی ایجاد شود.

قلاب همچنین شامل یک چک برای تعیین اینکه آیا ماسک ارائه شده خالی است یا خیر. این کار با تقسیم رشته ماسک در کاراکترهای جداگانه و بررسی اینکه آیا همه شخصیت ها تأکید می کنند ، انجام می شود. نتیجه در hasEmptyMask متغیر

import { useMemo } from 'react';
import { useMask as useMaskVanilla,  } from '@react-input/mask';

export type Props = {
  mask: string;
};

export const useMask = ({ mask }: Props) => {
  const options = useMemo(
    () => ({
      mask,
      replacement: { _: /\d/ },

    }),
    [mask],
  );

  const inputRef = useMaskVanilla(options);

  const hasEmptyMask = mask.split('').every((char) => {
    return char === '_';
  });

  return { options, inputRef, hasEmptyMask };
};

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ورودی انتخاب کد کشور

یک کد کشور تلفن ، که به عنوان کد شماره گیری مشترک بین المللی (ISD) نیز شناخته می شود ، پیشوند عددی است که پیش از یک شماره تلفن است. عملکرد اصلی آن مسیریابی تماس ها و پیام ها به کشور صحیح یا منطقه جغرافیایی است.

src/CountryCodeInput/CountryCodeInput.tsx سه پیشنهاد را می پذیرد: countryListبا valueوت onChangeبشر در countryList مجموعه ای از تنظیمات کشور است ، value کد کشور منتخب است (+351)) ، و onChange یک تابع پاسخ به تماس برای رسیدگی به تغییرات در کد انتخاب شده کشور است.

export type Props = {
  countryList: CountryConfig[];
  value: CountryConfig['code'];
  onChange: (value?: CountryConfig['code']) => void;
};
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

CountryCodeInput یک وضعیت داخلی را برای لیست کشور حفظ می کند ، که با countryList هر زمان که دومی تغییر کند ، خاصیت و به روز می شود.

const [countryList, setCountryList] = useState(countryListProp);

useEffect(() => {
  setCountryList(countryListProp);
}, [countryListProp]);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما ورودی کد کشور را به عنوان ترکیبی از Listbox و مؤلفه های ورودی از UI بدون سر پیاده سازی خواهیم کرد.

در Listbox از مؤلفه برای ایجاد منوی کشویی استفاده می شود. در ListboxButton کد و پرچم کشور منتخب را نشان می دهد ، و ListboxOptions شامل لیست گزینه های کشور است. این همان چیزی است که کاربر در ابتدا می بیند.

در Input در صورت وجود بیش از پنج کشور ، از مؤلفه برای فیلتر کردن لیست کشور استفاده می شود.

ما کلاس گروه Tailwind را به ListboxOption برای نمایش یک علامت گذاری برای نشان دادن انتخاب فعلی.

<div className="w-24">
  <Listbox value={selected} onChange={handleSelect}>
    <ListboxButton
      className={classNames(
        'relative w-full rounded-md bg-stone-100 px-2 py-1.5 text-sm/6 text-black',
        'flex items-center gap-1',
        'focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-black/25',
      )}
    >
      <span>{selectedFlag}span>
      <span className="ml-auto grow-0">{selected}span>

      <Icon className="group pointer-events-none size-3.5 shrink-0 text-black/60" name="caret-down" />
    ListboxButton>
    <ListboxOptions
      anchor={ANCHOR_PROP}
      transition
      className={classNames(
        'w-56 rounded-md border border-stone-300 bg-stone-100 focus-visible:outline-none',
        'flex flex-col',
        'origin-top transition duration-200 ease-in data-[closed]:scale-95 data-[closed]:opacity-0',
        'shadow-md shadow-black/20',
      )}
    >
      {countryListProp.length > 5 && (
        <div className=" border-b-2 border-b-black/10 p-2">
          <Input
            value={filter}
            onChange={setFilter}
            placeholder="Country name"
            className={classNames(
              'max-w-full rounded-full bg-stone-200 px-4 py-1 text-sm/6 text-black ',
              'focus:bg-white focus:outline-none data-[focus]:outline-2 data-[focus]:-outline-offset-2 data-[focus]:outline-black/25',
            )}
          />
        div>
      )}
      <div className="max-h-48 grow overflow-auto">
        {countryList.map(({ code, flag, name }) => (
          <ListboxOption
            key={code}
            value={code}
            className="group flex cursor-pointer select-none items-center gap-1 p-1.5 data-[focus]:bg-black/10"
          >
            <div className="size-4">
              <Icon className="invisible size-4 text-black group-data-[selected]:visible" name="check" />
            div>
            {flag}
            <div className="w-9 shrink-0 text-right text-sm/6 tabular-nums text-black">
              {code}
            div>
            <div className="truncate text-sm/6 text-black/65 group-data-[hover]:text-black">
              {name}
            div>
          ListboxOption>
        ))}
      div>
    ListboxOptions>
  Listbox>
div>
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

انتخاب کشور

نسخه ی نمایشی انتخاب کشور

src/CountryCodeInput/useCountrySelect.ts قلاب سفارشی منطق انتخاب لیست کشور را مدیریت می کند. این فراهم می کند selectedFlagبا handleSelectوت selected مقادیر رسیدگی به انتخاب و نمایش کشور منتخب.

useCountrySelect همان والدین را می پذیرد CountryCodeInputبشر NB! ما استفاده می کنیم countryList ارزش ملک ، نه وضعیت داخلی به همین نام. این مورد برای سازگاری با عملکرد فیلتر لازم است.

// src/CountryCodeInput/CountryCodeInput.tsx
const { selectedFlag, handleSelect, selected } = useCountrySelect({
  onChange,
  countryListProp,
  value,
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

useCountrySelect یک حالت داخلی را انتخاب می کند که برای پیگیری کد کشور انتخاب شده در حال حاضر انتخاب شده است. این حالت با مقدار ارزش اولیه تنظیم می شود و هر زمان که مقدار ارزش تغییر می کند به روز می شود.

handleSelect عملکرد حالت انتخاب شده را به روز می کند و تماس می گیرد onChange تماس با کد کشور تازه انتخاب شده.

selectedFlag متغیر با استفاده از useMemo قلاب این پرچم کشور منتخب را با جستجوی countryListProp آرایه برای کشور با کد تطبیق.

سرانجام ، قلاب این مقادیر را برای استفاده از مؤلفه والدین برای مدیریت انتخاب و نمایش کشور انتخاب شده باز می گرداند.

import {
  useCallback,
  useEffect,
  useMemo,
  useState,
} from 'react';
import { CountryConfig } from '../countryList.ts';

export type Props = {
  countryListProp: CountryConfig[];
  value: CountryConfig['code'];
  onChange: (value?: CountryConfig['code']) => void;
};

export const useCountrySelect = ({
  value,
  onChange,
  countryListProp,
}: Props) => {
  const [selected, setSelected] = useState(value);

  useEffect(() => {
    setSelected(value);
  }, [value]);

  const handleSelect = useCallback(
    (selectedCode: CountryConfig['code']) => {
      onChange(selectedCode);
      setSelected(selectedCode);
    },
    [onChange],
  );

  const selectedFlag = useMemo(
    () => countryListProp.find(({ code }) => code === selected)?.flag,
    [countryListProp, selected],
  );

  return { selected, handleSelect, selectedFlag };
};

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

لیست کشور فیلتر

نسخه نمایشی فیلتر لیست کشور

src/CountryCodeInput/useCountryFilter.ts قلاب سفارشی برای فیلتر کردن لیست کشور بر اساس ورودی کاربر استفاده می شود. آن را فراهم می کند filter حالت و الف setFilter عملکرد برای به روزرسانی مقدار فیلتر.

useCountryFilter دو غرفه را می پذیرد: countryListProp (همان فوق) و setCountryList: تابعی برای به روزرسانی لیست کشور فیلتر شده.

// src/CountryCodeInput/CountryCodeInput.tsx
const { filter, setFilter } = useCountryFilter({
  setCountryList,
  countryListProp,
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

useCountryFilter حالت داخلی را حفظ می کند filter برای پیگیری مقدار فیلتر فعلی. این حالت به عنوان یک رشته خالی آغاز می شود.

setFilter عملکرد به روز می کند filter دولت و فیلتر countryListProp بر اساس ورودی کاربر. اگر ورودی خالی نباشد ، این لیست را فیلتر می کند تا فقط کشورهایی را شامل شود که نام آنها حاوی مقدار ورودی (حساس به مورد) است. اگر ورودی خالی باشد ، لیست را به نسخه اصلی تنظیم می کند countryListPropبشر

import { ChangeEvent, useCallback, useState, Dispatch, SetStateAction } from 'react';
import { CountryConfig } from '../countryList.ts';

export type Props = {
  countryListProp: CountryConfig[];
  setCountryList: Dispatch<SetStateAction<CountryConfig[]>>;
}

export const useCountryFilter = ({setCountryList, countryListProp}: Props) => {
  const [filter, setFilterState] = useState('');

  const setFilter = useCallback(
    (event: ChangeEvent<HTMLInputElement>) => {
      setFilterState(event.target.value);
      const nextCountries = countryListProp.filter(({ name }) =>
        name.toLowerCase().includes(event.target.value.toLowerCase()),
      );
      if (event.target.value !== '') {
        setCountryList(nextCountries);
      } else {
        setCountryList(countryListProp);
      }
    },
    [countryListProp, setCountryList],
  );

  return {filter, setFilter}
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

سرانجام ، ما این بلوک را به src/CountryCodeInput/CountryCodeInput.tsx برای رسیدگی به نتایج فیلتر خالی.

{countryList.length === 0 && (
  <div className="py-1.5 text-center text-sm/6 text-black/65">
    No results.
  div>
)}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نسخه آزمایشی

در اینجا نمایشی کامل از مؤلفه ورودی تلفن بین المللی ارائه شده است.

برنامه نویسی مبارک!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا