|
|
4 months ago | |
|---|---|---|
| .. | ||
| README.md | 4 months ago | |
| context-menu-arrow.svelte | 5 months ago | |
| context-menu-checkbox-item.svelte | 4 months ago | |
| context-menu-content.svelte | 4 months ago | |
| context-menu-group-heading.svelte | 5 months ago | |
| context-menu-group.svelte | 5 months ago | |
| context-menu-item.svelte | 4 months ago | |
| context-menu-radio-group.svelte | 5 months ago | |
| context-menu-radio-item.svelte | 4 months ago | |
| context-menu-separator.svelte | 5 months ago | |
| context-menu-sub-content.svelte | 4 months ago | |
| context-menu-sub-trigger.svelte | 5 months ago | |
| context-menu-sub.svelte | 5 months ago | |
| context-menu-trigger.svelte | 5 months ago | |
| context-menu.css | 4 months ago | |
| context-menu.svelte | 4 months ago | |
| index.ts | 5 months ago | |
| types.ts | 4 months ago | |
README.md
<ContextMenu> — eidos
Right-click / long-press menu. Visually identical to
<DropdownMenu> — same panel chrome, same item rows. Differences vs
DropdownMenu:
| DropdownMenu | ContextMenu | |
|---|---|---|
| Trigger | <button> toggle |
<span> wrap intercepting contextmenu |
| Open via | Click / Enter / Space / ArrowDown | Right-click / Ctrl+Click / Long-press / Shift+F10 |
| Anchored to | Trigger's bounding box | Cursor position |
CheckboxGroup |
✓ | — (Bits parity gap; can be added later) |
Motion parity too. Items fade in staggered on open — the event-driven
cascade. The Content marks the panel [data-stagger] + sets the rhythm
--motion-stagger-each: var(--motion-stagger); each item carries
data-animation-style="fade"; the per-item index comes from :nth-child. Same
mechanism + caveats (direct-child items only; enter-only) as DropdownMenu — see
its README → Motion for the full explanation.
Usage
<script lang="ts">
import { ContextMenu } from '$uix/eidos/components/context-menu';
let showGrid = $state(true);
</script>
<ContextMenu>
<ContextMenu.Trigger>
<div class="canvas">Right-click anywhere on the canvas…</div>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item onSelect={() => copy()}>Copy</ContextMenu.Item>
<ContextMenu.Item onSelect={() => paste()}>Paste</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.CheckboxItem bind:checked={showGrid}>
Show grid
</ContextMenu.CheckboxItem>
<ContextMenu.Sub>
<ContextMenu.SubTrigger>Format</ContextMenu.SubTrigger>
<ContextMenu.SubContent>
<ContextMenu.Item>Bold</ContextMenu.Item>
<ContextMenu.Item>Italic</ContextMenu.Item>
</ContextMenu.SubContent>
</ContextMenu.Sub>
</ContextMenu.Content>
</ContextMenu>
Parts
14 pass-through wrappers — same vocabulary as DropdownMenu minus
CheckboxGroup.
Provider · Trigger · Content · Arrow · Item · Group · GroupHeading · Separator · CheckboxItem · RadioGroup · RadioItem · Sub · SubTrigger · SubContent.
Baseline
WAI-ARIA APG Menu pattern: https://www.w3.org/WAI/ARIA/apg/patterns/menu/
Soma owns:
contextmenuevent interception, long-press detection, Shift+F10 handling- Floating positioning anchored to the cursor (not the trigger box)
- Same keyboard navigation as DropdownMenu inside the open Content
- Focus management on open / close / sub navigation
Comparativa
| Lib | Trigger source | Long-press (touch) | Shift+F10 | Sub-menus | Cursor anchor |
|---|---|---|---|---|---|
| Radix Primitives | contextmenu event |
✓ | ✓ | ✓ | ✓ |
| Bits UI | same | ✓ | ✓ | ✓ | ✓ |
| Ark UI | same | ✓ | ✓ | ✓ | ✓ |
| Eidos (this) | same | ✓ | ✓ | ✓ | ✓ |
Full functional parity. No styled API additions at the eidos level — visual treatment identical to DropdownMenu (single recipe vocabulary across the menu family).
Decisiones
- Recipe duplication, not aliasing. The CSS uses separate
[data-context-menu-*]selectors rather than sharing[data-dropdown-menu-*]. Reason: the morfo emits distinct markers per component (correct contract behaviour), and aliasing the recipe would couple two independent components in CSS even though their visual vocabulary is identical. Cost: ~150 lines of CSS duplication. Benefit: each component evolves freely without cascade surprises. - Trigger gets a subtle
data-state='open'outline. Users often forget which region they right-clicked; a 1px dashed border while the menu is open provides a quiet "this is the source" affordance without competing with the menu itself. - No
CheckboxGroup. Soma doesn't ship one for context-menu (Bits doesn't either). Can be added later if needed.
Gaps
| Gap | Disposición | Detalle |
|---|---|---|
CheckboxGroup (array binding, Bits' addition for DropdownMenu) |
diferir | Soma doesn't ship one for context-menu yet. |
Per-item data-color cascade for destructive items |
implementar próximo turno | Same gap as DropdownMenu — would be the same cascade rule. Resolve once for both. |
| Touch UX testing | diferir | Long-press is wired in soma but visual + haptic feedback during the hold could be richer (Material-style ripple, etc.). |