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.
svelte-kit-vice/src/uix/eidos/components/button/button.svelte

87 lines
2.9 KiB

feat(eidos): Button + 9 demo rebuilds + picker recipe polish Build: - New <Button> compound (morfo + soma + sema + eidos): 6 variants (solid/soft/surface/outline/ghost/plain), 8 colors, 5 sizes, optional icon/endIcon snippets, loading state with built-in Spinner, asChild Slot pattern, intent-driven sema (commit-action with fromProp:intent). Demo rebuilds (canonical depth per DEMO_AUTHORING_GUIDE): - context-menu, dropdown-menu, pin-input, alert-dialog, toggle-group: full per-part Morfo contracts, reference-parity tables, ARIA contract tables, keyboard tables, composition controls. - color-field, time-field, time-picker, time-range-picker, color-picker, month-grid, year-grid: same canonical treatment; per-part data/aria/ keyboard iteration of raw morfo, reference parity vs react-aria/ ark-ui/Chakra/bits-ui/Mantine. Picker recipe polish: - PickerShell.Clear/Cancel/Close refactored to use <Button size="xs"> (affects 5 pickers — date / date-range / time / time-range / color). - TimePicker + TimeRangePicker: popover condensed (min-width 18rem -> 14 / 32 -> 28), per-channel accent (hour=primary, minute=secondary, second=neutral; end variant muted), ruler off by default, hour ruler cleaned to 0/6/12/18/23. - ColorPicker: typography token --_color-picker-trigger-font-size that scales with data-size (10/12/14px); autoFormat default true; Area recipe override for [data-x-channel='hue'] with rainbow gradient + saturation/brightness Y overlays. - ValueText eidos wrapper: fix children snippet bug that suppressed soma's displayValue fallback. - date-field.css: segment editing affordance (cursor + hover + active ring) via new --_date-field-accent-element token across 8 color cascades. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
<script lang="ts">
/**
* Eidos `<Button>` — visual wrapper over Soma's headless button.
*
* Soma owns the click flow, `data-state`, `aria-busy`, `aria-disabled`
refactor(button): align with book cap. 22 — contact.activate, intent visual-only (Plan B commit 2) Button morfo declared `commit-action` with verb `action` (not in canon) and full intent range. Per «Diseñando lo que ocurre»: - Cap 22 §10: "Botón: contact.press" — the press is reception of the gesture, not the commit. - Cap 22 §11: "el intent fuerte no debería vivir en el contacto, sino en la señal o consecuencia posterior. La interfaz puede anticipar el peso mediante forma, color o señal previa." - Cap 22 §8: "Error típico: contact.press + fulfill — Esto hace que el sistema diga 'ya terminó' cuando solo ha empezado." Strict book reading applied: - Morfo event renamed: `commit-action` → `contact-activate` (family: `contact`, verb: `activate`, no intent binding) - Soma provider trigger string + comments updated - `intent` prop survives as VISUAL signal only — drives `data-color` and chip variant (anticipatory weight via form/color per cap. 22 §11) - README + types JSDoc updated with the book's prescription and the composition pattern: perceptual richness emerges from the OTHER morfos in the flow (Dialog warning, Item being deleted, etc.) firing their own commit/signal events at the actual moment of the consequence - `EVENT_NAME_ALLOWLIST` in morfo-vocabulary-check.ts emptied — no exceptions to the canon needed No `declaresOutcome` props, no imperative sema escape hatches: the morfo remains the single source of truth for events. If a flow loses perceptual differentiation, the answer is to model the missing morfo, not to overload the Button. Verification: - npm run morfo:vocabulary → EXIT 0 (zero allowlisted) - npx vitest src/uix/sema src/uix/morfo → 194/194 pass - npm run check → no new errors from these changes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* and the `contact-activate` perceptual event (book cap. 22 §10).
* Eidos adds the visual API on top — `variant`, `size`, `rounded`,
* `block`, `iconOnly`, the `icon` / `endIcon` snippet slots, and the
* loading Spinner — and wires those to the `data-*` selectors that
* `button.css` reacts to.
feat(eidos): Button + 9 demo rebuilds + picker recipe polish Build: - New <Button> compound (morfo + soma + sema + eidos): 6 variants (solid/soft/surface/outline/ghost/plain), 8 colors, 5 sizes, optional icon/endIcon snippets, loading state with built-in Spinner, asChild Slot pattern, intent-driven sema (commit-action with fromProp:intent). Demo rebuilds (canonical depth per DEMO_AUTHORING_GUIDE): - context-menu, dropdown-menu, pin-input, alert-dialog, toggle-group: full per-part Morfo contracts, reference-parity tables, ARIA contract tables, keyboard tables, composition controls. - color-field, time-field, time-picker, time-range-picker, color-picker, month-grid, year-grid: same canonical treatment; per-part data/aria/ keyboard iteration of raw morfo, reference parity vs react-aria/ ark-ui/Chakra/bits-ui/Mantine. Picker recipe polish: - PickerShell.Clear/Cancel/Close refactored to use <Button size="xs"> (affects 5 pickers — date / date-range / time / time-range / color). - TimePicker + TimeRangePicker: popover condensed (min-width 18rem -> 14 / 32 -> 28), per-channel accent (hour=primary, minute=secondary, second=neutral; end variant muted), ruler off by default, hour ruler cleaned to 0/6/12/18/23. - ColorPicker: typography token --_color-picker-trigger-font-size that scales with data-size (10/12/14px); autoFormat default true; Area recipe override for [data-x-channel='hue'] with rainbow gradient + saturation/brightness Y overlays. - ValueText eidos wrapper: fix children snippet bug that suppressed soma's displayValue fallback. - date-field.css: segment editing affordance (cursor + hover + active ring) via new --_date-field-accent-element token across 8 color cascades. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
*/
import { ActiveEidos } from '$uix/eidos';
import * as Button from '$soma/components/button';
import ButtonSpinner from './button-spinner.svelte';
import type { ButtonProps } from './types';
let {
variant = 'solid',
size = 'md',
rounded,
block = false,
iconOnly = false,
loading = false,
icon,
endIcon,
spinner,
spinnerPlacement = 'start',
loadingText,
children: bodyContent,
...headlessProps
}: ButtonProps = $props();
const eidos = ActiveEidos.require();
const resolvedSize = $derived(eidos.resolve(size, 'md'));
const resolvedRounded = $derived(eidos.resolve(rounded));
</script>
<Button.Provider
{...headlessProps}
{loading}
{loadingText}
{spinnerPlacement}
data-variant={variant}
data-size={resolvedSize}
data-rounded={resolvedRounded ?? undefined}
data-block={block ? '' : undefined}
data-icon-only={iconOnly ? '' : undefined}
data-has-icon={icon || (loading && spinnerPlacement === 'start') ? '' : undefined}
data-spinner-placement={spinnerPlacement}
>
{#snippet children(snippetProps)}
<!-- Start slot: spinner (when loading) wins over icon at start placement. -->
{#if loading && spinnerPlacement === 'start'}
{#if spinner}<ButtonSpinner>{@render spinner()}</ButtonSpinner>{:else}<ButtonSpinner />{/if}
{:else if icon}
<span class="eidos-button-icon" data-button-icon="start" aria-hidden="true">
{@render icon(snippetProps)}
</span>
{/if}
<!--
* Body wrapper holds the text label. When `iconOnly` is on, the
* recipe makes it visually-hidden but keeps it in the accessibility
* tree so screen readers still announce the label. When loading
* with `loadingText`, the original children remain in DOM (sr-only)
* and the loadingText takes the visual slot.
-->
<span class="eidos-button-body">
{#if loading && loadingText}
<span class="eidos-button-loading-text">{loadingText}</span>
<span class="eidos-button-sr-only">{@render bodyContent?.(snippetProps)}</span>
{:else}
{@render bodyContent?.(snippetProps)}
{/if}
</span>
<!-- End slot: spinner (when loading) wins over endIcon at end placement. -->
{#if loading && spinnerPlacement === 'end'}
{#if spinner}<ButtonSpinner>{@render spinner()}</ButtonSpinner>{:else}<ButtonSpinner />{/if}
{:else if endIcon}
<span class="eidos-button-icon" data-button-icon="end" aria-hidden="true">
{@render endIcon(snippetProps)}
</span>
{/if}
{/snippet}
</Button.Provider>

Powered by TurnKey Linux.