Skip to content

Skeptick/input-mask-compose

Repository files navigation

input-mask-compose

Библиотека для форматирования вводимых пользователем значений с поддержкой Compose Multiplatform.

Использование

kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation("io.github.skeptick.inputmask:core:0.0.4")
                implementation("io.github.skeptick.inputmask:compose:0.0.4")
            }
        }
    }
}

Поддерживаемые платформы

core поддерживает все таргеты Kotlin-а, compose все таргеты Compose Multiplatform.

Идея

Синтаксис масок вдохновлён небезызвестной библиотекой от RedMadRobot ( Android, iOS ), но реализация гораздо более примитивная. В частности, не поддерживаются «родственные» маски.

Объявление масок

Детальнее в документации RedMadRobot.

Верхнеуровнево:

  • в [] описываем то, что ожидаем от пользователя
  • в {} любые символы, которые хотим получить в извлекаемом значении
  • все символы за пределами [] и {} будут вставлены в процессе форматирования, но не попадут в извлекаемое значение

В [] поддерживаются следующие символы:

  • 0 - обязательная цифра
  • 9 - опциональная цифра
  • A - обязательная буква
  • a - опциональная буква
  • _ - обязательная буква или цифра
  • - - опциональная буква или цифра
  • - неограниченное количество цифр или букв
    • если перед стоит 0 или 9, то будут ожидаться цифровые символы
    • если перед стоит A или a, то будут ожидаться буквенные символы
    • если перед стоит _ или -, или не стоит ничего, то будут ожидаться буквы или цифры

Создание маски

val inputMask = InputMasks.getOrCreate("+{7} ([000]) [000]-[0000]")

Поддерживается создание с помощью простого DSL:

val inputMask = InputMasks.build {
    fixedChar('+', extracted = false)
    fixedChar('7', extracted = true)
    fixedChar(' ', extracted = false)
    fixedChar('(', extracted = false)
    repeat(3) { singleDigit(required = true) }
    fixedChar(')', extracted = false)
    fixedChar(' ', extracted = false)
    repeat(3) { singleDigit(required = true) }
    fixedChar('-', extracted = false)
    repeat(4) { singleDigit(required = true) }
}

Использование

val result = inputMask.format("9001234567")
result.isComplete // -> true
result.formattedValue // -> +7 (900) 123-4567
result.extractedValue // -> 79001234567

Обратите внимание на параметр replacePrefix:

inputMask.format("79001234567", replacePrefix = true) // -> +7 (900) 123-4567
inputMask.format("79001234567", replacePrefix = false) // -> +7 (790) 012-3456

Использование в Compose

Артефакт compose поставляет имлементации VisualTransformation для TextField принимающих String или TextFieldValue:

var text by remember { mutableStateOf("") }
val mask = "[000]-[000]"
val visualTransformation = remember { InputMaskVisualTransformation(mask) }

BasicTextField(
    value = text,
    onValueChange = { text = visualTransformation.sanitize(it) },
    visualTransformation = visualTransformation,
)

Специальная вариация для форматирования телефона, обрабатывающая вставку в поле ввода номера как с кодом страны, так и без:

var text by remember { mutableStateOf("") }
var mask = "+{7} ([000]) [000]-[00]-[00]"
val visualTransformation = remember(mask) { PhoneInputMaskVisualTransformation(mask) }

BasicTextField(
    value = value,
    onValueChange = { text = visualTransformation.sanitize(it) },
    visualTransformation = visualTransformation,
)

Обратите внимание, что если маска может измениться в процессе ввода (например, пользователю предоставляется возможность выбрать страну во время авторизации), то нужно также обновить значение:

BasicTextField(
    value = remember(text, mask) { visualTransformation.sanitize(value) },
    // ...
)

Для полей ввода, построенных вокруг TextFieldState есть имлементации InputTransformation и OutputTransformation:

val textFieldState = remember { TextFieldState() }
val mask = "[000]-[000]"
    
BasicTextField(
    state = textFieldState,
    inputTransformation = remember(mask) { InputMaskInputTransformation(mask) },
    outputTransformation = remember(mask) { InputMaskOutputTransformation(mask) },
)

И специальная вариация InputTransformation для телефонных номеров:

BasicTextField(
    // ...
    inputTransformation = remember(mask) { PhoneInputTransformation(mask) },
    // ...
)