Follow-up to commit 714ee373 (PickerShell extraction). Norm N-8 now
applied to every composite picker — date-picker, time-picker and
date-range-picker stop carrying their own Footer/Clear/Cancel/Close
duplicates and read the shared <PickerShell.Footer/Clear/Cancel/Close>
through `pickerShellContext`.
Soma changes
------------
- date-picker, time-picker, date-range-picker providers each gain a
`pickerShellHandle: PickerShellHandle` field exposing
`getMode/commit/cancel/clear`, and call
`pickerShellContext.set(this.pickerShellHandle)` in the constructor
so descendant Eidos shell parts can drive the picker.
Morfo changes
-------------
- Footer/Clear/Cancel/Close parts removed from each picker morfo.
The shared chrome lives outside the per-picker contract — its data
attrs are `[data-picker-footer/clear/cancel/close]` (no per-picker
prefix). Comment block in each morfo file documents the move.
Eidos changes
-------------
- Deleted 12 obsolete files:
src/uix/eidos/components/date-picker/date-picker-{footer,clear,cancel,close}.svelte
src/uix/eidos/components/time-picker/time-picker-{footer,clear,cancel,close}.svelte
src/uix/eidos/components/date-range-picker/date-range-picker-{footer,clear,cancel,close}.svelte
- Each picker's `index.ts` re-imports the 4 parts from
`../picker-shell/picker-shell-{footer,clear,cancel,close}.svelte`
so the public namespace stays `X.Footer / X.Clear / X.Cancel /
X.Close` — no breaking change for consumers.
- Each picker's `types.ts` re-exports `FooterProps/ClearProps/
CancelProps/CloseProps` from `../picker-shell`, replacing the stale
per-picker types that referenced soma popover Close props.
CSS changes
-----------
- Deleted ~190 lines of duplicated footer/clear/cancel/close rules
from date-picker.css, time-picker.css, date-range-picker.css. The
canonical `[data-picker-footer/clear/cancel/close]` rules in
picker-shell.css now style every picker uniformly.
- prefers-reduced-motion blocks pruned of the same selectors.
Public API
----------
Unchanged from a consumer perspective: `<DatePicker.Footer>`,
`<TimePicker.Clear>`, `<DateRangePicker.Close>`, etc. still resolve
to working components. The inner DOM markers change from
`[data-{x}-picker-footer]` to `[data-picker-footer]` — anyone with
custom CSS targeting the old selectors must migrate.
Checks
------
- 67/67 component-audit PASS
- 40/40 morfo:check PASS
- svelte-check 0 errors / 0 warnings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
parent
aaf35fdbb0
commit
aa62f0203c
@ -1,38 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DatePickerProvider } from '$soma/components/date-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
// Cancel action — reverts to the value snapshot taken on open and
|
||||
// closes the popover. Renders unconditionally — visibility is the
|
||||
// consumer's responsibility (include the part to show it).
|
||||
interface CancelProps extends HTMLButtonAttributes {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, onclick, ...rest }: CancelProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = DatePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.cancel();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-date-picker-cancel
|
||||
data-action="cancel"
|
||||
aria-label={uix.langs.ts('#?components.date-picker.cancel|Cancel')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.date-picker.cancel|Cancel')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,38 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DatePickerProvider } from '$soma/components/date-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
// Clear action — resets the picker's value (undefined) and keeps the
|
||||
// popover open. Renders unconditionally — visibility is the
|
||||
// consumer's responsibility (include the part to show it).
|
||||
interface ClearProps extends HTMLButtonAttributes {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, onclick, ...rest }: ClearProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = DatePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.clear();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-date-picker-clear
|
||||
data-action="clear"
|
||||
aria-label={uix.langs.ts('#?components.date-picker.clear|Clear date')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.date-picker.clear|Clear date')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,42 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DatePickerProvider } from '$soma/components/date-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
// Close / Save action — commits the current value and closes the
|
||||
// popover. Renders unconditionally — visibility is the consumer's
|
||||
// responsibility (include the part to show it).
|
||||
//
|
||||
// In `mode='modal'` the consumer MUST include this part (or another
|
||||
// route out) because outside-click and Escape are intentionally
|
||||
// blocked — otherwise the picker has no exit affordance.
|
||||
interface CloseProps extends HTMLButtonAttributes {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, onclick, ...rest }: CloseProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = DatePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.commit();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-date-picker-close
|
||||
data-action="close"
|
||||
aria-label={uix.langs.ts('#?components.date-picker.close|Close')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.date-picker.close|Close')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,26 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DatePickerProvider } from '$soma/components/date-picker';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
// Footer container for the picker calendar. Pure composition: render
|
||||
// whatever you want inside it (Clear / Cancel / Close — or arbitrary
|
||||
// children). Visibility is the consumer's responsibility — include
|
||||
// the part to show it, omit it to hide it.
|
||||
//
|
||||
// Carries `data-mode` so the CSS can react to the picker's mode
|
||||
// without re-reading provider context (e.g. enforce a minimum width
|
||||
// in modal, change the visual emphasis of the close button, etc.).
|
||||
interface FooterProps extends HTMLAttributes<HTMLDivElement> {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, ...rest }: FooterProps = $props();
|
||||
|
||||
const provider = DatePickerProvider.require();
|
||||
const mode = $derived(provider.opts.mode.current);
|
||||
</script>
|
||||
|
||||
<div data-date-picker-footer data-mode={mode} {...rest}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@ -1,35 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DateRangePickerProvider } from '$soma/components/date-range-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
interface CancelProps extends HTMLButtonAttributes {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, onclick, ...rest }: CancelProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = DateRangePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.cancel();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-date-range-picker-cancel
|
||||
data-action="cancel"
|
||||
aria-label={uix.langs.ts('#?components.date-range-picker.cancel|Cancel')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.date-range-picker.cancel|Cancel')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,35 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DateRangePickerProvider } from '$soma/components/date-range-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
interface ClearProps extends HTMLButtonAttributes {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, onclick, ...rest }: ClearProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = DateRangePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.clear();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-date-range-picker-clear
|
||||
data-action="clear"
|
||||
aria-label={uix.langs.ts('#?components.date-range-picker.clear|Clear range')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.date-range-picker.clear|Clear range')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,35 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DateRangePickerProvider } from '$soma/components/date-range-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
interface CloseProps extends HTMLButtonAttributes {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, onclick, ...rest }: CloseProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = DateRangePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.commit();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-date-range-picker-close
|
||||
data-action="close"
|
||||
aria-label={uix.langs.ts('#?components.date-range-picker.close|Close')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.date-range-picker.close|Close')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,21 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { DateRangePickerProvider } from '$soma/components/date-range-picker';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
// Pure composition: render whatever you want inside it (Clear /
|
||||
// Cancel / Close — or arbitrary children). Visibility is the
|
||||
// consumer's responsibility — include the part to show it.
|
||||
interface FooterProps extends HTMLAttributes<HTMLDivElement> {
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { children, ...rest }: FooterProps = $props();
|
||||
|
||||
const provider = DateRangePickerProvider.require();
|
||||
const mode = $derived(provider.opts.mode.current);
|
||||
</script>
|
||||
|
||||
<div data-date-range-picker-footer data-mode={mode} {...rest}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@ -1,33 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { TimePickerProvider } from '$soma/components/time-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { TimePickerCancelProps } from './types';
|
||||
|
||||
// Cancel action — reverts to the value snapshot taken on open and
|
||||
// closes the popover. Renders unconditionally — visibility is the
|
||||
// consumer's responsibility (include the part to show it).
|
||||
const { children, onclick, ...rest }: TimePickerCancelProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = TimePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.cancel();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-time-picker-cancel
|
||||
data-action="cancel"
|
||||
aria-label={uix.langs.ts('#?components.time-picker.cancel|Cancel')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.time-picker.cancel|Cancel')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,33 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { TimePickerProvider } from '$soma/components/time-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { TimePickerClearProps } from './types';
|
||||
|
||||
// Clear action — resets the picker's value (undefined) and keeps the
|
||||
// popover open. Renders unconditionally — visibility is the
|
||||
// consumer's responsibility (include the part to show it).
|
||||
const { children, onclick, ...rest }: TimePickerClearProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = TimePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.clear();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-time-picker-clear
|
||||
data-action="clear"
|
||||
aria-label={uix.langs.ts('#?components.time-picker.clear|Clear time')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.time-picker.clear|Clear time')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,37 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { TimePickerProvider } from '$soma/components/time-picker';
|
||||
import { getActiveUix } from '$active-uix';
|
||||
import type { TimePickerCloseProps } from './types';
|
||||
|
||||
// Close / Save action — commits the current value and closes the
|
||||
// popover. Renders unconditionally — visibility is the consumer's
|
||||
// responsibility (include the part to show it).
|
||||
//
|
||||
// In `mode='modal'` the consumer MUST include this part (or another
|
||||
// route out) because outside-click and Escape are intentionally
|
||||
// blocked — otherwise the picker has no exit affordance.
|
||||
const { children, onclick, ...rest }: TimePickerCloseProps = $props();
|
||||
|
||||
const uix = getActiveUix();
|
||||
const provider = TimePickerProvider.require();
|
||||
|
||||
function handleClick(event: MouseEvent & { currentTarget: EventTarget & HTMLButtonElement }) {
|
||||
provider.commit();
|
||||
if (typeof onclick === 'function') onclick.call(event.currentTarget, event);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-time-picker-close
|
||||
data-action="close"
|
||||
aria-label={uix.langs.ts('#?components.time-picker.close|Close')}
|
||||
onclick={handleClick}
|
||||
{...rest}
|
||||
>
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{:else}
|
||||
{uix.langs.ts('#?components.time-picker.close|Close')}
|
||||
{/if}
|
||||
</button>
|
||||
@ -1,20 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { TimePickerProvider } from '$soma/components/time-picker';
|
||||
import type { TimePickerFooterProps } from './types';
|
||||
|
||||
// Footer container for the picker clock. Pure composition: render
|
||||
// whatever you want inside it (Clear / Cancel / Close — or arbitrary
|
||||
// children). Visibility is the consumer's responsibility — include
|
||||
// the part to show it, omit it to hide it.
|
||||
//
|
||||
// Carries `data-mode` so the CSS can react to the picker's mode
|
||||
// without re-reading provider context.
|
||||
const { children, ...rest }: TimePickerFooterProps = $props();
|
||||
|
||||
const provider = TimePickerProvider.require();
|
||||
const mode = $derived(provider.opts.mode.current);
|
||||
</script>
|
||||
|
||||
<div data-time-picker-footer data-mode={mode} {...rest}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
Loading…
Reference in new issue