palabras(menubar): color/fondo inline + popover de escape opaco y no-modal

Dos correcciones del usuario sobre la v4:
- Color de texto / Fondo (una sola propiedad) YA NO abren superficie: son un
  submenu de MUESTRAS de la paleta (chip + nombre, RadioItem) + "Personalizado…"
  solo para un hex arbitrario. Patron Notion; menu-nativo.
- El escape de un campo pasa de Dialog MODAL a Popover NO-MODAL anclado a la
  barra, y se fuerza OPACO (kill del frost de cristal en [data-depth] + solid
  surface): tienes que ver el campo, no lo de detras; el editor queda visible e
  interactivo.

Verificado en Chrome real: Color de texto/Fondo = submenus de 9 muestras;
Enlace… = popover opaco (oklch solido, backdrop-filter none), sin overlay
modal, editor detras visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
menubar-v4-safe
dev 3 months ago
parent e2172aec42
commit 6c10e78378

@ -138,20 +138,47 @@
white-space: nowrap;
}
/* Focused ONE-FIELD escape dialog — the real control, bare (the Dialog.Title
carries the label, so the field's own label is hidden and it fills the width).
No panel surface / grid / skin: inside a proper dialog the control is itself. */
[data-palabras-menu-dialog] [data-palabras-menu-field] {
padding-block-start: var(--space-2);
/* Focused ONE-FIELD escape — a NON-MODAL popover anchored under the bar. The
own title labels it, the field's own label is hidden and the control fills the
width. No panel surface / grid / skin: the control is itself, at popover width. */
[data-palabras-menu-popover][data-palabras-menu-popover] {
inline-size: min(320px, calc(100vw - var(--space-8)));
display: flex;
flex-direction: column;
gap: var(--space-2);
/* OPAQUE surface — you edit INTO this popover, so the field must be readable
over the editor content behind. Kill the theme's overlay frost/translucency
(cristal blurs `[data-depth]`) and any entrance opacity. */
background: var(--color-surface-default);
backdrop-filter: none;
opacity: 1;
}
/* Colour swatch chip — leads a palette RadioItem / trails a colour Sub trigger. */
[data-palabras-swatch] {
display: inline-block;
inline-size: 0.75rem;
block-size: 0.75rem;
border-radius: var(--radius-xs, 3px);
border: var(--border-width) solid var(--color-border-subtle);
margin-inline-end: var(--space-2);
vertical-align: -1px;
}
[data-palabras-menu-pop-title] {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--color-content-default);
}
[data-palabras-menu-popover] [data-palabras-menu-field] {
display: contents;
}
[data-palabras-menu-dialog] [data-palabras-field] {
[data-palabras-menu-popover] [data-palabras-field] {
grid-template-columns: minmax(0, 1fr);
}
[data-palabras-menu-dialog] [data-palabras-field] > :not([data-palabras-field-label]) {
[data-palabras-menu-popover] [data-palabras-field] > :not([data-palabras-field-label]) {
grid-column: 1;
justify-self: stretch;
}
[data-palabras-menu-dialog] [data-palabras-field-label] {
[data-palabras-menu-popover] [data-palabras-field-label] {
display: none;
}
[data-palabras-toc-level='2'] {

@ -124,6 +124,20 @@
return effective(f) === true;
}
// ── Colour fields (Color de texto / Fondo) — a SINGLE property, so they stay
// IN the menu as swatches of the scheme palette (Notion's colour submenu), not
// a surface. Only an arbitrary hex escalates via «Personalizado…». ──
const paletteEntries = $derived(
(scheme.palette ?? []).map((e) =>
typeof e === 'string' ? { value: e, name: e } : { value: e.value, name: e.name ?? e.value }
)
);
/** The block's stored colour override (a plain hex), or undefined (inherited). */
function curColor(f: PalabrasFieldDef): string | undefined {
const v = loc?.node?.[f.key];
return typeof v === 'string' ? v : undefined;
}
/** The active option's label — shown trailing on a Diseño Sub trigger. */
function currentLabel(f: PalabrasFieldDef): string {
if (f.type === 'slider') {
@ -195,6 +209,30 @@
<Menubar.CheckboxItem checked={toggleOn(field)} onCheckedChange={(v) => commit(field, v)}>
{ts(field.label)}
</Menubar.CheckboxItem>
{:else if field.type === 'color'}
<Menubar.Sub>
<Menubar.SubTrigger>
<span data-palabras-menu-label>{ts(field.label)}</span>
<span data-palabras-menu-value>
{#if curColor(field)}
<span data-palabras-swatch style:background={curColor(field)}></span>
{/if}
</span>
</Menubar.SubTrigger>
<Menubar.SubContent data-words-external-tool>
<Menubar.RadioGroup value={curColor(field)} onValueChange={(v) => commit(field, v)}>
{#each paletteEntries as pe (pe.value)}
<Menubar.RadioItem value={pe.value}>
<span data-palabras-swatch style:background={pe.value}></span>{pe.name}
</Menubar.RadioItem>
{/each}
</Menubar.RadioGroup>
<Menubar.Separator />
<Menubar.Item onSelect={() => onEdit(field)}>
{ts('#?components.palabras.action.custom-color|Personalizado…')}
</Menubar.Item>
</Menubar.SubContent>
</Menubar.Sub>
{:else if field.type === 'slider'}
<Menubar.Sub>
<Menubar.SubTrigger>

@ -17,7 +17,7 @@
import { ActiveEidos } from '$uix/eidos';
import * as Words from '$soma/components/words';
import { Card } from '$uix/eidos/components/card';
import { Dialog } from '$uix/eidos/components/dialog';
import { Popover } from '$uix/eidos/components/popover';
import { Flex } from '$uix/eidos/components/flex';
import type { PalabrasFieldDef } from './types';
import WordsActivate from './palabras-activate.svelte';
@ -124,18 +124,24 @@
/>
{/snippet}
</PalabrasMenubar>
<!-- The focused one-field editor: a small Dialog hosting the ONE real
control the element menu escalated. Not the panel — one field. -->
<Dialog
<!-- The focused one-field editor: a NON-MODAL popover anchored under the
bar, hosting the ONE real control the element menu escalated. Not a
modal, not the panel — one field, a second focused step in place. -->
<Popover
open={editField != null}
onOpenChange={(o) => {
if (!o) editField = null;
}}
>
<Dialog.Overlay />
<Dialog.Content size="sm" data-palabras-menu-dialog data-words-external-tool>
<Dialog.Title>{editField ? eidos.langs.ts(editField.label) : ''}</Dialog.Title>
<Popover.Content
side="bottom"
align="start"
customAnchor="[data-palabras-menubar]"
data-palabras-menu-popover
data-words-external-tool
>
{#if editField}
<span data-palabras-menu-pop-title>{eidos.langs.ts(editField.label)}</span>
<PalabrasPanel
{api}
{scheme}
@ -149,8 +155,8 @@
onClose={() => (editField = null)}
/>
{/if}
</Dialog.Content>
</Dialog>
</Popover.Content>
</Popover>
{/if}
<!-- Crumbs NAVIGATE (activate the node). Only floating mode also opens
its window — in docked the panel is already visible and in menubar

Loading…
Cancel
Save

Powered by TurnKey Linux.