This media is not supported in your browser
VIEW IN TELEGRAM
#codepen дня
Яркий пример работы UI/UX сообществ.
1. Один популярный в iOS UX-сообществе автор, Робера Гелера, выдвигает идею. Например, индикатор pull-to-refresh совсем необязательно делать чем-то вроде спиннера или капли, как сейчас везде. Ну, типа, зачем это делать, когда есть скроллбар? Ну и делает пример на SwiftUI. Именно его я прикладываю к посту.
2. Потом приходит второй, популярный уже во frontend-кругах (ну Джей, конечно, кто же ещё), и говорит, что это можно легко сделать на CSS и JS.
3. Пока Джей телится с кодпеном, выходит Альваро Монторо и достаёт из широких штанин свой: https://codepen.io/alvaromontoro/pen/JjzEKWG
4. Спустя несколько часов Джей, наконец, прикладывает свой вариант: https://codepen.io/jh3y/pen/gOEgxbd
Грубо говоря, там, конечно, не настоящие скроллбары, а их имитация на SVG, анимированная через GSAP и ScrollTrigger.
Но! В скором времени нас ждёт Scroll-Driven Animations API и можно будет сделать всё то же самое. С нетерпением ждём.
Бросайте вызов устоявшимся практикам, котаны!
#css #ui #gsap #noscript
Яркий пример работы UI/UX сообществ.
1. Один популярный в iOS UX-сообществе автор, Робера Гелера, выдвигает идею. Например, индикатор pull-to-refresh совсем необязательно делать чем-то вроде спиннера или капли, как сейчас везде. Ну, типа, зачем это делать, когда есть скроллбар? Ну и делает пример на SwiftUI. Именно его я прикладываю к посту.
2. Потом приходит второй, популярный уже во frontend-кругах (ну Джей, конечно, кто же ещё), и говорит, что это можно легко сделать на CSS и JS.
3. Пока Джей телится с кодпеном, выходит Альваро Монторо и достаёт из широких штанин свой: https://codepen.io/alvaromontoro/pen/JjzEKWG
4. Спустя несколько часов Джей, наконец, прикладывает свой вариант: https://codepen.io/jh3y/pen/gOEgxbd
Грубо говоря, там, конечно, не настоящие скроллбары, а их имитация на SVG, анимированная через GSAP и ScrollTrigger.
Но! В скором времени нас ждёт Scroll-Driven Animations API и можно будет сделать всё то же самое. С нетерпением ждём.
Бросайте вызов устоявшимся практикам, котаны!
#css #ui #gsap #noscript
👍26❤5🤩3
This media is not supported in your browser
VIEW IN TELEGRAM
#баг дня
Кастомные скроллбары — штука прекрасная, хоть за 20 лет так и не нашедшая свой путь в стандарты. До сих пор полноценная стилизация возможна только в Blink aka Chromium и WebKit aka Safari.
В Firefox как не работало, так и не работает.
К слову, хорошая статья Ахмада Шадида на тему: https://ishadeed.com/article/custom-scrollbars-css/
Короче, суть бага: если вы решились таки стилизовать скроллбар, то не рассчитывайте, что Safari правильно применит стили по наведению мыши :)
Я, правда, не знаю, кому может прийти в голову менять стили скроллбара по hover.
Раз сработает, два сработает, а на третий — всё, нет. Смотрим видео, собственно.
Решение от Брамуса Ван-Дамма довольно забавное: нужно стриггерить инвалидацию стилей, для чего можно рандомной CSS-переменной присвоить неопределённое значение. Вот так:
Странные вайбы такие... IE-шные.
Короче, как будет ссылка на официальный багтрекер — закину.
Upd. Ссылка на багтрекер: https://bugs.webkit.org/show_bug.cgi?id=267575
#css #scrollbar #safari #bug
Кастомные скроллбары — штука прекрасная, хоть за 20 лет так и не нашедшая свой путь в стандарты. До сих пор полноценная стилизация возможна только в Blink aka Chromium и WebKit aka Safari.
В Firefox как не работало, так и не работает.
К слову, хорошая статья Ахмада Шадида на тему: https://ishadeed.com/article/custom-scrollbars-css/
Короче, суть бага: если вы решились таки стилизовать скроллбар, то не рассчитывайте, что Safari правильно применит стили по наведению мыши :)
Я, правда, не знаю, кому может прийти в голову менять стили скроллбара по hover.
Раз сработает, два сработает, а на третий — всё, нет. Смотрим видео, собственно.
Решение от Брамуса Ван-Дамма довольно забавное: нужно стриггерить инвалидацию стилей, для чего можно рандомной CSS-переменной присвоить неопределённое значение. Вот так:
.section:hover {
--force-rerender: ;
}
Странные вайбы такие... IE-шные.
Короче, как будет ссылка на официальный багтрекер — закину.
Upd. Ссылка на багтрекер: https://bugs.webkit.org/show_bug.cgi?id=267575
#css #scrollbar #safari #bug
👍8❤2
#статья дня
Скорее даже заметка от Софии Валитовой о порядке применения трансформаций в CSS, но концентрация знаний зашкаливает. В чём же весь вопрос?
Как вы можете помнить, не так давно все современные движки браузеров стали поддерживать определение трансформаций не только в правиле transform, но и в индивидуальных. То есть:
может быть записано как:
Но так ли это на самом деле? И да и нет. В первом случае порядок важен, во втором — нет, он всегда будет
Подробнее — в заметке Софии. Вообще у неё очень много полезного выходит, глубоко копает в спеки.
#css #transform #бородач
Скорее даже заметка от Софии Валитовой о порядке применения трансформаций в CSS, но концентрация знаний зашкаливает. В чём же весь вопрос?
Как вы можете помнить, не так давно все современные движки браузеров стали поддерживать определение трансформаций не только в правиле transform, но и в индивидуальных. То есть:
transform: translateX(20px) rotate(40deg) scale(1.5);
может быть записано как:
translate: 20px;
rotate: 40deg;
scale: 1.5;
Но так ли это на самом деле? И да и нет. В первом случае порядок важен, во втором — нет, он всегда будет
translate👉rotate👉scale. И, естественно, повторяющиеся правила будут схлопнуты. Подробнее — в заметке Софии. Вообще у неё очень много полезного выходит, глубоко копает в спеки.
#css #transform #бородач
👍13
#такое дня
А давайте, а давайте я вам расскажу про две драмы, произошедшие во фронтенд-сообществе на днях!
Сначала у Дэна Абрамова бомбануло от того, что люди не понимают, что такое RSC aka React Server Components.
Цитата: "React Server Components does not require a server (and never has). i get that this is confusing but i’m at loss what to do. people keep repeating that it does and speak about it as if it were a true fact. can someone spread the word pls?"
Суть в том, что слово Server в RSC не означает необходимость наличия сервера. Нейминг от бога просто.
Там шикарные треды, кстати, затрагивающие историю React вплоть до нечто под названием XHP, подрались по HTMX и сигналам. Короче, вообще не скучно.
Кончилось тем, что Даня Наше Всё пообещал статьи по RSC. Ждём.
Ну а вторая драма чуть менее весёлая.
Есть такой фронтенд-ютубер — Theo Rants. И вот он выкатил видео по поводу вынесенной в иллюстрации штучки: https://youtu.be/cH8VbLM1958
Казалось бы, ну тут-то где драма? А всё просто. В качестве доказательств он привёл обзор проекта уже знакомого нам (даже лично) Ахмада Шадида — https://defensivecss.dev/
Но не упомянул его имя, вообще. Когда ему указали на это — он забанил указавшего :)
А потом указал без имени, просто поставив ссылку. Имена остальных экспертов под видео имеются.
Ахмад немного раздул скандал и теперь всё в порядке. Но осадок остался.
Я догадываюсь о причине произошедшего, но, пожалуй, воздержусь от озвучивания.
Не будьте токсичными, котаны :)
А давайте, а давайте я вам расскажу про две драмы, произошедшие во фронтенд-сообществе на днях!
Сначала у Дэна Абрамова бомбануло от того, что люди не понимают, что такое RSC aka React Server Components.
Цитата: "React Server Components does not require a server (and never has). i get that this is confusing but i’m at loss what to do. people keep repeating that it does and speak about it as if it were a true fact. can someone spread the word pls?"
Суть в том, что слово Server в RSC не означает необходимость наличия сервера. Нейминг от бога просто.
Там шикарные треды, кстати, затрагивающие историю React вплоть до нечто под названием XHP, подрались по HTMX и сигналам. Короче, вообще не скучно.
Кончилось тем, что Даня Наше Всё пообещал статьи по RSC. Ждём.
Ну а вторая драма чуть менее весёлая.
Есть такой фронтенд-ютубер — Theo Rants. И вот он выкатил видео по поводу вынесенной в иллюстрации штучки: https://youtu.be/cH8VbLM1958
Казалось бы, ну тут-то где драма? А всё просто. В качестве доказательств он привёл обзор проекта уже знакомого нам (даже лично) Ахмада Шадида — https://defensivecss.dev/
Но не упомянул его имя, вообще. Когда ему указали на это — он забанил указавшего :)
А потом указал без имени, просто поставив ссылку. Имена остальных экспертов под видео имеются.
Ахмад немного раздул скандал и теперь всё в порядке. Но осадок остался.
Я догадываюсь о причине произошедшего, но, пожалуй, воздержусь от озвучивания.
Не будьте токсичными, котаны :)
👍12❤1
#тип дня
Попробую вместо тега #фишка внести что-то новое для TypeScript. Пусть будет "тип", а там посмотрим.
И сегодня на повестке дня запрет определённых ключей при передаче объекта. Например, контекста в трекинге или тех же пропсов в React.
Как правило, контекст в трекинге передаётся в функцию-хелпер, а после — дополняется какими-то переменными среды. Как-то так:
Понятное дело, нам совсем неохота, чтобы кто-то случайно передал в контекст type, userId или env и затёр всё нафиг.
"Погоди, так разверни контекст повыше", — скажет кто-то умный.
Ну так тоже не стоит, мы не хотим сюрпризов уже на разбореполётов логов в случае инцидента, когда ожидалось одно, а на выходе — другое.
Проще сразу не дать сделать странное, как минимум на этапе введения контекста. Наш вариант в итоге стал таким:
Ну и конечно, ссылка на песочницу: пуньк.
Есть предложения получше, котаны? Ну кроме проверки в рантайме :)
#typenoscript #ts #type
Попробую вместо тега #фишка внести что-то новое для TypeScript. Пусть будет "тип", а там посмотрим.
И сегодня на повестке дня запрет определённых ключей при передаче объекта. Например, контекста в трекинге или тех же пропсов в React.
Как правило, контекст в трекинге передаётся в функцию-хелпер, а после — дополняется какими-то переменными среды. Как-то так:
function logEvent(severity: LogSeverity = "info", context: LogContext = {}) {
log(severity, {
type: "event",
userId: "rick@ro.ll",
env: "dev",
...context,
})
}
Понятное дело, нам совсем неохота, чтобы кто-то случайно передал в контекст type, userId или env и затёр всё нафиг.
"Погоди, так разверни контекст повыше", — скажет кто-то умный.
Ну так тоже не стоит, мы не хотим сюрпризов уже на разборе
Проще сразу не дать сделать странное, как минимум на этапе введения контекста. Наш вариант в итоге стал таким:
type LogContextDefaults = "type" | "userId" | "env";
type LogContext = {
[key: string]: string;
} & {
[key in LogContextDefaults]?: never;
}
Ну и конечно, ссылка на песочницу: пуньк.
Есть предложения получше, котаны? Ну кроме проверки в рантайме :)
#typenoscript #ts #type
❤15👍1🤩1
#статья дня
Давно не было интерактивных статей от кого-то, кроме Шадида и Комо :)
Время исправлять.
Сегодня — статья про концепцию и варианты реализации Drag&Drop-интерфейсов от Амита Пателя и Red Blob Games: https://www.redblobgames.com/making-of/draggable/
Давайте сразу: Red Blob Games это не компания, это сайт, собственно, Амита, где он объясняет используемые в играх и интерфейсах концепции: от поиска пути и генерации шестиугольной карты, до простейшего ИИ для игр.
Так вот, возвращаясь к статье: рассмотрены варианты реализации на мыши, тач-девайсах, скроллинг и различные сопутствующие проблемы (ну чтобы вам не пришлось разбираться, как, например, отключить выделение текста, пока тащишь элемент).
Прекрасная статья и проект вообще, моя горячая рекомендация.
#javanoscript #games #drag #drop
Давно не было интерактивных статей от кого-то, кроме Шадида и Комо :)
Время исправлять.
Сегодня — статья про концепцию и варианты реализации Drag&Drop-интерфейсов от Амита Пателя и Red Blob Games: https://www.redblobgames.com/making-of/draggable/
Давайте сразу: Red Blob Games это не компания, это сайт, собственно, Амита, где он объясняет используемые в играх и интерфейсах концепции: от поиска пути и генерации шестиугольной карты, до простейшего ИИ для игр.
Так вот, возвращаясь к статье: рассмотрены варианты реализации на мыши, тач-девайсах, скроллинг и различные сопутствующие проблемы (ну чтобы вам не пришлось разбираться, как, например, отключить выделение текста, пока тащишь элемент).
Прекрасная статья и проект вообще, моя горячая рекомендация.
#javanoscript #games #drag #drop
👍23
#фишка дня
Мне снилось, что есть такой сайт, где описаны все виды центрирования контента в CSS...
Я проснулся — а его нет. Зато есть вот что: https://codepen.io/alinaki/pen/VwqXKOL
Здесь собраны шесть вариантов центрирования контента на флексах и гридах и наглядно показана разница между🤦♀️
В общем, не зря утро прошло.
#css #grid #flex #center #бородач
Мне снилось, что есть такой сайт, где описаны все виды центрирования контента в CSS...
Я проснулся — а его нет. Зато есть вот что: https://codepen.io/alinaki/pen/VwqXKOL
Здесь собраны шесть вариантов центрирования контента на флексах и гридах и наглядно показана разница между
place-content и place-items в разных контекстах! Я, честно, эти правила толком и не использовал... стыд и позор В общем, не зря утро прошло.
#css #grid #flex #center #бородач
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13
#фишка дня
Вроде как все в курсе, что в CSS есть функция filter(). С её помощью мы делаем размытие, дёргаем яркость... тень, опять же!
Но она умеет больше, намного больше. Точнее, умеют фильтры SVG, а функция — на них ссылаться:
Например, используя фильтр рассеянного света — feDiffuseLighting — можно превратить круг в шар: https://codepen.io/alinaki/pen/OJqmdEJ
А можно сымитировать бумагу, как в примере от Сары Суйедан: https://codepen.io/SaraSoueidan/pen/oppeJM/adc57533be256b5148d5924a5dbd46a3
Или угареть и применить карту смещений: https://www.smashingmagazine.com/2021/09/deep-dive-wonderful-world-noscript-displacement-filtering/
SVG-фильтры в принципе позволяют добиться эффекта, похожего на карту нормалей в 3D-графике, но без, собственно, применения WebGL. Иногда этого достаточно.
Спека по ним великолепная просто: https://www.w3.org/TR/2002/CR-SVG11-20020430/filters.html
Хотя, если честно, тяжеловаты они, GPU c WebGL справляется получше. Да и поддержка на iOS так себе.
#css #filter #noscript
Вроде как все в курсе, что в CSS есть функция filter(). С её помощью мы делаем размытие, дёргаем яркость... тень, опять же!
Но она умеет больше, намного больше. Точнее, умеют фильтры SVG, а функция — на них ссылаться:
filter: url("filters.noscript#filterId");Например, используя фильтр рассеянного света — feDiffuseLighting — можно превратить круг в шар: https://codepen.io/alinaki/pen/OJqmdEJ
А можно сымитировать бумагу, как в примере от Сары Суйедан: https://codepen.io/SaraSoueidan/pen/oppeJM/adc57533be256b5148d5924a5dbd46a3
Или угареть и применить карту смещений: https://www.smashingmagazine.com/2021/09/deep-dive-wonderful-world-noscript-displacement-filtering/
SVG-фильтры в принципе позволяют добиться эффекта, похожего на карту нормалей в 3D-графике, но без, собственно, применения WebGL. Иногда этого достаточно.
Спека по ним великолепная просто: https://www.w3.org/TR/2002/CR-SVG11-20020430/filters.html
Хотя, если честно, тяжеловаты они, GPU c WebGL справляется получше. Да и поддержка на iOS так себе.
#css #filter #noscript
👍11❤10
#фишка дня от Гарри Робертса aka csswizardry.com
Когда-то очень давно поднялся вопрос красивого обтекания картинок. И появилась такая вещь:
Естественно, IE эту штуку не поддерживал вообще никогда, да и в целом современное направление фронтенда больше идёт в сторону приложений и блочной структуры, нежели документов с иллюстрациями.
Тем не менее, иногда надо. И поэтому, спешу напомнить:
Если коротко, shape-outside позволяет описать контуры объекта, чтобы текст обтекал его именно по этим контурам. Более того, браузерам не нужен контур, они могут определить его самостоятельно по прозрачным областям PNG!
Вот только в Firefox требуется насильный репейнт... будем думать, как победить. А Safari чуть иначе определяет форму по альфа-каналу, нежели Chrome.
Upd. Важное дополнение! Чтобы правило shape-outside работало адекватно, не прячьте изображения в блоки; Firefox сходит с ума.
Ну и кодпен, конечно же: https://codepen.io/alinaki/pen/WNLVGae
Важный момент, это правило
Ну а статья, откуда взята фишка, вот: https://csswizardry.com/2023/07/the-http1liness-of-http2/
Весьма интересный обзор проблем протокола HTTP/2, если что.
#css #shape #img #бородач
Когда-то очень давно поднялся вопрос красивого обтекания картинок. И появилась такая вещь:
shape-outside.Естественно, IE эту штуку не поддерживал вообще никогда, да и в целом современное направление фронтенда больше идёт в сторону приложений и блочной структуры, нежели документов с иллюстрациями.
Тем не менее, иногда надо. И поэтому, спешу напомнить:
shape-outside нынче очень хорошо поддерживается и если вам нужно оформлять тексты, книги и статьи — самое время!Если коротко, shape-outside позволяет описать контуры объекта, чтобы текст обтекал его именно по этим контурам. Более того, браузерам не нужен контур, они могут определить его самостоятельно по прозрачным областям PNG!
Вот только в Firefox требуется насильный репейнт... будем думать, как победить. А Safari чуть иначе определяет форму по альфа-каналу, нежели Chrome.
Upd. Важное дополнение! Чтобы правило shape-outside работало адекватно, не прячьте изображения в блоки; Firefox сходит с ума.
Ну и кодпен, конечно же: https://codepen.io/alinaki/pen/WNLVGae
Важный момент, это правило
shape-margin, без него текст начинает прилипать к объекту.Ну а статья, откуда взята фишка, вот: https://csswizardry.com/2023/07/the-http1liness-of-http2/
Весьма интересный обзор проблем протокола HTTP/2, если что.
#css #shape #img #бородач
👍6❤1
This media is not supported in your browser
VIEW IN TELEGRAM
#codepen дня
Ну что, кажется, настало то время, когда для стилизации радиокнопок и чекбоксов не нужно больше изгаляться с
Итак, смотрим на пример от Джона Кантнера: https://codepen.io/alinaki/pen/ExMXbqz
1. Для начала, обнуляем все браузерные стили и предположения браузера об внешнем виде радиокнопок вообще через
2. Я вам этого не говорил, но, технически, уже давно можно на поля ввода накладывать псевдоэлементы. Но не на select. Я всё хочу написать большой пост про реализацию select, пока вот так.
Благодаря этой возможности, собственно, можно стилизовать чекбокс как душе угодно:
3. Освободившиеся ресурсы мозга и тот факт, что теперь все элементы красиво вложены в label (как минимум, не нужны for и id), можно отправить на реализацию разных эффектов.
Например, проверить, есть ли лейбл с выделенным чекбоксом и подвинуть к нему рамку:
Обратите внимание, рамка — отдельный элемент, к ней обращаемся через селектор низлежащих соседей ~.
Не знаю, что меня больше впечатляет. Псевдоэлементы на полях ввода или :has.
И да, поддерживается везде.
#css #has #appearance
Ну что, кажется, настало то время, когда для стилизации радиокнопок и чекбоксов не нужно больше изгаляться с
input:checked+i. Это освобождает мозг и руки для более приятных вещей.Итак, смотрим на пример от Джона Кантнера: https://codepen.io/alinaki/pen/ExMXbqz
1. Для начала, обнуляем все браузерные стили и предположения браузера об внешнем виде радиокнопок вообще через
appearance: none.2. Я вам этого не говорил, но, технически, уже давно можно на поля ввода накладывать псевдоэлементы. Но не на select. Я всё хочу написать большой пост про реализацию select, пока вот так.
Благодаря этой возможности, собственно, можно стилизовать чекбокс как душе угодно:
::before, ::after, :checked::before, :checked::after... В целом, лично я бы обошёлся радиальным градиентом и одним псевдоэлементом.3. Освободившиеся ресурсы мозга и тот факт, что теперь все элементы красиво вложены в label (как минимум, не нужны for и id), можно отправить на реализацию разных эффектов.
Например, проверить, есть ли лейбл с выделенным чекбоксом и подвинуть к нему рамку:
label:nth-of-type(2):has(input[type="radio"]:checked) ~ .selection {
transform: translateY(100%);
}
Обратите внимание, рамка — отдельный элемент, к ней обращаемся через селектор низлежащих соседей ~.
Не знаю, что меня больше впечатляет. Псевдоэлементы на полях ввода или :has.
И да, поддерживается везде.
#css #has #appearance
👍27❤3
#фишка дня
Что, котан, поймал ошибку CORS aka Cross-origin resource sharing error? В переводе на русский — ошибка совместного использования ресурсов между разными источниками.
Поздравляю, ты только что перешёл на следующий уровень в разработке.
В Википедии очень простое и понятное объяснение, что это такое. А нам же с тобой нужно как-то эту проблему обойти, пусть и временно.
Весь прикол ситуации в том, что отношения между сервером и браузером построены на доверии одного другому. CORS нужен не для защиты сервера, а для защиты клиента.
Кстати, именно поэтому первым решением проблемы с CORS могут быть специальные прокси: https://nordicapis.com/10-free-to-use-cors-proxies/
Если хотите, я подробнее потом о них расскажу. И о CORS тоже.
Но что делать если использование прокси не представляется возможным?
Всё просто, открываем Chrome DevTools и меняем заголовок Access-Control-Allow-Origin на *, нажав на карандашик рядом.
Естественно, таким образом можно менять и добавлять любые заголовки.
А ещё можно создать файлик с заранее прописанными заголовками — .headers — и включить его как правило через Add overrrde rule в Sources 👉 Overrides.
Всем безоблачной разработки, котаны!
#javanoscript #network #cors
Что, котан, поймал ошибку CORS aka Cross-origin resource sharing error? В переводе на русский — ошибка совместного использования ресурсов между разными источниками.
Поздравляю, ты только что перешёл на следующий уровень в разработке.
В Википедии очень простое и понятное объяснение, что это такое. А нам же с тобой нужно как-то эту проблему обойти, пусть и временно.
Весь прикол ситуации в том, что отношения между сервером и браузером построены на доверии одного другому. CORS нужен не для защиты сервера, а для защиты клиента.
Кстати, именно поэтому первым решением проблемы с CORS могут быть специальные прокси: https://nordicapis.com/10-free-to-use-cors-proxies/
Если хотите, я подробнее потом о них расскажу. И о CORS тоже.
Но что делать если использование прокси не представляется возможным?
Всё просто, открываем Chrome DevTools и меняем заголовок Access-Control-Allow-Origin на *, нажав на карандашик рядом.
Естественно, таким образом можно менять и добавлять любые заголовки.
А ещё можно создать файлик с заранее прописанными заголовками — .headers — и включить его как правило через Add overrrde rule в Sources 👉 Overrides.
Всем безоблачной разработки, котаны!
#javanoscript #network #cors
❤23
#такое дня
Январь — время заполнения годовых Performance Review aka оценки эффективности работы.
Я, честно, в русскоязычном пространстве не работал в компаниях, где они проводились бы. Как в вашей компании это называется?
Как правило, степеньупоротости глубины проработки проблем в отчётах зависит от... да ни от чего она не зависит. Бывает, что в компании на 10 человек проводится оценка по Методу 360 градусов, а бывает, что в огромной корпорации опираются только на число закрытых PR-ов и мнение менеджера.
В любом случае, очень часто приходится писать отчёт на самого себя. С одной стороны, сам себя не похвалишь — никто не похвалит, с другой — почти все мы подвержены синдрому самозванца.
Сегодня я услышал интересное мнение:
— Я просто везде отметил «выше ожиданий» и в комментарии попросил менеджера объяснить, почему он так не считает.
Позиция, как минимум, смелая. Она точно лучше чем отчёт, котором просто бы стояло «Соответствую ожиданиям» и всё.
Но насколько ж сильна вера в менеджера...
В любом случае, мне интересно, проходят ли и как подобные раунды оценки в ваших компаниях?
#work #review
Январь — время заполнения годовых Performance Review aka оценки эффективности работы.
Я, честно, в русскоязычном пространстве не работал в компаниях, где они проводились бы. Как в вашей компании это называется?
Как правило, степень
В любом случае, очень часто приходится писать отчёт на самого себя. С одной стороны, сам себя не похвалишь — никто не похвалит, с другой — почти все мы подвержены синдрому самозванца.
Сегодня я услышал интересное мнение:
— Я просто везде отметил «выше ожиданий» и в комментарии попросил менеджера объяснить, почему он так не считает.
Позиция, как минимум, смелая. Она точно лучше чем отчёт, котором просто бы стояло «Соответствую ожиданиям» и всё.
Но насколько ж сильна вера в менеджера...
В любом случае, мне интересно, проходят ли и как подобные раунды оценки в ваших компаниях?
#work #review
❤8👍4🤩1
This media is not supported in your browser
VIEW IN TELEGRAM
#codepen дня
Давно собирался написать игру по Гарри Поттеру, но не знал, как?
Не расстраивайся, я принёс решение! Steve Gardner и его прекрасное создание Spell Caster!
Вот: https://codepen.io/ste-vg/full/zYerxoR
Я обожаю подобные примеры. Игра буквально укладывается в несколько экранов кода, но красива до безобразия.
Из технологий — Three.js и стейт-машина Stately, позволяющая описывать состояния приложения через удобные диаграммы.
Много комментариев по коду, интересная реализация распознавания жестов. Хоть сейчас бери и в магазин выкладывай :)
Я залип, в общем, как в игре, так и в её исходниках.
#webgl #threejs #game #бородач
Давно собирался написать игру по Гарри Поттеру, но не знал, как?
Не расстраивайся, я принёс решение! Steve Gardner и его прекрасное создание Spell Caster!
Вот: https://codepen.io/ste-vg/full/zYerxoR
Я обожаю подобные примеры. Игра буквально укладывается в несколько экранов кода, но красива до безобразия.
Из технологий — Three.js и стейт-машина Stately, позволяющая описывать состояния приложения через удобные диаграммы.
Много комментариев по коду, интересная реализация распознавания жестов. Хоть сейчас бери и в магазин выкладывай :)
Я залип, в общем, как в игре, так и в её исходниках.
#webgl #threejs #game #бородач
👍10
#тип дня
Типом дня назначается вон тот в кожаной куртке. Да-да, о тебе говорю!
TL;DR: вычисленный тип функции с дженерик-аргументом можно сузить, декларируя тип как const.
Кроме шуток, разве тебя не бесит, что указываешь вот дженерик тип аргумента функции, производишь манипуляции над переданным объектом — а в ответ тебе вычисленный базовый тип?
Непонятно? Давай так:
Какой вычисленный тип получат first и second?
Очевидно, такой:
Но... это же довольно неудобно. А если мы схему валидируем, например? Или форму описываем. Или тесты пишем в конце-концов. Почему при заранее известном константном типе входных данных мы получаем вычисленную строку?
К счастью, начиная с TypeScript 5.0 можно объявлять дженерики как константные типы! Иначе говоря, работать с максимально узкими (narrow) типами. Это, собственно, аналог указания as const у предопределённой константы.
Использование простое:
И на выходе тип у first и second будет такой:
Песочница: пуньк.
Давайте исходя из полученной информации создадим валидатор по известной схеме данных и дальше поработаем с ним:
Определение getSurname приобретёт такой вид:
То есть, обратите внимание, не просто строку на вход, а конкретные, существующие в нужных параметрах строки.
Теперь это вполне себе напоминает удобную работу с коллекциями данных, не стыдно и форму провалидировать.
Песочница: пуньк.
Штука относительно новая, к ней ещё придётся немного привыкнуть. Но уже можно пробовать применять.
Ах да, ссылка на соответствующий PR в TypeScript: https://github.com/microsoft/TypeScript/pull/51865
#typenoscript #generic #const
Типом дня назначается вон тот в кожаной куртке. Да-да, о тебе говорю!
TL;DR: вычисленный тип функции с дженерик-аргументом можно сузить, декларируя тип как const.
Кроме шуток, разве тебя не бесит, что указываешь вот дженерик тип аргумента функции, производишь манипуляции над переданным объектом — а в ответ тебе вычисленный базовый тип?
Непонятно? Давай так:
function wrapNames <T>(names:T[]) {
return names.map<{name: T}>(name => ({name}));
}
const [first, second] = wrapNames(['Sergey', 'Alex'])
Какой вычисленный тип получат first и second?
Очевидно, такой:
{
name: string;
}
Но... это же довольно неудобно. А если мы схему валидируем, например? Или форму описываем. Или тесты пишем в конце-концов. Почему при заранее известном константном типе входных данных мы получаем вычисленную строку?
К счастью, начиная с TypeScript 5.0 можно объявлять дженерики как константные типы! Иначе говоря, работать с максимально узкими (narrow) типами. Это, собственно, аналог указания as const у предопределённой константы.
Использование простое:
function wrapNames <const T>(names:T[])
И на выходе тип у first и second будет такой:
{
name: "Sergey" | "Alex";
}
Песочница: пуньк.
Давайте исходя из полученной информации создадим валидатор по известной схеме данных и дальше поработаем с ним:
// initValidator
function parse<const T extends { name: string; surname: string }>(users: T[]) {
return (name: T['name']) => users.find(u => u.name === name)?.surname;
}
// validate
const getSurname = parse([
{
name: 'Joe',
surname: 'Doe',
age: 30,
},
{
name: 'John',
surname: 'Dohn'
}
]);
Определение getSurname приобретёт такой вид:
getSurname: (name: "Joe" | "John") => string | undefined
То есть, обратите внимание, не просто строку на вход, а конкретные, существующие в нужных параметрах строки.
Теперь это вполне себе напоминает удобную работу с коллекциями данных, не стыдно и форму провалидировать.
Песочница: пуньк.
Штука относительно новая, к ней ещё придётся немного привыкнуть. Но уже можно пробовать применять.
Ах да, ссылка на соответствующий PR в TypeScript: https://github.com/microsoft/TypeScript/pull/51865
#typenoscript #generic #const
GitHub
`const` modifier on type parameters by ahejlsberg · Pull Request #51865 · microsoft/TypeScript
With this PR we implement a new const modifier for type parameters. In a function, method, or constructor invocation, when a literal expression in an argument is contextually typed by a const type ...
👍25❤3
#фишка дня
Сегодня на ваших экранах новая серия сериала "Не боги горшки обжигают".
На сей раз отличился Kent C. Dodds, которого уж никак нельзя упрекнуть в незнании фронтенда 🙂
Но давайте разбираться. Что же случилось?
Если пройти по ссылке, то можно понять, что случилась типичная для интернет-каталогов ситуация: карточка товара со ссылкой куда-нибудь ещё.
И да, если раньше всем было вообще пофигу на валидацию — рендерит и ладно — то нынче в дело вступают SSR/SSG и они не позволяют гидрацию невалидного HTML 🥁
А попасть в ситуацию такую ну просто слишком легко: достаточно обозвать пару компонентов как-то иначе, чем просто
Так что же делать-то? Псевдоэлементы к спасению!
Верстаем как обычно, вставляем просто две ссылки: одну на товар, вторую куда-нибудь ещё. И растягиваем псевдоэлемент первой на всю ширину. Как-то так: https://codepen.io/alinaki/pen/OJdJQyB
Все довольны, котята спасены!
#css #trick #card #validation #бородач
Сегодня на ваших экранах новая серия сериала "Не боги горшки обжигают".
На сей раз отличился Kent C. Dodds, которого уж никак нельзя упрекнуть в незнании фронтенда 🙂
Но давайте разбираться. Что же случилось?
Если пройти по ссылке, то можно понять, что случилась типичная для интернет-каталогов ситуация: карточка товара со ссылкой куда-нибудь ещё.
И да, если раньше всем было вообще пофигу на валидацию — рендерит и ладно — то нынче в дело вступают SSR/SSG и они не позволяют гидрацию невалидного HTML 🥁
А попасть в ситуацию такую ну просто слишком легко: достаточно обозвать пару компонентов как-то иначе, чем просто
a, и всё, потерялись. Нет, кнопки тоже нельзя. Нет, любой интерактивный контент нельзя.Так что же делать-то? Псевдоэлементы к спасению!
Верстаем как обычно, вставляем просто две ссылки: одну на товар, вторую куда-нибудь ещё. И растягиваем псевдоэлемент первой на всю ширину. Как-то так: https://codepen.io/alinaki/pen/OJdJQyB
Все довольны, котята спасены!
#css #trick #card #validation #бородач
👍7
This media is not supported in your browser
VIEW IN TELEGRAM
#фишка дня
Как заставить элемент изменять свою ширину... ступенчато?
Ну, например, вам нужно, чтобы шахматная сетка при любых условиях оставалась цельной.
И сегодня у нас аж несколько вариантов, как этого добиться.
1. От Аны Тюдор: https://codepen.io/thebabydino/pen/zYbZpBq
Используем функцию модуля:
Но вот беда, в Chrome модуль пока не поддерживается... но не унываем! Есть ещё два варианта от Темани Афифа aka CSS Challenges.
2. Эффективное значение функции выше это, буквально, округление. Но так и такая функция в CSS имеется! Как-то так:
Пример: https://codepen.io/alinaki/pen/VwRMpPY
Но... Chrome опять в обломе.
Да, и в первом и во втором вариантах на коне Firefox и Safari. Как я уже недавно писал, парни из Chrome взяли привычку реализовывать только то, что можно красиво оформить в пост. И реализовали тригонометрические функции, забыв про базу.
Потому нам ничего не остаётся кроме как...
3. CSS Grid конечно же: https://codepen.io/t_afif/pen/KKEXwmR
И секрет будет в правиле:
Всем шахмат, котаны :)
#css #trick #math
Как заставить элемент изменять свою ширину... ступенчато?
Ну, например, вам нужно, чтобы шахматная сетка при любых условиях оставалась цельной.
И сегодня у нас аж несколько вариантов, как этого добиться.
1. От Аны Тюдор: https://codepen.io/thebabydino/pen/zYbZpBq
Используем функцию модуля:
width: calc(95vmin + -1*mod(95vmin, 15px));
Но вот беда, в Chrome модуль пока не поддерживается... но не унываем! Есть ещё два варианта от Темани Афифа aka CSS Challenges.
2. Эффективное значение функции выше это, буквально, округление. Но так и такая функция в CSS имеется! Как-то так:
min-width: round(95vmin,15px);
Пример: https://codepen.io/alinaki/pen/VwRMpPY
Но... Chrome опять в обломе.
Да, и в первом и во втором вариантах на коне Firefox и Safari. Как я уже недавно писал, парни из Chrome взяли привычку реализовывать только то, что можно красиво оформить в пост. И реализовали тригонометрические функции, забыв про базу.
Потому нам ничего не остаётся кроме как...
3. CSS Grid конечно же: https://codepen.io/t_afif/pen/KKEXwmR
И секрет будет в правиле:
grid-template-columns: repeat(auto-fill, 15px);
Всем шахмат, котаны :)
#css #trick #math
👍24❤4
#статья дня
Smashing Magazine — один из старейших онлайн-журналов по веб-разработке. Статьи там как правило подбираются весьма подробные и полные, объясняющие все аспекты даже, казалось бы, простых эффектов.
Сегодня в наш канал залетает как раз такая статья: 3D-эффекты для картинок. Блеск, параллакс и поворот.
Весьма впечатляющий разбор, включающий в себя даже немного геометрических расчётов, чтоб вы понимали, откуда ноги растут.
Сразу ссылки на кодпены с примерами, чтоб далеко не ходить:
1. https://codepen.io/t_afif/pen/VwEJqKV
2. https://codepen.io/t_afif/pen/qBJyXNy
3. https://codepen.io/t_afif/pen/yLRRBKj
Ну и, конечно, сама статья: https://www.smashingmagazine.com/2023/07/shines-perspective-rotations-css-3d-effects-images/
Будет полезно не только лишь всем.
#css #3d #image #бородач
Smashing Magazine — один из старейших онлайн-журналов по веб-разработке. Статьи там как правило подбираются весьма подробные и полные, объясняющие все аспекты даже, казалось бы, простых эффектов.
Сегодня в наш канал залетает как раз такая статья: 3D-эффекты для картинок. Блеск, параллакс и поворот.
Весьма впечатляющий разбор, включающий в себя даже немного геометрических расчётов, чтоб вы понимали, откуда ноги растут.
Сразу ссылки на кодпены с примерами, чтоб далеко не ходить:
1. https://codepen.io/t_afif/pen/VwEJqKV
2. https://codepen.io/t_afif/pen/qBJyXNy
3. https://codepen.io/t_afif/pen/yLRRBKj
Ну и, конечно, сама статья: https://www.smashingmagazine.com/2023/07/shines-perspective-rotations-css-3d-effects-images/
Будет полезно не только лишь всем.
#css #3d #image #бородач
❤3👍2🤩2
#детектив дня
Сегодня в чат пришёл подписчик с забавной проблемой.
Дано: форма с двумя полями, одно просто как HTML-тег, второе — React-компонент. Ну, очевидно, компонент тоже содержит поле ввода.
При нажатию на клавишу Ввод событие onSubmit не происходит, форма не отправляется. Хотя, вроде как, всем известно, что это — стандартное поведение.
Было замечено, что при удалении React-компонента всё отлично работает. И первым предположением стало, что React что-то испортил, или песочница кривая.
Кто-то догадался сделать ванильный пример, поведение сохранилось.
То есть, вот такое:
...просто-напросто не работает. Стали копать.
И докопались до спецификации 1995 года для HTML 2.0!
Вот она: https://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2
И там написано следующее:
Перевожу: чёрным по белому сказано, что форма должна отправляться по кнопке submit (или специальной картинке, но то такое).
То есть, наше предположение о том, что форма всегда должна отправляться по нажатию Ввода — уже неверное.
Но, буквально, в следующем же абзаце:
Так-так-так, а что тут у нас? А тут написано, что единственное текстовое поле — это частный случай и нажатие клавиши ввода должно рассматриваться как отправка формы!
Удивительное рядом, короче. По всей видимости, такое поведение связано с обработкой событий focus и blur, когда полей больше одного.
Короче, так как же решить проблему и не добавлять кнопку?
Очень просто, вставить в форму следующее:
По-моему, пахнет лёгким бредом, но уж как есть.
И да, за много лет веб-разработки мне ни разу не понадобилась форма без кнопки, но расследование вышло забавным.
#html #form #submit
Сегодня в чат пришёл подписчик с забавной проблемой.
Дано: форма с двумя полями, одно просто как HTML-тег, второе — React-компонент. Ну, очевидно, компонент тоже содержит поле ввода.
При нажатию на клавишу Ввод событие onSubmit не происходит, форма не отправляется. Хотя, вроде как, всем известно, что это — стандартное поведение.
Было замечено, что при удалении React-компонента всё отлично работает. И первым предположением стало, что React что-то испортил, или песочница кривая.
Кто-то догадался сделать ванильный пример, поведение сохранилось.
То есть, вот такое:
<form onSubmit="alert('aha')">
<input type="text"/>
<input type="text"/>
</form>
...просто-напросто не работает. Стали копать.
И докопались до спецификации 1995 года для HTML 2.0!
Вот она: https://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2
И там написано следующее:
When the user indicates that the form should be submitted (using a submit button or image input), the form data set is processed according to its method, action URI and enctype.
Перевожу: чёрным по белому сказано, что форма должна отправляться по кнопке submit (или специальной картинке, но то такое).
То есть, наше предположение о том, что форма всегда должна отправляться по нажатию Ввода — уже неверное.
Но, буквально, в следующем же абзаце:
When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.
Так-так-так, а что тут у нас? А тут написано, что единственное текстовое поле — это частный случай и нажатие клавиши ввода должно рассматриваться как отправка формы!
Удивительное рядом, короче. По всей видимости, такое поведение связано с обработкой событий focus и blur, когда полей больше одного.
Короче, так как же решить проблему и не добавлять кнопку?
Очень просто, вставить в форму следующее:
<input type="submit" hidden/>
По-моему, пахнет лёгким бредом, но уж как есть.
И да, за много лет веб-разработки мне ни разу не понадобилась форма без кнопки, но расследование вышло забавным.
#html #form #submit
🤩30👍23❤4👎1
#фишка дня
Как легко и быстро добиться эффекта фаски на кнопке без использования вложенных и псевдоэлементов?
Конечно же, использовать градиентный бордер!
— Ну да, конечно, это кто делает градиентные границы без вложенных элементов?
Ну как, кто. Мы и делаем. Всё довольно просто:
1. Задаём нужную нам ширину, используя обычный прозрачный border.
2. Как только мы наложили границу, мы получили доступ к двум свойствам боксовой модели: border-box и padding-box.
Условно, border-box — это весь размер элемента, включая границу. А padding-box — всё до границы.
Итого, первый фон-градиент распространяется на контент, а второй — заходит на созданный нами прозрачный бордер.
➖ нельзя сделать прозрачную кнопку.
➕ можно использовать вообще любую картинку или тип градиента (линейный, радиальный, конический).
Пример: https://codepen.io/alinaki/pen/zYbPBEd
Кто-то готов сделать фаску ещё более выраженной? :)
#css #bevel #emboss #border
Как легко и быстро добиться эффекта фаски на кнопке без использования вложенных и псевдоэлементов?
Конечно же, использовать градиентный бордер!
— Ну да, конечно, это кто делает градиентные границы без вложенных элементов?
Ну как, кто. Мы и делаем. Всё довольно просто:
border: 0.5vw solid transparent;
background: linear-gradient(#17181c, #17181c) padding-box, linear-gradient(to right, #1e1e22, #121316, #1e1e22) border-box;
1. Задаём нужную нам ширину, используя обычный прозрачный border.
2. Как только мы наложили границу, мы получили доступ к двум свойствам боксовой модели: border-box и padding-box.
Условно, border-box — это весь размер элемента, включая границу. А padding-box — всё до границы.
Итого, первый фон-градиент распространяется на контент, а второй — заходит на созданный нами прозрачный бордер.
➖ нельзя сделать прозрачную кнопку.
➕ можно использовать вообще любую картинку или тип градиента (линейный, радиальный, конический).
Пример: https://codepen.io/alinaki/pen/zYbPBEd
Кто-то готов сделать фаску ещё более выраженной? :)
#css #bevel #emboss #border
👍16
#такое дня
Уже довольно поздно. Отметьтесь реакциями, кто ещё онлайн.
Наберём достаточное количество — скину историю смешного бага, который у нас недавно произошёл по невнимательности.
Не наберём — скину её утром. Никуда она не денется.
Upd. Хорошо разогнались. Сейчас всё будет.
Уже довольно поздно. Отметьтесь реакциями, кто ещё онлайн.
Наберём достаточное количество — скину историю смешного бага, который у нас недавно произошёл по невнимательности.
Не наберём — скину её утром. Никуда она не денется.
Upd. Хорошо разогнались. Сейчас всё будет.
🤩67👍36🤡4❤1🤬1
#баг дня
Преамбула
Некий сервис, назовём его Doodle Lampolytics, заставил всех своих клиентов переползти с модели подсчёта просмотров страниц на модель подсчёта событий.
Суть проблемы сервиса очевидна: постраничная модель слабо подходила для серьёзной аналитики данных в веб-приложениях. Даже элементарные лендинги стали гораздо сложнее, чем просто переходы по ссылкам.
И если раньше мы в нагрузку к «просмотру» передавали так называемые индивидуальные размерности и метрики, привязанные к сессии, то теперь — каждое событие может нести индивидуальный их набор.
Не в последнюю очередь это связано с ужесточением запретов на слежку за посетителями сайтов.
Поскольку в типичном проекте зачастую присутствует не одна сотня событий/страниц, то переезд сопровождается знатным перетряхиванием всей аналитики.
Суть
Суд да дело, приходим мы к идее передачи события с неким контекстом:
В одном из постов выше я уже показывал, что этот самый контекст (context) обогащает собой общие для всех событий параметры, превращая событийную модель в некое подобие сессионной.
Передаём туда, например, productId и orderId — и получаем трекинг истории конкретного продукта или заказа. Удобно, когда продукт сильно кастомизированный. Можно накинуть туда optionId и следить, какие продукты какими опциями нашпиговывают.
Таким образом, мы отсылаем объекты вида:
Потом объект из camelCase конвертируется в snake_case, как того требует документация, и дёргается некий API. Запомните этот момент.
И всё, поехали в продакшен. События уходят, счастье. Заодно, те же самые события, за некоторым исключением, можно пихать в сервисы вроде Sentry.
И всё бы ничего, но интерфейс Doodle Lampolytics настолько проклят, что оказалось намного проще выгружать данные оттуда сразу в Doodle LargeQuery и SQL-ем их запрашивать.
Но в какой-то момент времени мне это радикально надоело. Мне нравилась простота старого интерфейса, и я решил разобраться в новом. Проблема была в том, что я там не мог найти не только половину событий, но и их параметры.
Я почитал документацию, нашёл где создавать свои размерности и метрики на основе передаваемых параметров — в админке — захожу туда и наблюдаю следующую картину:
— Какого хера?! — заорал я, я же был в офисе.
Но параметры с ı вместо i исчислялись десятками, в то время как остальные — десятками тысяч. И я зачем-то успокоился. Подумал, что это косяк ламполитики, с которым можно разобраться попозже.
Но сегодня я зашёл в наш прекрасный чат, зачем-то ткнул в аватарку одного из подписчиков и увидел у него в истории некую песню с названием Bakı.
И тут до меня резко дошло, какой я идиот.
Прежде чем читать дальше, вы можете посмотреть на код функции, которой мы преобразуем camelCase в snake_case: https://github.com/jonschlinkert/snakecase/blob/master/index.js
Особо внимательные, наверное, уже догадались, в чём проблема. А ведь мы даже тесты написали.
Поскольку не все пройдут по ссылке, я приведу кусочек:
Ничего не смущает? Я подожду.
...
Да, правильно. Если забыть передать локаль (язык клиента, сервера в нашем случае), то будет взята переменная среды.
И для кого-то из наших клиентов в этой переменной оказался турецкий (или узбекский) язык.
В которых нет буквы i, вместо неё — ı.
Вы все правильно поняли, мы не полностью контролируем среду выполнения. Это так называемый Doodle AppsScript. Он исполняется на языке, в котором открыт офисный инструмент посетителя.
Естественно, мы локаль передать забыли. Хороший вопрос, почему мы вообще не проверили это поведение изначально... но тут как есть.
Стыдно? Стыдно. Зря я винил Doodle Lampolytics.
Преамбула
Некий сервис, назовём его Doodle Lampolytics, заставил всех своих клиентов переползти с модели подсчёта просмотров страниц на модель подсчёта событий.
Суть проблемы сервиса очевидна: постраничная модель слабо подходила для серьёзной аналитики данных в веб-приложениях. Даже элементарные лендинги стали гораздо сложнее, чем просто переходы по ссылкам.
И если раньше мы в нагрузку к «просмотру» передавали так называемые индивидуальные размерности и метрики, привязанные к сессии, то теперь — каждое событие может нести индивидуальный их набор.
Не в последнюю очередь это связано с ужесточением запретов на слежку за посетителями сайтов.
Поскольку в типичном проекте зачастую присутствует не одна сотня событий/страниц, то переезд сопровождается знатным перетряхиванием всей аналитики.
Суть
Суд да дело, приходим мы к идее передачи события с неким контекстом:
trackEvent('event_name', context) {}
В одном из постов выше я уже показывал, что этот самый контекст (context) обогащает собой общие для всех событий параметры, превращая событийную модель в некое подобие сессионной.
Передаём туда, например, productId и orderId — и получаем трекинг истории конкретного продукта или заказа. Удобно, когда продукт сильно кастомизированный. Можно накинуть туда optionId и следить, какие продукты какими опциями нашпиговывают.
Таким образом, мы отсылаем объекты вида:
{
productId,
optionId,
orderId,
locale,
}
Потом объект из camelCase конвертируется в snake_case, как того требует документация, и дёргается некий API. Запомните этот момент.
И всё, поехали в продакшен. События уходят, счастье. Заодно, те же самые события, за некоторым исключением, можно пихать в сервисы вроде Sentry.
И всё бы ничего, но интерфейс Doodle Lampolytics настолько проклят, что оказалось намного проще выгружать данные оттуда сразу в Doodle LargeQuery и SQL-ем их запрашивать.
Но в какой-то момент времени мне это радикально надоело. Мне нравилась простота старого интерфейса, и я решил разобраться в новом. Проблема была в том, что я там не мог найти не только половину событий, но и их параметры.
Я почитал документацию, нашёл где создавать свои размерности и метрики на основе передаваемых параметров — в админке — захожу туда и наблюдаю следующую картину:
product_id,
product_ıd,
option_id,
option_ıd
— Какого хера?! — заорал я, я же был в офисе.
Но параметры с ı вместо i исчислялись десятками, в то время как остальные — десятками тысяч. И я зачем-то успокоился. Подумал, что это косяк ламполитики, с которым можно разобраться попозже.
Но сегодня я зашёл в наш прекрасный чат, зачем-то ткнул в аватарку одного из подписчиков и увидел у него в истории некую песню с названием Bakı.
И тут до меня резко дошло, какой я идиот.
Прежде чем читать дальше, вы можете посмотреть на код функции, которой мы преобразуем camelCase в snake_case: https://github.com/jonschlinkert/snakecase/blob/master/index.js
Особо внимательные, наверное, уже догадались, в чём проблема. А ведь мы даже тесты написали.
Поскольку не все пройдут по ссылке, я приведу кусочек:
const lowercase = (input = '', options) => input.toLocaleLowerCase(options?.locale);
Ничего не смущает? Я подожду.
...
Да, правильно. Если забыть передать локаль (язык клиента, сервера в нашем случае), то будет взята переменная среды.
И для кого-то из наших клиентов в этой переменной оказался турецкий (или узбекский) язык.
В которых нет буквы i, вместо неё — ı.
Вы все правильно поняли, мы не полностью контролируем среду выполнения. Это так называемый Doodle AppsScript. Он исполняется на языке, в котором открыт офисный инструмент посетителя.
Естественно, мы локаль передать забыли. Хороший вопрос, почему мы вообще не проверили это поведение изначально... но тут как есть.
Стыдно? Стыдно. Зря я винил Doodle Lampolytics.
🤩23👍9❤3