Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fix(Flex): improve className withGaps calculation #8360

Merged
merged 11 commits into from
Mar 18, 2025

Conversation

EldarMuhamethanov
Copy link
Contributor

@EldarMuhamethanov EldarMuhamethanov commented Mar 11, 2025

  • Unit-тесты
  • Release notes

Описание

Сейчас в компоненте Flex есть проверка const withGaps = Children.count(children) > 1 && gap;, которая определяет нужно ли включать полифил gap для старых браузеров(добавляет отрицательные маржины для контейнера). Эта проверка не всегда работает корректно, так как неточно определяет сколько дочерних элементов будет к контейнера. Например, прокидывание элементов, обернутых одним Fragment. Исходя из проверки withGaps будет false, так как по сути children один. Нужно придумать как сделать корректное определение количества непосредственных детей компонента

Изменения

  • Добавил хук useWithGaps, который определяет значение withGaps на основании children у контейнера. Определяет он это по факту после отрисовки компонента. Также он отслеживает изменение количества непосредственных потомков flex-контейнера через MutationObserver
  • Чтобы не ухудшать производительность компонента в браузерах, где поддерживается нативное решение flex + gap, добавил проверку с помощью CSS.supports('(inset: 0)') - аналогичная проверка есть в css
  • Доработал хук useMutationObserver, так чтобы он принимал объект options для настройки MutationObserver
  • Из документации удалил раздел с рекомендациями не использовать Fragment, так как это больше не будет актуальным
  • Добавил тест для нового функционала

UPD

В итоге решили откатить эти изменения. Был поправлен баг с наследованием gap во вложенных Flex. Классней withGaps был выпилен. Подробнее можно прочитать в комменте

Release notes

Улучшения

@EldarMuhamethanov EldarMuhamethanov added this to the v7.2.0 milestone Mar 11, 2025
@EldarMuhamethanov EldarMuhamethanov self-assigned this Mar 11, 2025
@EldarMuhamethanov EldarMuhamethanov requested a review from a team as a code owner March 11, 2025 11:49
@github-actions github-actions bot added the ci:cherry-pick:patch Автоматизация: PR продублируется в ветку последнего минорного релиза для выпуска патча label Mar 11, 2025
Copy link
Contributor

github-actions bot commented Mar 11, 2025

size-limit report 📦

Path Size
JS 398.09 KB (-0.02% 🔽)
JS (gzip) 120.8 KB (-0.02% 🔽)
JS (brotli) 99.28 KB (-0.13% 🔽)
JS import Div (tree shaking) 1.56 KB (0%)
CSS 348.95 KB (-0.01% 🔽)
CSS (gzip) 43.23 KB (-0.01% 🔽)
CSS (brotli) 34.47 KB (+0.06% 🔺)

Copy link

codesandbox-ci bot commented Mar 11, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Copy link
Contributor

github-actions bot commented Mar 11, 2025

e2e tests

Playwright Report

Copy link
Contributor

github-actions bot commented Mar 11, 2025

👀 Docs deployed

Commit 9777934

Copy link

codecov bot commented Mar 11, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.44%. Comparing base (84d02f9) to head (a64369e).
Report is 4 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8360      +/-   ##
==========================================
+ Coverage   95.42%   95.44%   +0.01%     
==========================================
  Files         409      410       +1     
  Lines       11649    11671      +22     
  Branches     3859     3862       +3     
==========================================
+ Hits        11116    11139      +23     
+ Misses        533      532       -1     
Flag Coverage Δ
unittests 95.44% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@EldarMuhamethanov EldarMuhamethanov removed the ci:cherry-pick:patch Автоматизация: PR продублируется в ветку последнего минорного релиза для выпуска патча label Mar 11, 2025
@EldarMuhamethanov EldarMuhamethanov marked this pull request as draft March 11, 2025 13:58
@EldarMuhamethanov EldarMuhamethanov marked this pull request as ready for review March 12, 2025 07:53
Copy link
Contributor

@inomdzhon inomdzhon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Решение крутое! Если думать в масштабе, то смущает MutationOberserv – этот полифилл нужен для старых браузеров, а старый браузер у пользователя может быть по двум причинам:

  1. у него старое устройство, которое не поддерживается новыми версиями браузера
  2. осознанно не обновляет

