Skip to content

Popover (експеримент) ​

ЕКСПЕРИМЕНТ

Постачається як підшлях orio-ui/experiments, а не як компонент з автоімпортом. Назви, пропси й поведінка можуть змінитися в будь-якому релізі, навіть у патчі. Використовуйте <orio-popover>, коли потрібна стабільність.

Поповер на основі нативного HTML Popover API — без телепорту, без вимірювання позицій; закриття кліком поза ним і Esc обробляє браузер.

Живе демо ​

Два екземпляри, щоб перевірити, що кожна панель прив'язується до свого тригера:

Вміст нативного поповера.

Клікніть поза ним або натисніть Esc, щоб закрити.

Прив'язаний до власного тригера.

Без слотів, щоб показати значення за замовчуванням — тригер <orio-button>, панель із поверхнею, поява з прозорістю та зсувом:

Test popover

flip="initial" зберігає бік, на якому панель відкрилася, навіть коли прокручування виштовхує її з поля зору:

Test popover

Використання ​

Експерименти ніколи не імпортуються автоматично. Імпортуйте з підшляху:

vue
<script setup lang="ts">
import { Popover } from "orio-ui/experiments";
</script>

<template>
  <Popover>
    <template #trigger="triggerProps">
      <orio-button v-bind="triggerProps">Open</orio-button>
    </template>

    <template #body="bodyProps">
      <div v-bind="bodyProps">
        <p>Your content here</p>
      </div>
    </template>
  </Popover>
</template>

Обидва набори слотів треба розгорнути на справжній елемент — саме пара popovertarget / id пов'язує тригер із панеллю.

Демо на цій сторінці використовують <orio-x-popover> — глобальний псевдонім лише для документації того самого компонента.

Пропси ​

ПропТипЗа замовчуваннямОпис
idstringuseId()Id, спільний для тригера й панелі; має бути унікальним
positionPopoverPosition'bottom'Розміщення як CSS-значення position-area
gapnumber0.25Відстань між тригером і панеллю, у rem
flipPopoverFlip'auto'Наскільки наполегливо панель тримається в межах в'юпорту

Значення позиції ​

Одне ключове слово охоплює поперечну вісь, тож bottom центрує панель під тригером:

  • По центру: 'top', 'bottom', 'left', 'right'
  • Вирівняні за краєм: 'bottom span-right' (лівий край панелі збігається з краєм тригера), 'bottom span-left', 'top span-right', 'top span-left', 'left span-top', 'left span-bottom', 'right span-top', 'right span-bottom'
  • Кутові клітинки: 'top left', 'top right', 'bottom left', 'bottom right'

Об'єднання навмисно закрите, а не Property.PositionArea з csstype — той тип приймає будь-який рядок, а це значення підставляється в тег <style>.

Слоти ​

СлотПропсиОпис
trigger{ popovertarget }Елемент-тригер
body{ id, class, popover }Панель поповера

Значення за замовчуванням ​

Без слотів компонент уже рендерить щось придатне до використання:

  • Тригер — <orio-button>, тож одразу відповідає дизайн-системі.
  • Поверхня — фон панелі, рамка, заокруглення, відступи й тінь з токенів теми.
  • Рух — поява з прозорістю й зсувом від тригера через @starting-style і transition-behavior: allow-discrete. Враховує prefers-reduced-motion.

Усе це генерується всередині :where(), що має нульову специфічність, — один ваш клас перекриває будь-що з цього без !important:

vue
<template #body="bodyProps">
  <div v-bind="bodyProps" class="my-panel">…</div>
</template>

<style scoped>
.my-panel {
  background: var(--color-accent-soft);
  padding: 1.5rem;
  --popover-enter-distance: 0; /* kill the slide, keep the fade */
}
</style>

Перевертання біля країв ​

Якщо flip не дорівнює 'off', панель генерує position-try-fallbacks, тож браузер пробує інше розміщення, коли панель вийшла б за в'юпорт: спершу по блоковій осі для top/bottom, по рядковій — для left/right, потім по обох.

Значення flip ​

ЗначенняПеревертається під час відкриттяПеревертається, поки відкрита
'auto' (за замовчуванням)тактак — перевизначається після зупинки прокручування та зміни розміру
'initial'такні — бік фіксується, щойно панель відкрилася
'off'ніні — завжди рендериться з запитаного боку

'initial' — це поведінка чистого CSS: браузер генерує position-try-fallbacks і застосовує їх під час розкладки. 'auto' додає непомітне перевідкриття, описане нижче.

Навіщо 'auto' потрібен трюк ​

Браузер обирає запасний варіант лише під час розкладки панелі — тобто коли вона відкривається. Поки панель відкрита, вона стежить за тригером, але обраний бік «липкий»: панель, що відкрилася під тригером, а потім її прокрутили до краю в'юпорту, лишається знизу й виходить за межі. За вимірюваннями в Chrome 151 примусова інвалідація стилів не допомагає; перезапис position-area і перемикання position-try-fallbacks не змінюють обраного боку. Перевизначає його лише нова розкладка в момент показу.

Тож flip="auto" закриває й знову відкриває панель, щоб викликати нову розкладку. Підміна непомітна:

  • приховування й показ відбуваються в одній задачі, тож жодного кадру із закритою панеллю не малюється — виміряно нуль прихованих кадрів;
  • переходи на час підміни вимикаються, тож анімація відкриття не програється вдруге;
  • нативна пара beforetoggle/toggle поглинається, тож слухачі на панелі не бачать хибного закриття/відкриття;
  • фокус і власні зсуви прокручування панелі відновлюються, бо приховування поповера повертає фокус ініціатору, а зміна display скинула б scrollTop.

Це відбувається після зупинки прокручування (через 100 мс після останньої події scroll) і під час зміни розміру, і лише коли панель справді виходить за в'юпорт — одна перерозкладка на жест, а не на кожен кадр. Перейдіть на flip="initial", якщо хочете, щоб панель зберігала бік, на якому відкрилася.

Порівняння з <orio-popover> ​

<orio-popover><orio-x-popover>
Розміщеннявимірюється в JS, автоперевертанняCSS anchor positioning, position-try-fallbacks
Переміщення під час прокручуванняслухачі у фазі перехопленнякомпоновник стежить за якорем, без JS
Повторне перевертання під час прокручуваннятак, перевимірювання на кожну подіютак, через непомітне перевідкриття після зупинки прокручування
Накладаннятелепорт + z-index: 999999нативний top layer
Закриття кліком позавласна реалізація click-outsideбраузер
Закриття через Escнібраузер
Стан відкриттявнутрішній ref + слот-проп toggleDOM (popovertarget)

Не реалізовано ​

  • position-visibility — панель лишається на місці, коли її тригер прокручують за межі видимості, замість того щоб ховатися разом із ним
  • Керування фокусом (popover="auto" — не діалог, тож фокус не утримується)
  • aria-expanded / aria-haspopup у наборі тригера
  • Перевірка підтримки CSS anchor positioning і @starting-style браузером — де їх немає, панель повертається до нативної позиції по центру й з'являється без анімації