Обновить
8K+
15
Константин Роман@nihil-pro

Пользователь

6,1
Рейтинг
9
Подписчики
Отправить сообщение

Ура, может так появится нормальный интерфейс в приложении Яндекс Такси!

Самописная шина из 20 строк кода может быть лучше NgRx или postboy для проекта на 15 компонентов. Но когда система разрастается до 200+ акторов — ищите решения с типизацией.

Если отбросить в сторону проблемы event-driven, то всю статью можно сократить до нескольких строк:

Ребята, у нас есть «кафка*» в браузере. Вот как она работает:

Можно использовать CustomEvent как есть, но получится «многословно» и с подсказками в IDE будет не очень, поэтому формируем реестр событий с единым API и храним их в одном месте. Api, например, такой:

interface PriceChangedEventPayload {
  productId: string;
  newPrice: number;
}

export class PriceChangedEvent extends CustomEvent<PriceChangedEventPayload> {
  
  init: CustomEventInit<PriceChangedEventPayload>;

  // избавляемся от бойлерплейта при создании событий
  constructor(detail: PriceChangedEventPayload) {
    const init = { detail }
    
    super('onPriceChanged', init);
    
    this.init = init;
    
    // избавляемся от бойлерплейта при отправке событий
    dispatchEvent(this);
  }
  
}

// Добавляем подсказки для IDE и Typescript-а
declare global {
  interface WindowEventMap {
    onPriceChange: PriceChangedEvent;
  }
}

Ну и пользуемся:

// где-то в одном месте
addEventListener('onPriceChange', (event) => {
  // ...
});



// где-то в другом месте
import { PriceChangedEvent } from 'registry'

if (priceWasChanged) {
  // Создание экземпляра сразу диспатчит событие
  new PriceChangedEvent({ productId, newPrice });
  
}

*Конечно это не кафка, но:

  • Формат сообщения = единственный контракт

  • Издатель не знает подписчиков

  • Брокер гарантирует доставку

Так это ведь девтулзы, там много чего можно принудительно включить/выключить, чтобы потестить какое-то поведение. Как только девтулзы закрываются, это больше не работает.

Об элементах внутри shadowDom. Я ловил в элементе label и в плейсхолдеров в инпутах.

:root { color-scheme: light dark; }

Такая инструкция говорит браузеру выбрать оформление в соответствии с оформлением на уровне ОС. Если на уровне ОС установлена темная тема, будет выбрано оформление dark, и наоборот.

С одним аргументом она ограничивает использование цветовой схемы. Если указать так:

:root {
  color-scheme: light;
}

То при изменении темы на уровне ОС, ui никак реагировать не будет, а оформление всегда будет light. Пример тут https://codepen.io/s5604/pen/vEErMvL

Код из моего примера этим манипулирует:

:root:has(#theme:checked) {
  color-scheme: dark;
}

:root:has(#theme:not(:checked)) {
  color-scheme: light;
}

В этом случае оформление будет зависеть от состояния чекбокса, а при изменении темы на уровне ОС, ui реагировать не будет. Пример тут https://codepen.io/s5604/pen/jEEKoLQ

заметно хуже

Не так уж и заметно:

prefer-color-scheme с 30 июля 2019 г.

color-scheme  c 7 апреля 2020 г.

Поддержка ключевого слова system-ui очень хорошая

Нужно еще учитывать особенность Safari, он не применит этот шрифт к некоторым элементам внутри веб-компонентов (если они используются), я обхожу это так:

* {
  font-family: "Roboto", system-ui;
}

Такая реализация не позволяет включить или выключить тёмную тему для отдельного сайта, поэтому так никто не делает

Позволяет.

Добавляем checkbox

<input id="theme" type="checkbox" />

И еще немного CSS

:root:has(#theme:checked) {
  color-scheme: dark;
  /*    */
}

:root:has(#theme:not(:checked)) {
  color-scheme: light;
  /*    */
}

Нужно также иметь ввиду, что любое переключение темы через CSS/JS всегда будет конкурировать с настройкой ОС, что не всегда хорошо.

Очень близко! Наш нейрончик хорош

Почему если в generateDataSets увеличить количество итераций до 110, то наш нейрончик будет думать что правильный ответ 157, а если до 120 то вообще выдаст NaN. От чего это зависит? Как подобрать правильное количество данных?

P.s. Спасибо за статью

Кажется что плюсы интернациализации теряются, когда нам надо поставить кастомные сообщения

В каком-то смысле да. Но все познается в сравнении. Если не задать текст ошибки, будет использоваться дефолтный, он не идеален, но лучше чем ничего + интернациализация из коробки. Но для сравнения, давайте возьмем какую-то альтернативу, например что-то типа zod/yup, (они очень похоже).
Я не буду воспроизводить пример выше полностью, потому что это долго, возьмем одно свойство:

const formSchema = object({
  email: string().email({ message: "Невалидный емэйл" });
})

Как добавить интернациализацию для такой валидации? Можно взять что-то вроде i18n, или прям завести словарик если языков нужно поддерживать два или три. То же справедливо и для решения предложенном в статье.

Преимущества встроенного API для валидации, не столько в сообщениях об ошибках, хотя и в этом оно выигрывает. Например, что будет если для схемы выше в поле емэйл мы введем "mymail@myhost.com", если согласно условию, можно вводить адреса только вида @habr.com, что нам скажет zod или yup в этом случае? Невалидный емэйл? Но он валиден, просто не на хабре а на myhost.

Constraint Validation API предлагает более гибкий способ менять текст ошибки. В статье к это демонстрируется, например – будет одна ошибка если введенный текст вообще не емэйл и другая если он не заканчивается на habr.com.

Так же немного сбивает с толку PaymentForm

