# HDROPDOWN CANON

Документ фиксирует единый канон dropdown-окон NODE.DC.

Цель документа:
- убрать локальные самодельные реализации выпадающих окон
- перевести dropdown на переиспользуемые shared-компоненты
- зафиксировать единый контракт для trigger, portal, placement, стилей и поведения
- исключить повторение проблемы, когда соседние controls работают по-разному на одном и том же экране

## Базовый принцип

Dropdown в системе не является "локальной маленькой версткой рядом с кнопкой".

Dropdown это:
- отдельный floating-layer
- отдельный reusable UI-компонент
- предсказуемый контракт открытия и закрытия
- единый визуальный канон

Если на экране есть несколько выпадающих окон, пользователь не должен видеть разные механики:
- одно окно открывается по тексту
- второе только по стрелке
- третье клипается контейнером
- четвертое открывается с другим типом привязки

Это считается дефектом стандартизации.

## Какие dropdown бывают

В проекте фиксируются три типа выпадающих окон.

### 1. Selection dropdown

Используется там, где пользователь выбирает значение.

Примеры:
- `Статус`
- `Приоритет`
- `Дата`
- `Назначенные`
- `Метки`
- выбор проекта
- выбор модуля

Смысл:
- пользователь выбирает одно или несколько значений
- у dropdown есть список опций
- часто есть поиск
- текущее значение отображается в trigger

Канонические reference-реализации:
- [state/base.tsx](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/apps/web/core/components/dropdowns/state/base.tsx:45)
- [priority.tsx](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/apps/web/core/components/dropdowns/priority.tsx:298)

### 2. Action dropdown

Используется там, где пользователь вызывает набор действий.

Примеры:
- `...` на карточке задачи
- `...` в detail-header
- быстрые действия у рабочего элемента

Смысл:
- dropdown не меняет поле напрямую
- dropdown показывает список команд
- каждая строка запускает `action`

Канонический shared-компонент:
- [action-dropdown.tsx](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/packages/ui/src/dropdowns/action-dropdown.tsx:109)

### 3. Context menu

Используется только как контекстное меню, а не как основной UI для кнопки `...`.

Примеры:
- правый клик
- context-actions, привязанные к `parentRef`

Правило:
- `ContextMenu` не заменяет основной dropdown по клику на кнопку
- если у карточки есть кнопка `...`, основной visible menu должен открываться через `ActionDropdown`
- `ContextMenu` допустим как дополнительный способ вызвать те же действия через right click

## Что запрещено

Запрещено делать новый dropdown через:
- локальный `isOpen` без shared-компонента
- локальный `useOutsideClick` внутри карточки
- inline absolute-блок внутри карточки
- popup, который живет внутри scroll-контейнера
- отдельную реализацию `...`, если рядом уже есть рабочий shared-dropdown
- другой trigger-контракт для соседних controls на одной оси

Запрещено:
- делать одну механику для `Статуса`, а другую для `...`
- открывать меню только по стрелке, если весь control должен быть кликабельным
- класть popup внутрь карточки, detail-pane, sidebar или sticky-header без portal
- компенсировать неверную архитектуру случайными `left/right translate`

## Главный стандарт переиспользования

Перед созданием нового dropdown нужно ответить только на два вопроса:

1. Это выбор значения или набор действий?
2. Есть ли уже shared-компонент для этого типа?

Если это выбор значения:
- использовать существующий selection-dropdown
- расширять existing base-component, а не писать новый popup с нуля

Если это список действий:
- использовать `ActionDropdown`
- не создавать отдельную локальную механику для карточки, detail-view или sidebar

## Канонический стек для dropdown

Любой новый dropdown должен опираться на один и тот же стек:
- реальный `trigger`
- `usePopper`
- `portal`
- `outside click close`
- `keyboard close/open`
- `preventOverflow`
- `flip`

Для action dropdown текущий канон зафиксирован в:
- [action-dropdown.tsx](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/packages/ui/src/dropdowns/action-dropdown.tsx:118)

Для selection dropdown reference-стек уже зафиксирован в:
- [state/base.tsx](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/apps/web/core/components/dropdowns/state/base.tsx:45)

## Trigger-правила

Trigger должен быть реальным интерактивным элементом.

Правила:
- dropdown привязывается к настоящей кнопке или surface-контролу
- нельзя заворачивать trigger в лишний интерактивный wrapper без причины
- нельзя делать `button` внутри `button`
- нельзя вешать отдельный click-interceptor на родительскую карточку так, чтобы он ломал trigger dropdown

Если control живет внутри кликабельной карточки:
- интерактивная зона обязана быть помечена как ignore-area для клика карточки
- карточка не должна перехватывать клик у dropdown trigger

Пример текущего канона для вложенной интерактивной зоны карточки:
- `data-control-link-ignore="true"`

## Portal-правила

Dropdown нельзя рендерить inline, если control находится внутри:
- карточки
- kanban-колонки
- list-item
- detail-pane
- sidebar
- properties section
- sticky toolbar
- scrollable container

В этих случаях popup обязан рендериться через portal на верхнем слое.

Базовое правило:
- `document.body` является стандартным portal target, если нет отдельного системного слоя

Причина:
- иначе будут клиппинг, неправильный z-index, обрезание соседними контейнерами и ложные оффсеты

## Placement-правила

Placement выбирается не "на глаз", а по канону control-type.

### Канон для selection dropdown

По умолчанию:
- `bottom-start`

Это уже используется соседними controls:
- `Статус`
- `Приоритет`
- `Дата`

### Канон для action dropdown карточек

Для quick-actions карточек используется тот же базовый anchor-канон, что и у соседних dropdown на той же верхней оси.

