From aa62f0203cce21f812c6f1a08626057971eaa70c Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 21 May 2026 22:27:02 +0200 Subject: [PATCH] refactor(pickers): date/time/date-range pickers consume shared PickerShell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 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: ``, ``, ``, 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) --- .../date-picker/date-picker-cancel.svelte | 38 --------- .../date-picker/date-picker-clear.svelte | 38 --------- .../date-picker/date-picker-close.svelte | 42 ---------- .../date-picker/date-picker-footer.svelte | 26 ------- .../components/date-picker/date-picker.css | 62 --------------- src/uix/eidos/components/date-picker/index.ts | 12 ++- src/uix/eidos/components/date-picker/types.ts | 14 +++- .../date-range-picker-cancel.svelte | 35 --------- .../date-range-picker-clear.svelte | 35 --------- .../date-range-picker-close.svelte | 35 --------- .../date-range-picker-footer.svelte | 21 ----- .../date-range-picker/date-range-picker.css | 62 +-------------- .../components/date-range-picker/index.ts | 9 ++- .../components/date-range-picker/types.ts | 10 ++- src/uix/eidos/components/time-picker/index.ts | 9 ++- .../time-picker/time-picker-cancel.svelte | 33 -------- .../time-picker/time-picker-clear.svelte | 33 -------- .../time-picker/time-picker-close.svelte | 37 --------- .../time-picker/time-picker-footer.svelte | 20 ----- .../components/time-picker/time-picker.css | 62 +-------------- src/uix/eidos/components/time-picker/types.ts | 26 ++----- src/uix/morfo/components/date-picker.ts | 77 ++----------------- src/uix/morfo/components/date-range-picker.ts | 75 +----------------- src/uix/morfo/components/time-picker.ts | 67 +--------------- .../date-picker-provider.svelte.ts | 15 ++++ .../date-range-picker-provider.svelte.ts | 15 ++++ .../time-picker-provider.svelte.ts | 15 ++++ 27 files changed, 103 insertions(+), 820 deletions(-) delete mode 100644 src/uix/eidos/components/date-picker/date-picker-cancel.svelte delete mode 100644 src/uix/eidos/components/date-picker/date-picker-clear.svelte delete mode 100644 src/uix/eidos/components/date-picker/date-picker-close.svelte delete mode 100644 src/uix/eidos/components/date-picker/date-picker-footer.svelte delete mode 100644 src/uix/eidos/components/date-range-picker/date-range-picker-cancel.svelte delete mode 100644 src/uix/eidos/components/date-range-picker/date-range-picker-clear.svelte delete mode 100644 src/uix/eidos/components/date-range-picker/date-range-picker-close.svelte delete mode 100644 src/uix/eidos/components/date-range-picker/date-range-picker-footer.svelte delete mode 100644 src/uix/eidos/components/time-picker/time-picker-cancel.svelte delete mode 100644 src/uix/eidos/components/time-picker/time-picker-clear.svelte delete mode 100644 src/uix/eidos/components/time-picker/time-picker-close.svelte delete mode 100644 src/uix/eidos/components/time-picker/time-picker-footer.svelte diff --git a/src/uix/eidos/components/date-picker/date-picker-cancel.svelte b/src/uix/eidos/components/date-picker/date-picker-cancel.svelte deleted file mode 100644 index 8d0853bab..000000000 --- a/src/uix/eidos/components/date-picker/date-picker-cancel.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - - diff --git a/src/uix/eidos/components/date-picker/date-picker-clear.svelte b/src/uix/eidos/components/date-picker/date-picker-clear.svelte deleted file mode 100644 index 88c29dd26..000000000 --- a/src/uix/eidos/components/date-picker/date-picker-clear.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - - diff --git a/src/uix/eidos/components/date-picker/date-picker-close.svelte b/src/uix/eidos/components/date-picker/date-picker-close.svelte deleted file mode 100644 index bd6345769..000000000 --- a/src/uix/eidos/components/date-picker/date-picker-close.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/src/uix/eidos/components/date-picker/date-picker-footer.svelte b/src/uix/eidos/components/date-picker/date-picker-footer.svelte deleted file mode 100644 index 5116662ab..000000000 --- a/src/uix/eidos/components/date-picker/date-picker-footer.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/src/uix/eidos/components/date-picker/date-picker.css b/src/uix/eidos/components/date-picker/date-picker.css index 6c0785c98..0773594f6 100644 --- a/src/uix/eidos/components/date-picker/date-picker.css +++ b/src/uix/eidos/components/date-picker/date-picker.css @@ -185,63 +185,6 @@ border-color: transparent; } -/* ── Footer (Clear / Cancel / Close action row) ──────────────────────────── */ - -[data-date-picker-footer] { - display: flex; - align-items: center; - gap: var(--space-2); - padding-block-start: var(--space-3); - margin-block-start: var(--space-3); - border-block-start: var(--border-width) solid var(--color-border-subtle); -} - -[data-date-picker-clear], -[data-date-picker-cancel] { - margin-inline-end: auto; -} - -[data-date-picker-clear], -[data-date-picker-cancel], -[data-date-picker-close] { - inline-size: max-content; - min-block-size: var(--control-height-sm); - padding-inline: var(--space-3); - border-radius: var(--radius-md); - border: var(--border-width) solid transparent; - background: transparent; - color: var(--color-content-primary); - font: inherit; - font-size: var(--font-size-sm); - line-height: var(--leading-ui); - cursor: pointer; - transition: - background var(--duration-fast) var(--ease-default), - border-color var(--duration-fast) var(--ease-default), - color var(--duration-fast) var(--ease-default); -} - -[data-date-picker-clear]:hover, -[data-date-picker-cancel]:hover { - background: var(--color-surface-overlay); -} - -[data-date-picker-close] { - border-color: var(--color-primary-border); - background: var(--color-primary-solid); - color: var(--color-content-on-solid); -} - -[data-date-picker-close]:hover { - background: var(--color-primary-solid-hover); -} - -[data-date-picker-clear]:focus-visible, -[data-date-picker-cancel]:focus-visible, -[data-date-picker-close]:focus-visible { - outline: var(--focus-ring-width) solid var(--color-primary-border); - outline-offset: var(--focus-ring-offset); -} /* ── Year-view + Month-view (kind='year' / 'month') ──────────────────────── */ @@ -333,11 +276,6 @@ [data-date-picker-trigger] { transition: none; } - [data-date-picker-clear], - [data-date-picker-cancel], - [data-date-picker-close] { - transition: none; - } [data-date-picker-year-view-cell], [data-date-picker-month-view-cell] { transition: none; diff --git a/src/uix/eidos/components/date-picker/index.ts b/src/uix/eidos/components/date-picker/index.ts index 4a55489e1..d047cbd30 100644 --- a/src/uix/eidos/components/date-picker/index.ts +++ b/src/uix/eidos/components/date-picker/index.ts @@ -2,10 +2,14 @@ import DatePickerComponent from './date-picker.svelte'; import Trigger from './date-picker-trigger.svelte'; import Content from './date-picker-content.svelte'; import Calendar from './date-picker-calendar.svelte'; -import Footer from './date-picker-footer.svelte'; -import Clear from './date-picker-clear.svelte'; -import Cancel from './date-picker-cancel.svelte'; -import Close from './date-picker-close.svelte'; +// PickerShell parts — shared by every composite picker. Re-exported +// under the DatePicker namespace so `` keeps working. The shell parts read `commit/cancel/clear` +// off the picker via `pickerShellContext`. +import Footer from '../picker-shell/picker-shell-footer.svelte'; +import Clear from '../picker-shell/picker-shell-clear.svelte'; +import Cancel from '../picker-shell/picker-shell-cancel.svelte'; +import Close from '../picker-shell/picker-shell-close.svelte'; import YearView from './date-picker-year-view.svelte'; import MonthView from './date-picker-month-view.svelte'; import Label from '../date-field/date-field-label.svelte'; diff --git a/src/uix/eidos/components/date-picker/types.ts b/src/uix/eidos/components/date-picker/types.ts index 144b2d8dc..8f7fe3fe8 100644 --- a/src/uix/eidos/components/date-picker/types.ts +++ b/src/uix/eidos/components/date-picker/types.ts @@ -2,7 +2,6 @@ import type { ArrowProps as SomaDatePickerArrowProps, CalendarProps as SomaDatePickerCalendarProps, CalendarSnippetProps as DatePickerCalendarSnippetProps, - CloseProps as SomaDatePickerCloseProps, InputProps as SomaDatePickerInputProps, InputSnippetProps as DatePickerInputSnippetProps, LabelProps as SomaDatePickerLabelProps, @@ -10,6 +9,12 @@ import type { SegmentProps as SomaDatePickerSegmentProps, TriggerProps as SomaDatePickerTriggerProps } from '$soma/components/date-picker'; +import type { + FooterProps as PickerShellFooterProps, + ClearProps as PickerShellClearProps, + CancelProps as PickerShellCancelProps, + CloseProps as PickerShellCloseProps +} from '../picker-shell'; import type { PopoverAnchorProps, PopoverContentProps, @@ -78,7 +83,12 @@ export type DatePickerHiddenInputProps = Record; export type DatePickerAnchorProps = PopoverAnchorProps; export type DatePickerOverlayProps = PopoverOverlayProps; export type DatePickerArrowProps = SomaDatePickerArrowProps; -export type DatePickerCloseProps = SomaDatePickerCloseProps; + +// PickerShell action-row props. +export type DatePickerFooterProps = PickerShellFooterProps; +export type DatePickerClearProps = PickerShellClearProps; +export type DatePickerCancelProps = PickerShellCancelProps; +export type DatePickerCloseProps = PickerShellCloseProps; export type DatePickerHeaderProps = EidosCalendarHeaderProps; export type DatePickerHeadingProps = EidosCalendarHeadingProps; diff --git a/src/uix/eidos/components/date-range-picker/date-range-picker-cancel.svelte b/src/uix/eidos/components/date-range-picker/date-range-picker-cancel.svelte deleted file mode 100644 index 854c828c6..000000000 --- a/src/uix/eidos/components/date-range-picker/date-range-picker-cancel.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/src/uix/eidos/components/date-range-picker/date-range-picker-clear.svelte b/src/uix/eidos/components/date-range-picker/date-range-picker-clear.svelte deleted file mode 100644 index f44831922..000000000 --- a/src/uix/eidos/components/date-range-picker/date-range-picker-clear.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/src/uix/eidos/components/date-range-picker/date-range-picker-close.svelte b/src/uix/eidos/components/date-range-picker/date-range-picker-close.svelte deleted file mode 100644 index e2ccfc744..000000000 --- a/src/uix/eidos/components/date-range-picker/date-range-picker-close.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/src/uix/eidos/components/date-range-picker/date-range-picker-footer.svelte b/src/uix/eidos/components/date-range-picker/date-range-picker-footer.svelte deleted file mode 100644 index f99585302..000000000 --- a/src/uix/eidos/components/date-range-picker/date-range-picker-footer.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/src/uix/eidos/components/date-range-picker/date-range-picker.css b/src/uix/eidos/components/date-range-picker/date-range-picker.css index e02cfa4f2..7e07ce22d 100644 --- a/src/uix/eidos/components/date-range-picker/date-range-picker.css +++ b/src/uix/eidos/components/date-range-picker/date-range-picker.css @@ -933,63 +933,6 @@ outline-offset: var(--focus-ring-offset); } -/* ── Footer (Clear / Cancel / Close action row) ──────────────────────────── */ - -[data-date-range-picker-footer] { - display: flex; - align-items: center; - gap: var(--space-2); - padding-block-start: var(--space-3); - margin-block-start: var(--space-3); - border-block-start: var(--border-width) solid var(--color-border-subtle); -} - -[data-date-range-picker-clear], -[data-date-range-picker-cancel] { - margin-inline-end: auto; -} - -[data-date-range-picker-clear], -[data-date-range-picker-cancel], -[data-date-range-picker-close] { - inline-size: max-content; - min-block-size: var(--control-height-sm); - padding-inline: var(--space-3); - border-radius: var(--radius-md); - border: var(--border-width) solid transparent; - background: transparent; - color: var(--color-content-primary); - font: inherit; - font-size: var(--font-size-sm); - line-height: var(--leading-ui); - cursor: pointer; - transition: - background var(--duration-fast) var(--ease-default), - border-color var(--duration-fast) var(--ease-default), - color var(--duration-fast) var(--ease-default); -} - -[data-date-range-picker-clear]:hover, -[data-date-range-picker-cancel]:hover { - background: var(--color-surface-overlay); -} - -[data-date-range-picker-close] { - border-color: var(--color-primary-border); - background: var(--color-primary-solid); - color: var(--color-content-on-solid); -} - -[data-date-range-picker-close]:hover { - background: var(--color-primary-solid-hover); -} - -[data-date-range-picker-clear]:focus-visible, -[data-date-range-picker-cancel]:focus-visible, -[data-date-range-picker-close]:focus-visible { - outline: var(--focus-ring-width) solid var(--color-primary-border); - outline-offset: var(--focus-ring-offset); -} @media (prefers-reduced-motion: reduce) { [data-date-range-picker-trigger], @@ -999,10 +942,7 @@ [data-range-calendar-next-button], [data-range-calendar-month-select], [data-range-calendar-year-select], - [data-range-calendar-day], - [data-date-range-picker-clear], - [data-date-range-picker-cancel], - [data-date-range-picker-close] { + [data-range-calendar-day] { transition: none; } } diff --git a/src/uix/eidos/components/date-range-picker/index.ts b/src/uix/eidos/components/date-range-picker/index.ts index d1447437e..b21631fdb 100644 --- a/src/uix/eidos/components/date-range-picker/index.ts +++ b/src/uix/eidos/components/date-range-picker/index.ts @@ -8,12 +8,13 @@ import Segment from './date-range-picker-segment.svelte'; import Anchor from '../popover/popover-anchor.svelte'; import Overlay from '../popover/popover-overlay.svelte'; import Arrow from '../popover/popover-arrow.svelte'; -import Close from './date-range-picker-close.svelte'; -import Footer from './date-range-picker-footer.svelte'; +// PickerShell parts — shared by every composite picker. +import Footer from '../picker-shell/picker-shell-footer.svelte'; +import Clear from '../picker-shell/picker-shell-clear.svelte'; +import Cancel from '../picker-shell/picker-shell-cancel.svelte'; +import Close from '../picker-shell/picker-shell-close.svelte'; import YearView from './date-range-picker-year-view.svelte'; import MonthView from './date-range-picker-month-view.svelte'; -import Clear from './date-range-picker-clear.svelte'; -import Cancel from './date-range-picker-cancel.svelte'; import Header from './date-range-picker-header.svelte'; import Heading from './date-range-picker-heading.svelte'; import PrevButton from './date-range-picker-prev-button.svelte'; diff --git a/src/uix/eidos/components/date-range-picker/types.ts b/src/uix/eidos/components/date-range-picker/types.ts index 3a5eb0462..2b2702145 100644 --- a/src/uix/eidos/components/date-range-picker/types.ts +++ b/src/uix/eidos/components/date-range-picker/types.ts @@ -2,7 +2,6 @@ import type { ArrowProps as SomaDateRangePickerArrowProps, CalendarProps as SomaDateRangePickerCalendarProps, CalendarSnippetProps as SomaDateRangePickerCalendarSnippetProps, - CloseProps as SomaDateRangePickerCloseProps, GridBodyProps as SomaDateRangePickerGridBodyProps, GridHeadProps as SomaDateRangePickerGridHeadProps, GridProps as SomaDateRangePickerGridProps, @@ -76,7 +75,14 @@ export type DateRangePickerSegmentProps = SomaDateRangePickerSegmentProps; export type DateRangePickerAnchorProps = PopoverAnchorProps; export type DateRangePickerOverlayProps = PopoverOverlayProps; export type DateRangePickerArrowProps = SomaDateRangePickerArrowProps; -export type DateRangePickerCloseProps = SomaDateRangePickerCloseProps; + +// PickerShell action-row props — Footer/Clear/Cancel/Close are shared. +export type { + FooterProps as DateRangePickerFooterProps, + ClearProps as DateRangePickerClearProps, + CancelProps as DateRangePickerCancelProps, + CloseProps as DateRangePickerCloseProps +} from '../picker-shell'; export type DateRangePickerHeaderProps = SomaDateRangePickerHeaderProps; export type DateRangePickerHeadingProps = SomaDateRangePickerHeadingProps; diff --git a/src/uix/eidos/components/time-picker/index.ts b/src/uix/eidos/components/time-picker/index.ts index b5a1f117f..d94c22e44 100644 --- a/src/uix/eidos/components/time-picker/index.ts +++ b/src/uix/eidos/components/time-picker/index.ts @@ -2,10 +2,11 @@ import TimePickerComponent from './time-picker.svelte' import Trigger from './time-picker-trigger.svelte' import Content from './time-picker-content.svelte' import Clock from './time-picker-clock.svelte' -import Footer from './time-picker-footer.svelte' -import Clear from './time-picker-clear.svelte' -import Cancel from './time-picker-cancel.svelte' -import Close from './time-picker-close.svelte' +// PickerShell parts — shared by every composite picker. +import Footer from '../picker-shell/picker-shell-footer.svelte' +import Clear from '../picker-shell/picker-shell-clear.svelte' +import Cancel from '../picker-shell/picker-shell-cancel.svelte' +import Close from '../picker-shell/picker-shell-close.svelte' import HourSlider from './time-picker-hour-slider.svelte' import MinuteSlider from './time-picker-minute-slider.svelte' import SecondSlider from './time-picker-second-slider.svelte' diff --git a/src/uix/eidos/components/time-picker/time-picker-cancel.svelte b/src/uix/eidos/components/time-picker/time-picker-cancel.svelte deleted file mode 100644 index 6fcd4a20c..000000000 --- a/src/uix/eidos/components/time-picker/time-picker-cancel.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/src/uix/eidos/components/time-picker/time-picker-clear.svelte b/src/uix/eidos/components/time-picker/time-picker-clear.svelte deleted file mode 100644 index 19d021c08..000000000 --- a/src/uix/eidos/components/time-picker/time-picker-clear.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/src/uix/eidos/components/time-picker/time-picker-close.svelte b/src/uix/eidos/components/time-picker/time-picker-close.svelte deleted file mode 100644 index b50431f19..000000000 --- a/src/uix/eidos/components/time-picker/time-picker-close.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/src/uix/eidos/components/time-picker/time-picker-footer.svelte b/src/uix/eidos/components/time-picker/time-picker-footer.svelte deleted file mode 100644 index 13ef5e13e..000000000 --- a/src/uix/eidos/components/time-picker/time-picker-footer.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/src/uix/eidos/components/time-picker/time-picker.css b/src/uix/eidos/components/time-picker/time-picker.css index 4d3212edd..650341a60 100644 --- a/src/uix/eidos/components/time-picker/time-picker.css +++ b/src/uix/eidos/components/time-picker/time-picker.css @@ -246,69 +246,9 @@ background: var(--_time-field-accent-border); } -/* ── Footer (Clear / Cancel / Close action row) ──────────────────────────── */ - -[data-time-picker-footer] { - display: flex; - align-items: center; - gap: var(--space-2); - padding-block-start: var(--space-3); - margin-block-start: var(--space-3); - border-block-start: var(--border-width) solid var(--color-border-subtle); -} - -[data-time-picker-clear], -[data-time-picker-cancel] { - margin-inline-end: auto; -} - -[data-time-picker-clear], -[data-time-picker-cancel], -[data-time-picker-close] { - inline-size: max-content; - min-block-size: var(--control-height-sm); - padding-inline: var(--space-3); - border-radius: var(--radius-md); - border: var(--border-width) solid transparent; - background: transparent; - color: var(--color-content-primary); - font: inherit; - font-size: var(--font-size-sm); - line-height: var(--leading-ui); - cursor: pointer; - transition: - background var(--duration-fast) var(--ease-default), - border-color var(--duration-fast) var(--ease-default), - color var(--duration-fast) var(--ease-default); -} - -[data-time-picker-clear]:hover, -[data-time-picker-cancel]:hover { - background: var(--color-surface-overlay); -} - -[data-time-picker-close] { - border-color: var(--color-primary-border); - background: var(--color-primary-solid); - color: var(--color-content-on-solid); -} - -[data-time-picker-close]:hover { - background: var(--color-primary-solid-hover); -} - -[data-time-picker-clear]:focus-visible, -[data-time-picker-cancel]:focus-visible, -[data-time-picker-close]:focus-visible { - outline: var(--focus-ring-width) solid var(--color-primary-border); - outline-offset: var(--focus-ring-offset); -} @media (prefers-reduced-motion: reduce) { - [data-time-picker-trigger], - [data-time-picker-clear], - [data-time-picker-cancel], - [data-time-picker-close] { + [data-time-picker-trigger] { transition: none; } } diff --git a/src/uix/eidos/components/time-picker/types.ts b/src/uix/eidos/components/time-picker/types.ts index a6cb97660..a98d0c145 100644 --- a/src/uix/eidos/components/time-picker/types.ts +++ b/src/uix/eidos/components/time-picker/types.ts @@ -20,7 +20,6 @@ import type { } from '../popover/types' import type { ColorRole, ControlVariant, ResponsiveProp, Size } from '$uix/eidos/lib/types' import type { Snippet } from 'svelte' -import type { HTMLButtonAttributes } from 'svelte/elements' export type TimePickerSize = Extract export type TimePickerVariant = ControlVariant @@ -60,24 +59,13 @@ export type TimePickerClockProps = { children?: Snippet } -export type TimePickerFooterProps = { - id?: string - class?: string - style?: string - children?: Snippet -} - -export type TimePickerClearProps = HTMLButtonAttributes & { - children?: Snippet -} - -export type TimePickerCancelProps = HTMLButtonAttributes & { - children?: Snippet -} - -export type TimePickerCloseProps = HTMLButtonAttributes & { - children?: Snippet -} +// PickerShell action-row props — Footer/Clear/Cancel/Close are shared. +export type { + FooterProps as TimePickerFooterProps, + ClearProps as TimePickerClearProps, + CancelProps as TimePickerCancelProps, + CloseProps as TimePickerCloseProps +} from '../picker-shell' export type TimePickerLabelProps = SomaTimePickerLabelProps export type TimePickerInputProps = SomaTimePickerInputProps diff --git a/src/uix/morfo/components/date-picker.ts b/src/uix/morfo/components/date-picker.ts index 461c86da6..5a53c1555 100644 --- a/src/uix/morfo/components/date-picker.ts +++ b/src/uix/morfo/components/date-picker.ts @@ -231,78 +231,11 @@ export const datePickerMorfo = { { key: 'Tab', action: 'focus-next' }, { key: 'Shift+Tab', action: 'focus-prev' } ] - }, - { - // Optional footer rendered inside the Calendar. Visibility is owned - // by the consumer (`clearButton`/`cancelButton`/`closeButton` props - // on the root). In `mode='modal'` the Close affordance is forced on. - name: 'Footer', - kebab: 'footer', - archetype: 'footer', - kind: 'public', - defaultElement: 'div', - optional: true, - data: [{ attr: 'data-mode', values: ['inline', 'modal'], severity: 'optional' }], - aria: [] - }, - { - // Clear action — resets value to undefined, keeps the popover open. - name: 'Clear', - kebab: 'clear', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('clear') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.date-picker.clear|Clear date'), - severity: 'recommended' - } - ] - }, - { - // Cancel action — reverts to the value snapshot taken on open, closes. - name: 'Cancel', - kebab: 'cancel', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('cancel') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.date-picker.cancel|Cancel'), - severity: 'recommended' - } - ] - }, - { - // Close / Save action — commits current value and closes. In modal - // mode this is the only affordance to exit (outside-click + Escape - // are intentionally blocked). - name: 'Close', - kebab: 'close', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('close') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.date-picker.close|Close'), - severity: 'recommended' - } - ] } + // Footer / Clear / Cancel / Close — declared by the shared + // `picker-shell` (Eidos ``). + // Their data-attrs are `[data-picker-footer/clear/cancel/close]` + // (no per-picker prefix). The audit / runtime won't find them in + // this morfo because they're chrome shared across pickers. ] } as const satisfies Morfo; diff --git a/src/uix/morfo/components/date-range-picker.ts b/src/uix/morfo/components/date-range-picker.ts index cb802db9b..5de58498d 100644 --- a/src/uix/morfo/components/date-range-picker.ts +++ b/src/uix/morfo/components/date-range-picker.ts @@ -237,78 +237,9 @@ export const dateRangePickerMorfo = { { key: 'Tab', action: 'focus-next' }, { key: 'Shift+Tab', action: 'focus-prev' } ] - }, - { - // Optional footer rendered inside the Calendar. Visibility is owned - // by the consumer (`clearButton`/`cancelButton`/`closeButton` props - // on the root). In `mode='modal'` the Close affordance is forced on. - name: 'Footer', - kebab: 'footer', - archetype: 'footer', - kind: 'public', - defaultElement: 'div', - optional: true, - data: [{ attr: 'data-mode', values: ['inline', 'modal'], severity: 'optional' }], - aria: [] - }, - { - // Clear action — resets value to an empty range, keeps the popover open. - name: 'Clear', - kebab: 'clear', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('clear') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.date-range-picker.clear|Clear range'), - severity: 'recommended' - } - ] - }, - { - // Cancel action — reverts to the value snapshot taken on open, closes. - name: 'Cancel', - kebab: 'cancel', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('cancel') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.date-range-picker.cancel|Cancel'), - severity: 'recommended' - } - ] - }, - { - // Close / Save action — commits current value and closes. In modal - // mode this is the only affordance to exit (outside-click + Escape - // are intentionally blocked). - name: 'Close', - kebab: 'close', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('close') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.date-range-picker.close|Close'), - severity: 'recommended' - } - ] } + // Footer / Clear / Cancel / Close — chrome shared via picker-shell; + // not declared here. Eidos `` re-exports the `` parts. ] } as const satisfies Morfo; diff --git a/src/uix/morfo/components/time-picker.ts b/src/uix/morfo/components/time-picker.ts index 60d424c46..dfe307f1c 100644 --- a/src/uix/morfo/components/time-picker.ts +++ b/src/uix/morfo/components/time-picker.ts @@ -308,70 +308,9 @@ export const timePickerMorfo = { optional: true, data: [{ attr: 'data-checked', severity: 'optional' }], aria: [] - }, - { - name: 'Footer', - kebab: 'footer', - archetype: 'footer', - kind: 'public', - defaultElement: 'div', - optional: true, - data: [{ attr: 'data-mode', values: ['inline', 'modal'], severity: 'optional' }], - aria: [] - }, - { - name: 'Clear', - kebab: 'clear', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('clear') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.time-picker.clear|Clear time'), - severity: 'recommended' - } - ] - }, - { - name: 'Cancel', - kebab: 'cancel', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('cancel') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.time-picker.cancel|Cancel'), - severity: 'recommended' - } - ] - }, - { - name: 'Close', - kebab: 'close', - archetype: 'trigger', - kind: 'public', - defaultElement: 'button', - role: 'button', - optional: true, - data: [{ attr: 'data-action', value: v.literal('close') }], - aria: [ - { attr: 'type', value: v.literal('button') }, - { - attr: 'aria-label', - value: v.translationRef('#?components.time-picker.close|Close'), - severity: 'recommended' - } - ] } + // Footer / Clear / Cancel / Close — chrome shared via picker-shell; + // not declared here. Eidos `` + // re-exports the `` parts. ] } as const satisfies Morfo; diff --git a/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts b/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts index 500af79d7..d5dcea3ed 100644 --- a/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts +++ b/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts @@ -7,6 +7,10 @@ import { type ActiveProps, type StateProps } from '$libs/reactive'; import type { OnChangeFn, Direction } from '../../types'; import { Soma } from '../../core/soma.svelte'; import { isBefore } from '$libs/days'; +import { + pickerShellContext, + type PickerShellHandle +} from '../picker-shell'; import type { DateValue, DateMatcher, @@ -160,8 +164,19 @@ export class DatePickerProvider { } } ); + + // Expose our commit/cancel/clear/mode handle to descendant Eidos + // PickerShell parts (``, ``, etc.). + pickerShellContext.set(this.pickerShellHandle); } + readonly pickerShellHandle: PickerShellHandle = { + getMode: () => this.opts.mode.current, + commit: () => this.commit(), + cancel: () => this.cancel(), + clear: () => this.clear() + }; + readonly validationStatus = $derived.by(() => { const value = this.opts.value.current; if (!value) return false as const; diff --git a/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts b/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts index b7869165d..c178e4871 100644 --- a/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts +++ b/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts @@ -7,6 +7,10 @@ import { type ActiveProps, type StateProps } from '$libs/reactive'; import type { OnChangeFn, Direction } from '../../types'; import { Soma } from '../../core/soma.svelte'; import { getLocalTimeZone, isBefore } from '$libs/days'; +import { + pickerShellContext, + type PickerShellHandle +} from '../picker-shell'; /** * Picker interaction mode. `inline` (default) auto-closes the popover when @@ -150,8 +154,19 @@ export class DateRangePickerProvider { } } ); + + // Expose our commit/cancel/clear/mode handle to descendant Eidos + // PickerShell parts (``, etc.). + pickerShellContext.set(this.pickerShellHandle); } + readonly pickerShellHandle: PickerShellHandle = { + getMode: () => this.opts.mode.current, + commit: () => this.commit(), + cancel: () => this.cancel(), + clear: () => this.clear() + }; + readonly validationStatus = $derived.by(() => { const value = this.opts.value.current; const start = value.start; diff --git a/src/uix/soma/components/time-picker/time-picker-provider.svelte.ts b/src/uix/soma/components/time-picker/time-picker-provider.svelte.ts index c4e150e2a..23f8f1132 100644 --- a/src/uix/soma/components/time-picker/time-picker-provider.svelte.ts +++ b/src/uix/soma/components/time-picker/time-picker-provider.svelte.ts @@ -17,6 +17,10 @@ import type { import { timePickerMorfo } from '../../../morfo/components/time-picker'; import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte'; +import { + pickerShellContext, + type PickerShellHandle +} from '../picker-shell'; const attrs = createAttrs(timePickerMorfo); @@ -109,8 +113,19 @@ export class TimePickerProvider { } } ); + + // Expose our commit/cancel/clear/mode handle to descendant Eidos + // PickerShell parts (``, ``, etc.). + pickerShellContext.set(this.pickerShellHandle); } + readonly pickerShellHandle: PickerShellHandle = { + getMode: () => this.opts.mode.current, + commit: () => this.commit(), + cancel: () => this.cancel(), + clear: () => this.clear() + }; + /** Resolved hour cycle — same rule as TimeField: explicit → service → locale. */ readonly resolvedHourCycle: HourCycle = $derived.by(() => { const explicit = this.opts.hourCycle.current;