[feature][editor] Кнопки блока кода поверх кода: язык + копирование в углу, селектор языка — по наведению #275

Closed
opened 2026-07-01 20:19:30 +03:00 by vvzvlad · 0 comments
Owner

Проблема

В блоке кода панель управления (селектор языка + кнопка «копировать») занимает
отдельную строку над кодом и тратит на себя целую строку по вертикали. Нужно
перенести обе кнопки поверх кода, в правый верхний угол (в том же месте), а
селектор языка скрывать до наведения на блок.

Как работает сейчас

  • Компонент: apps/client/src/features/editor/components/code-block/code-block-view.tsx
    • .codeBlock (NodeViewWrapper) — flex-колонка. В DOM сначала идёт <pre> с
      кодом, затем <Group className={menuGroup}> с <Select> (язык) и <CopyButton>.
    • Панель визуально поднята над кодом через order: -1, поэтому она —
      полноширинная строка сверху.
  • CSS:
    • apps/client/src/features/editor/components/code-block/code-block.module.css
      .menuGroup { order: -1 }.
    • apps/client/src/features/editor/styles/code.css
      .codeBlock { display: flex; flex-direction: column; … }.
  • Важный контекст (#146): <pre> (редактируемый contentDOM) обязан идти
    первым в DOM — иначе браузерный hit-testing клика уводит каретку на строку
    выше. Поэтому панель рендерится после <pre>, а «наверх» её поднимает
    order: -1.

Что хотим

  1. Обе кнопки — оверлеем в правом верхнем углу блока кода (абсолютное
    позиционирование), больше не отдельной строкой.
  2. Селектор языка (только в режиме редактирования) скрыт по умолчанию и
    появляется при наведении на блок кода (а также при фокусе / открытом
    дропдауне).
  3. Кнопка «копировать» остаётся видимой всегда (по ТЗ скрываем только селектор).
  4. В read-only (published) селектор языка не показывается вообще — он там
    неактивен и бесполезен; остаётся только кнопка копирования.

Предлагаемая реализация

  • code.css: у .codeBlock добавить position: relative; — якорь для
    абсолютного оверлея. flex-колонку можно оставить: панель уйдёт из потока за
    счёт position: absolute.
  • code-block.module.css.menuGroup:
    • убрать order: -1;
    • position: absolute; top: 8px; right: 8px; z-index: 1; + уменьшить gap;
    • сохранить @media print { display: none; }.
  • Селектор языка рендерить только при editor.isEditable (убрать текущий
    костыль disabled={!editor.isEditable} — в read-only селектора просто нет,
    остаётся только кнопка копирования).
  • Скрытие селектора по наведению (в режиме редактирования):
    • добавить Select класс-обёртку:
      classNames={{ root: classes.languageSelect, input: classes.selectInput }};
    • .languageSelect { opacity: 0; pointer-events: none; transition: opacity 150ms ease; }
    • :global(.codeBlock):hover .languageSelect, .languageSelect:focus-within { opacity: 1; pointer-events: auto; }
    • (.codeBlock — глобальный класс NodeViewWrapper, поэтому через :global()).
  • <pre> оставить первым в DOM (порядок не трогаем — критично для #146).

Тонкости и edge-cases

  • #146: порядок DOM (<pre> первым) сохраняется; absolute-позиционирование
    дополнительно убирает панель из потока — каретка при клике не съезжает.
  • Нет сдвига layout: скрытый селектор (opacity: 0) сохраняет ширину внутри
    flex-Group, поэтому кнопка «копировать» не прыгает при появлении/скрытии
    селектора.
  • Клик сквозь скрытый селектор: pointer-events: none в скрытом состоянии →
    выделение/клик по коду под ним работает; видимая кнопка «копировать»
    перекрывает лишь маленький угол.
  • Mermaid: когда <pre> скрыт и рендерится диаграмма — оверлей ложится в
    правый верхний угол диаграммы, ок.
  • Read-only (published): селектор языка не рендерится вообще (он неактивен и
    бесполезен) — на наведении показывать нечего; остаётся только кнопка
    копирования, видимая всегда.
  • Горизонтальный скролл кода: оверлей привязан к .codeBlock, а не к
    прокручиваемому <pre> → остаётся в углу (как на GitHub).
  • Dropdown Mantine рендерится в портал → не обрезается overflow блока.
  • Обновить комментарии про #146 в обоих файлах (там описан механизм order: -1,
    который убираем), чтобы объяснение соответствовало новому подходу.

Критерии приёмки

  • Панель управления не занимает отдельную строку; кнопки — в правом верхнем
    углу поверх кода.
  • В режиме редактирования селектор языка невидим, пока не навели курсор на
    блок кода (или пока он не в фокусе); появление плавное.
  • В read-only (published) селектора языка нет вообще (даже при наведении).
  • Кнопка копирования видна всегда и работает.
  • Каретка при клике по коду встаёт в правильную строку (регресс #146 не
    воспроизводится).
  • Mermaid, read-only режим и горизонтальный скролл — без визуальных
    артефактов.
  • Печать: панель скрыта (@media print).

Затрагиваемые файлы

  • apps/client/src/features/editor/components/code-block/code-block-view.tsx
  • apps/client/src/features/editor/components/code-block/code-block.module.css
  • apps/client/src/features/editor/styles/code.css
## Проблема В блоке кода панель управления (селектор языка + кнопка «копировать») занимает отдельную строку над кодом и тратит на себя целую строку по вертикали. Нужно перенести обе кнопки **поверх кода**, в правый верхний угол (в том же месте), а селектор языка **скрывать до наведения** на блок. ## Как работает сейчас - Компонент: `apps/client/src/features/editor/components/code-block/code-block-view.tsx` - `.codeBlock` (`NodeViewWrapper`) — flex-колонка. В DOM сначала идёт `<pre>` с кодом, затем `<Group className={menuGroup}>` с `<Select>` (язык) и `<CopyButton>`. - Панель визуально поднята **над** кодом через `order: -1`, поэтому она — полноширинная строка сверху. - CSS: - `apps/client/src/features/editor/components/code-block/code-block.module.css` → `.menuGroup { order: -1 }`. - `apps/client/src/features/editor/styles/code.css` → `.codeBlock { display: flex; flex-direction: column; … }`. - Важный контекст (#146): `<pre>` (редактируемый contentDOM) обязан идти **первым** в DOM — иначе браузерный hit-testing клика уводит каретку на строку выше. Поэтому панель рендерится **после** `<pre>`, а «наверх» её поднимает `order: -1`. ## Что хотим 1. Обе кнопки — оверлеем в правом верхнем углу блока кода (абсолютное позиционирование), больше не отдельной строкой. 2. Селектор языка (только в режиме редактирования) скрыт по умолчанию и появляется при наведении на блок кода (а также при фокусе / открытом дропдауне). 3. Кнопка «копировать» остаётся видимой всегда (по ТЗ скрываем только селектор). 4. В read-only (published) селектор языка не показывается вообще — он там неактивен и бесполезен; остаётся только кнопка копирования. ## Предлагаемая реализация - `code.css`: у `.codeBlock` добавить `position: relative;` — якорь для абсолютного оверлея. flex-колонку можно оставить: панель уйдёт из потока за счёт `position: absolute`. - `code-block.module.css` → `.menuGroup`: - убрать `order: -1`; - `position: absolute; top: 8px; right: 8px; z-index: 1;` + уменьшить `gap`; - сохранить `@media print { display: none; }`. - Селектор языка рендерить **только при `editor.isEditable`** (убрать текущий костыль `disabled={!editor.isEditable}` — в read-only селектора просто нет, остаётся только кнопка копирования). - Скрытие селектора по наведению (в режиме редактирования): - добавить `Select` класс-обёртку: `classNames={{ root: classes.languageSelect, input: classes.selectInput }}`; - `.languageSelect { opacity: 0; pointer-events: none; transition: opacity 150ms ease; }` - `:global(.codeBlock):hover .languageSelect, .languageSelect:focus-within { opacity: 1; pointer-events: auto; }` - (`.codeBlock` — глобальный класс `NodeViewWrapper`, поэтому через `:global()`). - `<pre>` оставить **первым** в DOM (порядок не трогаем — критично для #146). ## Тонкости и edge-cases - **#146:** порядок DOM (`<pre>` первым) сохраняется; absolute-позиционирование дополнительно убирает панель из потока — каретка при клике не съезжает. - **Нет сдвига layout:** скрытый селектор (`opacity: 0`) сохраняет ширину внутри flex-`Group`, поэтому кнопка «копировать» не прыгает при появлении/скрытии селектора. - **Клик сквозь скрытый селектор:** `pointer-events: none` в скрытом состоянии → выделение/клик по коду под ним работает; видимая кнопка «копировать» перекрывает лишь маленький угол. - **Mermaid:** когда `<pre>` скрыт и рендерится диаграмма — оверлей ложится в правый верхний угол диаграммы, ок. - **Read-only (published):** селектор языка не рендерится вообще (он неактивен и бесполезен) — на наведении показывать нечего; остаётся только кнопка копирования, видимая всегда. - **Горизонтальный скролл кода:** оверлей привязан к `.codeBlock`, а не к прокручиваемому `<pre>` → остаётся в углу (как на GitHub). - **Dropdown Mantine** рендерится в портал → не обрезается `overflow` блока. - Обновить комментарии про #146 в обоих файлах (там описан механизм `order: -1`, который убираем), чтобы объяснение соответствовало новому подходу. ## Критерии приёмки - [ ] Панель управления не занимает отдельную строку; кнопки — в правом верхнем углу поверх кода. - [ ] В режиме редактирования селектор языка невидим, пока не навели курсор на блок кода (или пока он не в фокусе); появление плавное. - [ ] В read-only (published) селектора языка нет вообще (даже при наведении). - [ ] Кнопка копирования видна всегда и работает. - [ ] Каретка при клике по коду встаёт в правильную строку (регресс #146 не воспроизводится). - [ ] Mermaid, read-only режим и горизонтальный скролл — без визуальных артефактов. - [ ] Печать: панель скрыта (`@media print`). ## Затрагиваемые файлы - `apps/client/src/features/editor/components/code-block/code-block-view.tsx` - `apps/client/src/features/editor/components/code-block/code-block.module.css` - `apps/client/src/features/editor/styles/code.css`
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#275