Текущая фиксация для kanban:
- [base-kanban-root.tsx](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/apps/web/core/components/issues/issue-layouts/kanban/base-kanban-root.tsx:126)
- quick-actions в kanban теперь идут через `bottom-start`

Смысл:
- меню раскрывается относительно trigger по той же логике, что и соседние controls
- мы не подгоняем offset вручную под один экран
- мы нормализуем сам placement-contract

### Когда допустим другой placement

Другой placement допустим только если это диктует layout:
- `top-start`, если dropdown открывается из нижней части detail-pane и не должен врезаться в секцию ниже
- `top-end`, если control находится у правой кромки и popup иначе уходит за экран

Но это должно быть обосновано layout-контекстом, а не случайной визуальной подгонкой.

## Offset-правило

Offset нужен только для вертикального зазора между trigger и popup.

Канон:
- вертикальный gap небольшой и стабильный
- боковой offset по умолчанию не используется

Если кажется, что popup "смотрит не туда":
- сначала проверяется правильность `placement`
- потом anchor element
- и только в последнюю очередь ручной skidding

Правило:
- нельзя лечить неверный `placement` случайным `left/right offset`

## Визуальный канон

Все dropdown и popup подчиняются общему matte glass стилю.

Обязательные свойства:
- темный glass surface
- blur
- мягкая стеклянная граница
- без технических outline
- без браузерного синего focus ring
- без светлой инородной подложки на темном экране

Внутренние элементы dropdown:
- search shell визуально того же семейства, что и popup
- option row того же радиуса, что и системные popup-option
- hover мягкий, не кислотный
- active/selected state не ломает общую палитру

## Правила для списка действий

Action dropdown обязан принимать данные в виде menu items.

Каждый item:
- имеет `key`
- может иметь `icon`
- может иметь `title`
- может иметь `description`
- может иметь `disabled`
- может иметь `action`

Правило:
- визуальный рендер item делается внутри shared action-dropdown
- вызывающий код не рисует popup shell вручную

## Правила для карточек и detail-view

Для карточек рабочего элемента:
- `...` всегда action dropdown
- `Статус` и `Приоритет` всегда selection dropdown
- все три control-а на одной оси должны использовать совместимую trigger-геометрию и близкий placement-contract

Для detail-view:
- quick actions сверху не должны использовать отдельный popup-engine
- если действия те же, используется тот же `ActionDropdown`

Для `Внутреннего контура`:
- quick-actions карточек больше не живут на отдельной локальной механике
- они нормализуются под тот же shared dropdown-stack, что и другие controls

## Правила для хлебных крошек

Breadcrumb dropdown тоже подчиняется этому документу.

Правило:
- project breadcrumb это dropdown проектов
- module breadcrumb это dropdown модулей проекта
- весь breadcrumb-item кликабелен, а не только стрелка
- стрелка не может быть единственной интерактивной зоной

Если breadcrumb открывает список выбора:
- это selection dropdown
- trigger-контракт должен совпадать на всех проектных экранах

## Правила для новых экранов

Новый экран не имеет права изобретать свой тип dropdown, если:
- уже существует shared action dropdown
- уже существует shared selection dropdown

Порядок работы:
1. определить тип dropdown
2. выбрать shared-компонент
3. выбрать canonical placement
4. открыть popup через portal
5. применить glass-канон
6. проверить поведение внутри scroll/sticky/detail contexts

## Что делать, если нужен новый вариант

Если текущих shared-вариантов не хватает:
- сначала расширяется shared-component
- потом новый вариант документируется в этом файле
- только после этого он применяется на экранах

Что нельзя делать:
- сделать новый локальный dropdown "временно"
- спрятать временную логику внутри конкретной карточки
- оставлять два разных menu-engine для одинаковой задачи

## Legacy-правило

Если в проекте еще остались старые dropdown-механики:
- они считаются legacy
- новые карточечные quick-actions на них не строятся
- при доработке экрана предпочтение отдается переводу на shared-канон, а не лечению локального бага поверх legacy-кода

Это особенно касается:
- локальных `CustomMenu`-вариантов для карточечных `...`
- inline popup внутри карточек
- отдельных `isMenuActive` состояний в item-компонентах

## Reference-матрица переиспользования

Использовать:

- `ActionDropdown`
  - для `...`
  - для быстрых действий карточки
  - для action-menu detail-header

- `StateDropdown`
  - для выбора состояния

- `PriorityDropdown`
  - для выбора приоритета

- `DateDropdown`
  - для выбора даты

- `MemberDropdown`
  - для выбора участников

- `ContextMenu`
  - только как дополнительное context-menu по `parentRef`
  - не как основной visible dropdown у кнопки `...`

## Минимальный check-list перед merge

Перед завершением задачи с dropdown надо проверить:
- popup открывается по клику на весь intended trigger
- popup не клипается родительским контейнером
- popup не уезжает за экран
- popup не живет на отдельном локальном menu-engine без причины
- `Статус / Приоритет / ...` на карточке не конфликтуют по click-handling
- карточка не перехватывает клик у trigger
- popup визуально соответствует matte glass канону
- hover, active и selected состояния выглядят как часть одной системы

## Канон внедрения

С этого момента правило простое:

- одинаковая задача = одинаковый dropdown-engine
- одинаковый control-type = одинаковый placement-contract
- одинаковый popup-type = одинаковый visual shell

Нельзя:
- лечить оффсет одного окна отдельной заплаткой
- оставлять соседние dropdown на разных механиках
- дублировать menu-логику в карточке, detail-pane и sidebar

Нужно:
- расширять shared-компонент
- переиспользовать его на всех экранах
- документировать новый вариант сразу в этом файле