Если говорить про п.1, то тут нужно учитывать, что старое устройство будет слабей по ресурсам. Flex может использоваться достаточно часто – помимо React, который будет хранить в памяти каждый из них в своём VDOM, память ещё будет разрастаться экземплярами MutationObserver.

Будет ещё проблема при SSR – при гидрации будет ошибка, т.к. класс withGaps будет навешиваться после 1-го рендера. Ну и reflow будет.

Вообще withGaps, если так подумать, не нужен ведь. Даже если будет один элемент, то ему можно продолжать добавлять отступы и компенсировать их на родителе. Как минимум, можно учесть кейс, что будет 0 потомков через :empty. Давай попробуем вот так решить проблему:

@supports not (inset: 0) {
  .host:not(:empty) {
    margin-block-start: calc(
      -1 * var(--vkui_internal--row_gap) + var(--vkui_internal--flex_original_margin_block)
    );
    margin-inline-start: calc(
      -1 * var(--vkui_internal--column_gap) + var(--vkui_internal--flex_original_margin_inline)
    );
  }

  /* stylelint-disable-next-line @project-tools/stylelint-atomic, selector-max-universal */
  .host.host > * {
    margin-block-start: var(--vkui_internal--row_gap);
    margin-inline-start: var(--vkui_internal--column_gap);
  }
}

@EldarMuhamethanov
Copy link
Contributor Author

Даже если будет один элемент, то ему можно продолжать добавлять отступы и компенсировать их на родителе

Этот функционал был добавлен намерено, так как исправлял баг (тут добавлен #7492), так что проверять на :empty не правильно.

Flex может использоваться достаточно часто – помимо React, который будет хранить в памяти каждый из них в своём VDOM, память ещё будет разрастаться экземплярами MutationObserver

Понимаю, что решение далеко от идеала, но других вариантов нет(во всяком случае, я не нашел)

@inomdzhon
Copy link
Contributor

inomdzhon commented Mar 14, 2025

Даже если будет один элемент, то ему можно продолжать добавлять отступы и компенсировать их на родителе

Этот функционал был добавлен намерено, так как исправлял баг (тут добавлен #7492), так что проверять на :empty не правильно.

...можно было догадаться, что withGaps не зря появился 😅 нужно было ссылку на issues оставить

этот случай нужно иначе порешать установив --vkui_internal--row_gap: 0; --vkui_internal--column_gap: 0; когда gap === undefined – заметил это только сейчас, gap не должен наследоваться

В примере #7492 видим, что у вложенного нет gap, вот так не воспроизводится:

<Flex gap="xs">
  {new Array(3).fill(3).map((i) => (
    <div
      style={{ width: '36px', height: '36px', borderRadius: '50%', border: '1px solid red' }}
      key={i}
    >
      <Flex
        align="center"
        justify="center"
        style={{
          width: '36px',
          height: '36px',
          border: '1px solid blue',
          '--vkui_internal--column_gap': '0px',
          '--vkui_internal--row_gap': '0px'
        }}
      >
        VKUI
      </Flex>
    </div>
  ))}
</Flex>

Когда у тебя во <Flex> текстовая нода, gap не нужен


PS: пример фолбека можно глянуть тут https://gavinmcfarland.github.io/flex-gap-polyfill/, в целом мы так и делаем, за исключением бага, что gap наследуется у нас

@EldarMuhamethanov
Copy link
Contributor Author

Действительно в том кейсе проблема в наследовании. Собственно, добавил в css в host сброс значений css-переменных до 0. Убрал класснейм withGaps. Остальное откатил как было. Вроде как все работает

@inomdzhon
Copy link
Contributor

думаю нужно будет упомянуть фикс в release notes, что по новой решили #7492

Copy link
Contributor

@andrey-medvedev-vk andrey-medvedev-vk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💅

@EldarMuhamethanov EldarMuhamethanov merged commit 925a474 into master Mar 18, 2025
27 checks passed
@EldarMuhamethanov EldarMuhamethanov deleted the e.muhamethanov/improve-calculation-withGaps branch March 18, 2025 10:33
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

3 participants