User feedback batch from incidencia 2026-05-20:
1. Modal vs inline mode
- New `mode: 'inline' | 'modal'` prop on the soma `DateRangePicker.Provider`.
- Modal wires the popover `modal: true` → outside-click and Escape are
ignored; user must commit via the footer Close button (or revert via
Cancel).
- Provider exposes helpers `clear()`, `cancel()`, `commit()` for the
footer. `cancel()` restores the value snapshot taken on the OPEN edge
(captured via a `watch` on `opts.open` true-edge transition).
2. Footer buttons as boolean props
- `clearButton`, `cancelButton`, `closeButton` props on the picker.
The footer renders only if at least one is true. In `mode='modal'`
the closeButton is forced on (the user always needs an exit).
- `picker-actions.svelte` in the demo reads the picker context via
`DateRangePickerProvider.require()` and renders the enabled buttons
against `provider.clear/cancel/commit`.
3. Range field shape (revert to Chakra-style two boxes)
- Removed the `data-date-range-field-group` wrapper from the demo so
the start and end inputs are rendered as two separate boxed fields
with the icon embedded in the end box, matching Chakra's layout.
- The recipe CSS rules for `data-date-range-field-group` stay
available as an opt-in for consumers who prefer the unified pill.
4. Demo defaults
- `open` starts at `false` so the picker exercises the real
open/close flow when the user clicks the trigger — the segments are
for direct keyboard entry, the popover is for visual exploration.
- Mode toggle (inline / modal) + footer button switches surfaced as
controls in the demo.
5. Plumbing
- DateRangePickerOpts gains `mode`, `clearButton`, `cancelButton`,
`closeButton` (StateProps for mode, ActiveProps for the booleans).
- `DateRangePickerProvider` and `DateRangePickerMode` are now re-
exported from the soma barrel for consumers that wire footer
actions in the calendar tree.
- Test factory updated to seed the new opts.
Verified in browser: trigger opens; click-outside in modal mode is
ignored; Close commits & closes; Cancel reverts to snapshot & closes;
Clear empties the range & keeps open. Heading "May – June 2026" (year
collapse) and centered per-calendar titles still working from the
previous commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
parent
0d164b54de
commit
383e2ff7f7
@ -0,0 +1,111 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { DateRangePickerProvider } from '$soma/components/date-range-picker';
|
||||||
|
|
||||||
|
// Footer for the DateRangePicker calendar. Lives inside the provider tree
|
||||||
|
// (rendered from the Calendar snippet) so it can pull options + helpers
|
||||||
|
// straight from context.
|
||||||
|
//
|
||||||
|
// Buttons are configured via props on the picker:
|
||||||
|
// - `clearButton` — Clear (reset value, keep open)
|
||||||
|
// - `cancelButton` — Cancel (revert to value-at-open, close)
|
||||||
|
// - `closeButton` — Close / Save (commit current value, close)
|
||||||
|
//
|
||||||
|
// In `mode='modal'` Close is forced on so the user always has a way out:
|
||||||
|
// modal popovers ignore outside-click and Escape.
|
||||||
|
//
|
||||||
|
// If no buttons are enabled (and the picker is not modal), this footer
|
||||||
|
// renders nothing.
|
||||||
|
const provider = DateRangePickerProvider.require();
|
||||||
|
const mode = $derived(provider.opts.mode.current);
|
||||||
|
const showClear = $derived(provider.opts.clearButton.current);
|
||||||
|
const showCancel = $derived(provider.opts.cancelButton.current);
|
||||||
|
const showClose = $derived(provider.opts.closeButton.current || mode === 'modal');
|
||||||
|
const visible = $derived(showClear || showCancel || showClose);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if visible}
|
||||||
|
<div class="picker-footer" data-date-range-picker-footer data-mode={mode}>
|
||||||
|
{#if showClear}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="picker-footer-button"
|
||||||
|
data-tone="ghost"
|
||||||
|
onclick={() => provider.clear()}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
<div class="picker-footer-spacer"></div>
|
||||||
|
{#if showCancel}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="picker-footer-button"
|
||||||
|
data-tone="ghost"
|
||||||
|
onclick={() => provider.cancel()}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{#if showClose}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="picker-footer-button"
|
||||||
|
data-tone="solid"
|
||||||
|
onclick={() => provider.commit()}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.picker-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--uix-space-2, 0.5rem);
|
||||||
|
padding-block-start: var(--uix-space-3, 0.75rem);
|
||||||
|
margin-block-start: var(--uix-space-3, 0.75rem);
|
||||||
|
border-block-start: 1px solid var(--color-border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-footer-spacer {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-footer-button {
|
||||||
|
inline-size: max-content;
|
||||||
|
min-block-size: var(--control-height-sm);
|
||||||
|
padding-inline: var(--uix-space-3, 0.75rem);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: var(--border-width) solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-content-primary);
|
||||||
|
font: inherit;
|
||||||
|
font-size: var(--uix-text-sm);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-footer-button[data-tone='ghost']:hover {
|
||||||
|
background: var(--color-surface-overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-footer-button[data-tone='solid'] {
|
||||||
|
border-color: var(--color-primary-border);
|
||||||
|
background: var(--color-primary-solid);
|
||||||
|
color: var(--color-content-on-solid);
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-footer-button[data-tone='solid']:hover {
|
||||||
|
background: var(--color-primary-solid-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-footer-button:focus-visible {
|
||||||
|
outline: var(--focus-ring-width) solid var(--color-primary-border);
|
||||||
|
outline-offset: var(--focus-ring-offset);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in new issue