-
Notifications
You must be signed in to change notification settings - Fork 131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(MaskedInput): (rev. 2) #3390
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*backward compatible
halfee
reviewed
May 31, 2024
packages/react-ui/internal/MaskedInputElement/MaskedInputElement.tsx
Outdated
Show resolved
Hide resolved
packages/react-ui/internal/MaskedInputElement/MaskedInputElement.tsx
Outdated
Show resolved
Hide resolved
mshatikhin
reviewed
Aug 19, 2024
…MaskedInput-fixes # Conflicts: # packages/react-ui/test-setup.js
zhzz
reviewed
Aug 27, 2024
Нужно продублировать этот ПР в 5.x. Выкатывать будем в 5.0, т.к. изменения ломающие. Этот предлагаю потом просто закрыть. |
Попробую просто сменить base ветку. 5.x же отпачкована от |
…kedInput-fixes # Conflicts: # packages/react-ui/.creevey/images/MaskedInput/Mask/firefox2022.png # packages/react-ui/.creevey/images/MaskedInput/Mask/firefox2022Dark.png # packages/react-ui/.creevey/images/MaskedInput/Validations/chrome2022.png # packages/react-ui/.creevey/images/MaskedInput/Validations/chrome2022Dark.png # packages/react-ui/.creevey/images/MaskedInput/Validations/firefox2022.png # packages/react-ui/.creevey/images/MaskedInput/Validations/firefox2022Dark.png # packages/react-ui/components/Input/__stories__/Input.creevey.stories.tsx # packages/react-ui/components/MaskedInput/__stories__/MaskedInput.creevey.stories.tsx # packages/react-ui/components/MaskedInput/__stories__/MaskedInput.stories.tsx # packages/react-ui/components/MaskedInput/__tests__/MaskedInput-test.tsx # packages/react-ui/package.json # packages/react-ui/test-setup.js # yarn.lock
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
❌ Проблемы
В первой редакции осталась старая логика отображения символов маски, когда эти символы рендерятся в спан-элементах поверх нативного инпута.
У такого способа есть неоспоримые плюсы:
Но он также создаёт и проблемы.
value
. Чтобы корректно вырезать не введённую часть маски приходилось лезть в их внутрянку и писать хитрую логику. Это создаёт баги и вInput mask
и в первойMaskedInput
.MaskedInput
. Но удалось учесть только простые сценарии iMask, похожие на старые изInput mask
.✅ Новый концепт
Такая идея лежит во второй ревизии компонента.
Теперь если символы маски видно, значит они физически присутствуют в инпуте. Если не видно, то их и нет. Как в обычном Input.
При этом введённое пользователем значение (темная часть) всё ещё доступно, но лежит в специальном атрибуте
data-typed-value
.value
value
Чтобы символы подчёркивания не сливались в одну линию, мы вернулись к истокам - применили специальный шрифт на основе Lab Grotesque Regular.
Он доступен на статике под коротким название
ui
— https://s.kontur.ru/common-v2/fonts/ui/.Мы же храним его в пакете в формате base64. Это избавит нас от лишней связности, и не потребует от пользователя каких-либо дополнительных настроек билдеров.
Потенциально такой способ тоже может работать с разными шрифтами и начертаниями.
Для покраски не введённых символов в серый используются CSS и немного shadow DOM.
Всю реализацию второй редакции можно представить в виде 3-х слоёв, где в основе всегда остаётся обычный инпут.
ui.woff
стилизует символы подчёркиванияinput
🔍 Технические детали
В коде логика разделёна на 3 компонента:
MaskedInput
— точка входа. отвечает за пропы и схожесть в поведении с обычнымInput
FixedIMaskInput
— исправляет некоторые проблемы iMaskColorableInputElement
— занимается покраской символовColorableInputElement
После отказа от поддержки IE11 удалось спрятать вёрстку символов маски.
Новый код минимально влияет на вёрстку, благодаря чему
MaskedInput
тестируется как обычныйInput
.Работает всё с помощью градиента, стилей
background-clip
1 и-webkit-text-fill-color
2, иshadow-dom
.В
shadow-dom
рендерится заполненная часть маски, что позволяет узнать её ширину в пикселях.Затем с помощью
linear-gradient
введённая часть маски красится чёрным, и символы маски серым.А стиль
background-clip: text
переносит цвет фона внутрь текста.Вся реализация инкапсулирована в отдельный компонент
<ColorableInputElement />
.Старый новый шрифт
ui
Символ подчеркивания снова реализовал через специальный шрифт (
ui
), как это было в старых версиях пакета.Но теперь шрифт лежит на сервере статики, что позволило избежать сложностей, которые он создавал раньше3.
У текущая реализации подключения шрифта есть ограничение, о котором написано ниже.
FixedIMaskInput
Используемый внутри пакет
imask
умеет предотвращать смешение символов, если использовать апостроф в маске.Из-за этой фичи инпут всегда позволяет добавлять символы не только строго слева-направо, но и в середине маски.
Если эту фичу не использовать, то такое поведение больше похоже на баг, т.к. введённые в середине маски символы затем могут сместиться к ближайшему введённому символу слева.
Это всё противоречит текущему привычному поведение, когда с серыми символами маски невозможно взаимодействовать.
Всё исправлено в специальном компоненте
<FixedIMaskInput />
📛 Оставшиеся ранее известные ограничения
Также есть некоторые проблемы/недоработки которые были и в предыдущей реализации
<Input mask />
, но на которые не жаловались.Ломается маска при наличия скролла
Если
value
шире чемwidth
компонента, то появится возможность скролить содержимое стрелками, мышкой, или скролиться будет само по мере ввода.В предыдущей реализации ломалась вёрстка маски, и она могла пропадать частями.
В новой реализации css свойство
background-clip: text
всегда отображает текст будто у элементаscrollLeft = 0
.Настройки свойства
background-position
в этом случае тоже игнорируются.Единственный способ исправить - отключать подсветку при наличие скролла.
Маску можно заполнять строго слева направо
В предыдущей реализации технически невозможно ввести символ, скажем, в середине маски.
В новой реализации принципиально такая возможность есть (см символ ` в imask), но сейчас подсветка работает по старой логике.
В принципе это можно реализовать, но надо ли.
🤕 Новые ограничения
eager=true
По Гайдам во время редактирования каретка должна автоматически перескакивать фиксированные символы (скобки, тире и тп).
Это достигается пропом
eager=true
пакетаimask
. Но в самом пакетеimask
возникает баг, при удалении по клавишеdelete
— удаляются символы слева от каретки.Это не удалось исправить внутри нашего компонента, поэтому пока используется проп
eager=append
.Uncontrolled ввод и
defaultValue
При появлении маски по фокусу ломается неконтролируемый ввод, если
defaultValue
содержит любую фиксированную часть маски.Это происходит из-за динамической смены пропа
lazy
при фокусе.Исправить это можно, есть использовать проп
showOnFocus
в компонентColorableInputElement
.Тогда символы маски будут рендериться всегда, а их скрытие будет реализовано через CSS.
Но в таком случае
MaskedInput
будет отличаться от обычногоInput
, т.к. визуально отсутствующие символы маски будут находится вvalue
.Я посчитал что лучше пожертвовать довольно редким сценарием, чем лишаться удобства тестирования.
Шрифт
ui
подключается только при монтированииДля красивого символа земли
_
теперь используется специальный шрифт4.Особенность его подключение в том, что основной шрифт должен быть явно указан в том же css правиле:
Для этого основной шрифт необходимо получить через
getComputedStyle
у родителя.Проблема в том, что не существует общего/готового механизма, который бы оповещал о смене шрифта для конкретного элемента на странице.
Поэтому в текущей реализации основной шрифт берётся из
body
единожды, после чего шрифтui
подключается нужным элементам.Ссылки
Демо работы покраски текста градиентом:
https://codepen.io/lossir/pen/ExrewOx
Чек-лист перед запросом ревью
Добавлены тесты на все изменения
✅ unit-тесты для логики
✅ скриншоты для верстки и кросс-браузерности
⬜ нерелевантно
Добавлена (обновлена) документация
✅ styleguidist для пропов и примеров использования компонентов
✅ jsdoc для утилит и хелперов
✅ комментарии для неочевидных мест в коде
✅ прочие инструкции (
README.md
,contributing.md
и др.)⬜ нерелевантно
Изменения корректно типизированы
✅ без использования
any
(см. PR2856
)⬜ нерелевантно
Прочее
✅ все тесты и линтеры на CI проходят
✅ в коде нет лишних изменений
✅ заголовок PR кратко и доступно отражает суть изменений (он попадет в changelog)
Footnotes
https://caniuse.com/background-clip-text ↩
https://caniuse.com/mdn-css_properties_-webkit-text-fill-color ↩
https://github.com/skbkontur/retail-ui/issues/2181 ↩
https://s.kontur.ru/common-v2/fonts/ui/ ↩