You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
5.8 KiB
135 lines
5.8 KiB
/*
|
|
* Shared menu-item indicator recipe.
|
|
*
|
|
* Mirrors the visual treatment of `<Checkbox>` and `<RadioGroup>` so a
|
|
* menu CheckboxItem / RadioItem reads identically to its standalone
|
|
* sibling:
|
|
*
|
|
* - off → outlined box / circle (transparent fill, neutral border)
|
|
* - on → filled solid box / circle with a white SVG ✓ / dot inside
|
|
*
|
|
* Targets the four pseudo-element indicators that signal `data-state`
|
|
* on menu items inside a `role="menu"` container:
|
|
*
|
|
* [data-context-menu-checkbox-item]
|
|
* [data-context-menu-radio-item]
|
|
* [data-dropdown-menu-checkbox-item]
|
|
* [data-dropdown-menu-radio-item]
|
|
*
|
|
* Layout: the indicator slot is reserved on every checkbox / radio
|
|
* menu item so the row text doesn't shift when state flips. `::before`
|
|
* paints the box / circle (always present); `::after` paints the glyph
|
|
* inside (only when checked).
|
|
*
|
|
* Tokens (consumer-overridable):
|
|
* --_menu-indicator-size @default 0.875rem
|
|
* --_menu-indicator-radius @default 0.2rem (rounded square)
|
|
* --_menu-indicator-color @default var(--color-primary-solid) (filled accent)
|
|
* --_menu-indicator-glyph @default white (color of the check / dot)
|
|
* --_menu-indicator-border @default var(--color-border-default) (off-state border)
|
|
* --_menu-indicator-inset @default var(--space-2)
|
|
*
|
|
* NOT INCLUDED: standalone `<Checkbox>` / `<RadioGroup>` / Select.
|
|
* ItemIndicator. Those use real DOM parts with size scales and their
|
|
* own SVG markers; this partial is the simplified version for items
|
|
* rendered inside a menu chrome.
|
|
*/
|
|
|
|
/* Reserve the indicator slot.
|
|
Selectors are DOUBLED (`[x][x]` → specificity 0,2,0) so this longhand
|
|
`padding-inline-start` beats the menu recipe's `padding-inline` shorthand
|
|
(0,1,0) REGARDLESS of load order. Required now that menu recipes are
|
|
code-split: this partial loads with its own chunk and may resolve before or
|
|
after the menu recipe; the specificity bump removes the order dependency. */
|
|
[data-context-menu-checkbox-item][data-context-menu-checkbox-item],
|
|
[data-context-menu-radio-item][data-context-menu-radio-item],
|
|
[data-dropdown-menu-checkbox-item][data-dropdown-menu-checkbox-item],
|
|
[data-dropdown-menu-radio-item][data-dropdown-menu-radio-item] {
|
|
--_menu-indicator-size: 0.875rem;
|
|
--_menu-indicator-radius: 0.2rem;
|
|
--_menu-indicator-color: var(--color-primary-solid);
|
|
--_menu-indicator-glyph: var(--color-content-on-solid, #fff);
|
|
--_menu-indicator-border: var(--color-border-default);
|
|
--_menu-indicator-inset: var(--space-2);
|
|
|
|
position: relative;
|
|
padding-inline-start: calc(
|
|
var(--_menu-indicator-inset) + var(--_menu-indicator-size) + var(--space-1)
|
|
);
|
|
}
|
|
|
|
/* ── Box / circle (::before) — always painted, regardless of state ───── */
|
|
|
|
[data-context-menu-checkbox-item]::before,
|
|
[data-context-menu-radio-item]::before,
|
|
[data-dropdown-menu-checkbox-item]::before,
|
|
[data-dropdown-menu-radio-item]::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset-inline-start: var(--_menu-indicator-inset);
|
|
inline-size: var(--_menu-indicator-size);
|
|
block-size: var(--_menu-indicator-size);
|
|
border: 1px solid var(--_menu-indicator-border);
|
|
border-radius: var(--_menu-indicator-radius);
|
|
background: transparent;
|
|
transition:
|
|
background var(--duration-fast) var(--ease-default),
|
|
border-color var(--duration-fast) var(--ease-default);
|
|
}
|
|
|
|
/* Checked state: box fills with accent colour, border tracks it. */
|
|
[data-context-menu-checkbox-item][data-state='checked']::before,
|
|
[data-context-menu-radio-item][data-state='checked']::before,
|
|
[data-dropdown-menu-checkbox-item][data-state='checked']::before,
|
|
[data-dropdown-menu-radio-item][data-state='checked']::before {
|
|
border-color: var(--_menu-indicator-color);
|
|
background: var(--_menu-indicator-color);
|
|
}
|
|
|
|
/* Radio variant: circular box (both off and on). */
|
|
[data-context-menu-radio-item]::before,
|
|
[data-dropdown-menu-radio-item]::before {
|
|
border-radius: 50%;
|
|
}
|
|
|
|
/* ── Glyph (::after) — only painted when state='checked' ─────────────── */
|
|
|
|
/* Checkbox: SVG ✓ centred inside the box. We use mask-image so the
|
|
glyph colour follows --_menu-indicator-glyph (white over the solid
|
|
accent fill). The SVG is the same shape `<SvgCheck>` paints in the
|
|
standalone Checkbox component. */
|
|
[data-context-menu-checkbox-item][data-state='checked']::after,
|
|
[data-dropdown-menu-checkbox-item][data-state='checked']::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset-inline-start: var(--_menu-indicator-inset);
|
|
inset-block-start: 50%;
|
|
transform: translateY(-50%);
|
|
inline-size: var(--_menu-indicator-size);
|
|
block-size: var(--_menu-indicator-size);
|
|
background-color: var(--_menu-indicator-glyph);
|
|
mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12.5L10 17.5L19 7'/></svg>");
|
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12.5L10 17.5L19 7'/></svg>");
|
|
mask-repeat: no-repeat;
|
|
mask-position: center;
|
|
mask-size: contain;
|
|
-webkit-mask-repeat: no-repeat;
|
|
-webkit-mask-position: center;
|
|
-webkit-mask-size: contain;
|
|
}
|
|
|
|
/* Radio: smaller solid inner dot centred inside the circle, sized to
|
|
~40% of the indicator (matches RadioGroup's dot ratio). */
|
|
[data-context-menu-radio-item][data-state='checked']::after,
|
|
[data-dropdown-menu-radio-item][data-state='checked']::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset-inline-start: calc(var(--_menu-indicator-inset) + var(--_menu-indicator-size) * 0.3);
|
|
inset-block-start: 50%;
|
|
transform: translateY(-50%);
|
|
inline-size: calc(var(--_menu-indicator-size) * 0.4);
|
|
block-size: calc(var(--_menu-indicator-size) * 0.4);
|
|
border-radius: 50%;
|
|
background: var(--_menu-indicator-glyph);
|
|
}
|