PaymentForm это «стейт», можно было его сделать как угодно – хуки, редакс, зустанд и т.д. Я реализовал на удобном для меня инструменте. FormValidator служит совсем другой цели, и он может работать с любой формой и любым количеством полей.

Если бы мы использовали zod/yup, то нам точно также нужно было бы использовать какое-то состояние (стейт). В примере ниже formSchema описывает схему валидации, но нам нужно откуда-то взять объект и скормить ей, чтобы проверить валиден он или нет:


const formSchema = object({
  sum: number().required().positive().integer(),
  email: string().email(),
  password: string().min(5).max(12),
  passwordCopy: string().min(5).max(12),
});

// псевдокод
function Form() {
  
  const [form, setForm] => useState({
    sum: 0,
    email: '',
    password: '',
    passwordCopy: '',
  })

  function onChange(event) {
    form[event.target.name] = event.target.value;
    const errors = userSchema.validate(form);
    if (errors) {
      // делаем что-то если есть ошибки
    } else {
      // делаем что-то если ошибок нет
    }
  } 

  // ...
}

Javascript практически идеально (на любой сложности задачи, лишь бы контекстного окна и окна выдачи хватало) генерирует почти любая современная LLM

Слишком громкое заявление конечно ))

storage.#client.do(request)

Мой любимый пример «помощи» LLM. Так она мне тест на класс с приватными свойствами накидала ))

Что пользователям с ограниченными возможностями будет сложно заполнить вашу форму. В ней возраст это текстовое поле в котором нужно вводить числа, а ридерами оно определяется как поле для ввода номера телефона.

Очень просто - попытка отформатировать число приведёт вас к необходимости отказа не только от нативной валидации, но даже и от нативного поля ввода числа.

И приведет к такому accessibility. Спасибо, не надо.

Предлагаете всем пользоваться вашим калькулятором?

Вы это пользователю как объясните?

Я это объяснил читателям статьи: «Чтобы продемонстрировать работу с текстами ошибок, добавим простую схему...».

Покажите же нам хороший пример, а не отмахивайтесь от типичных требований к подобным контролам.

В первую очередь, это демонстрация возможностей а не полностью готовое решение. А кроме того, почему вы решили, что это «хороший пример»? А что если я на калькуляторе что-то посчитал, получил 1,25e7 и хочу это скопировать и вставить в форму? Почему вы считаете хорошим примером отобрать у меня эту возможность?

Как-то плохо работает ваш хак с подтверждением пароля

Для копии пароля в схеме не задан текст ошибки, показывается дефолтный от браузера.

Да и проверка почты лажает

Я не силен в регулярках, паттерн был \w@habr.com, поменял на *@habr.com, теперь нормально.

Да и с числами какая-то ерунда

А если я хочу ввести 1,223,58? Опять же, можешь отрегулировать под свой вкус.

Я вроде 100_000 вводил.. Cлабо отформатировать число так, чтобы от нулей в глазах не рябило?

Это как-то относится к тебе валидации?

И даже это число? Я вводил 333 рубля, почему с меня списали 3 тыщи?!?

Нет, ты ввел 3e3 а это 3000. Ввел бы 333, было бы 333. А ввел бы 2e1, было бы 20.

примера широко применяемой технологии

Ну понятно...))

но примеры своего полностью рабочего решения

Пожалуйста https://habr.com/ru/articles/906534/

именно это я и называю кашей, когда проверки пишутся прямо в сеттере в несколько этажей if

Слабый аргумент. Особенно без рабочего примера как альтернативы.

валидация формы из 10 полей наверное будет с 3 экрана кода

Еще выше в комментариях есть пример с валидацией четырех полей, кода там меньше, чем в этом примере с валидацией двух.

Неужели нужно не просто короткий пример накидывать для понимания общей концепции, а что-то полностью готовое? Мне нужно прямо вынести код валидации в какой-то отдельныйй класс/функцию чтобы продемонстрировать вам, что дублировать этот код в каждом методе не нужно? Как-то это грустно.

Было бы легко - вы бы уже поправили

Я и поправил это

Там Сафари артачится

Я пользуюсь не сафари а хромом. Проверил и в мозиле – та же история.

А что она забыла в конце?

Так работает input. Такое поведение ожидают все пользователи. Так вообще много чего работает, даже ячейки в экселе. Если в поле ввода есть какой-то текст, то когда мы на него переключаемся, каретка оказывается в конце и можно продолжить печатать.

Но можно поддержать и эти хоткеи, конечно

Это не хотелки, а базовое поведение инпутов с типом number, range и всех что со временем связаны.

Вообще не понимаю к чему ты это.

Есть инпут – это источник данных. Когда в нем данные изменяются, он меня об этом колбэком уведомляет. В своем коде я буквально пишу – если мне данные нравятся – я их присваиваю PaymentForm по ключу paymentSum, если не нравятся – не присваиваю. Это значит что в инпуте может быть одно, а в PaymentForm другое. Правильно это или нет, вопрос другой, но я ведь сам кодом написал что хочу так. К чему у тебя претензия?

Ты вот любишь язвить, а представь что с тобой все общались также. Например, в ответ на этот твой комментарий, я бы тебе ответил не по сути, а всякую фигню. Ну типа того что в твоем решении текст выделяется только слева направо, с справа налево нет. Про то, что при фокусировки табом, каретка у тебя перескакивает в начало а не конец инпута. Что в твоем инпуте типа number при нажимании стрелок вверх и вниз меняется не значение, а скачет каретка в конец и начало. Я бы мог так сделать, но зачем? Точно также, я не понимаю зачем ты докапываешься до несущественных мелочей, прекрасно понимая, что это легко поправить. Что твой комментарий полезного принес? Что ты им хотел сказать?

Информация

В рейтинге
1 012-й
Зарегистрирован
Активность