feat(picker-shell): extend to Root + Header + Body + Footer layout

Promote PickerShell from action-row-only to a canonical 4-zone shell
(Root → Header / Body / Footer) shared by every composite picker so
date/time/color popovers get the same visual contract for free.

- Add `<PickerShell.Root>` flex-column wrapper (emits data-picker-shell,
  data-mode) plus `<PickerShell.Header>` and `<PickerShell.Body>` zones
  matching the existing Footer's spacing + border tokens.
- Auto-wrap all 5 *-content.svelte popovers (date / date-range / time /
  time-range / color) in `<PickerShell.Root>` so consumers only compose
  Body + Footer.
- Refresh all 5 demos to use `<PickerShell.Body>` around picker content;
  date-picker demo gains a `showShellHeader` toggle showcasing the new
  Header zone.
- CSS scales section gap + header spacing across xs/sm/md/lg picker
  sizes via [data-popover-content][data-picker-size] scopes, in sync
  with the existing Footer scale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
dev 5 months ago
parent da10548a68
commit afb09c2254

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { ActiveEidos } from '$uix/eidos'; import { ActiveEidos } from '$uix/eidos';
import PopoverContent from '../popover/popover-content.svelte'; import PopoverContent from '../popover/popover-content.svelte';
import PickerShellRoot from '../picker-shell/picker-shell.svelte';
import { getColorPickerVisualContext } from './context.svelte'; import { getColorPickerVisualContext } from './context.svelte';
import type { ColorPickerContentProps } from './types'; import type { ColorPickerContentProps } from './types';
@ -28,6 +29,8 @@
data-color-picker-content="" data-color-picker-content=""
> >
{#snippet children(snippetProps)} {#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)} <PickerShellRoot>
{@render bodyContent?.(snippetProps)}
</PickerShellRoot>
{/snippet} {/snippet}
</PopoverContent> </PopoverContent>

@ -2,6 +2,7 @@
import { ActiveEidos } from '$uix/eidos'; import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style'; import { composeInlineStyle } from '$uix/eidos/lib/style';
import PopoverContent from '../popover/popover-content.svelte'; import PopoverContent from '../popover/popover-content.svelte';
import PickerShellRoot from '../picker-shell/picker-shell.svelte';
import { getDatePickerVisualContext } from './context.svelte'; import { getDatePickerVisualContext } from './context.svelte';
import type { DatePickerContentProps } from './types'; import type { DatePickerContentProps } from './types';
@ -71,6 +72,8 @@
style={composedStyle} style={composedStyle}
> >
{#snippet children(snippetProps)} {#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)} <PickerShellRoot>
{@render bodyContent?.(snippetProps)}
</PickerShellRoot>
{/snippet} {/snippet}
</PopoverContent> </PopoverContent>

@ -2,6 +2,7 @@
import { ActiveEidos } from '$uix/eidos'; import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style'; import { composeInlineStyle } from '$uix/eidos/lib/style';
import PopoverContent from '../popover/popover-content.svelte'; import PopoverContent from '../popover/popover-content.svelte';
import PickerShellRoot from '../picker-shell/picker-shell.svelte';
import { getDateRangePickerVisualContext } from './context.svelte'; import { getDateRangePickerVisualContext } from './context.svelte';
import type { DateRangePickerContentProps } from './types'; import type { DateRangePickerContentProps } from './types';
@ -71,6 +72,8 @@
style={composedStyle} style={composedStyle}
> >
{#snippet children(snippetProps)} {#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)} <PickerShellRoot>
{@render bodyContent?.(snippetProps)}
</PickerShellRoot>
{/snippet} {/snippet}
</PopoverContent> </PopoverContent>

@ -1,41 +1,64 @@
import PickerShellRoot from './picker-shell.svelte'
import Header from './picker-shell-header.svelte'
import Body from './picker-shell-body.svelte'
import Footer from './picker-shell-footer.svelte' import Footer from './picker-shell-footer.svelte'
import Clear from './picker-shell-clear.svelte' import Clear from './picker-shell-clear.svelte'
import Cancel from './picker-shell-cancel.svelte' import Cancel from './picker-shell-cancel.svelte'
import Close from './picker-shell-close.svelte' import Close from './picker-shell-close.svelte'
/** /**
* PickerShell — generic Footer + action row shared by every composite * PickerShell — canonical 4-zone layout (Root → Header / Body / Footer)
* picker that registers a `PickerShellHandle` via `pickerShellContext`. * + action row shared by every composite picker that registers a
* `PickerShellHandle` via `pickerShellContext`.
* *
* import { PickerShell } from '$uix/eidos/components/picker-shell'; * import { PickerShell } from '$uix/eidos/components/picker-shell';
* *
* <PickerShell.Footer> * <PickerShell>
* <PickerShell.Clear /> * <PickerShell.Header>...</PickerShell.Header>
* <PickerShell.Cancel /> * <PickerShell.Body>
* <PickerShell.Close /> * <DatePicker.Calendar>...</DatePicker.Calendar>
* </PickerShell.Footer> * </PickerShell.Body>
* <PickerShell.Footer>
* <PickerShell.Clear />
* <PickerShell.Cancel />
* <PickerShell.Close />
* </PickerShell.Footer>
* </PickerShell>
* *
* Each composite picker re-exports these as `X.Footer / X.Clear / …` * Header is optional. Footer / Clear / Cancel / Close are also exposed
* for namespace consistency: * under each composite picker's namespace for symmetry
* * (`<DatePicker.Footer>`, `<ColorPicker.Clear>`, …).
* <ColorPicker.Footer>
* <ColorPicker.Clear />
* <ColorPicker.Cancel />
* <ColorPicker.Close />
* </ColorPicker.Footer>
*
* is the same render — both reach the host picker through context.
*/ */
export const PickerShell = {
Footer, type PickerShellNamespace = typeof PickerShellRoot & {
Clear, Root: typeof PickerShellRoot
Cancel, Header: typeof Header
Close Body: typeof Body
Footer: typeof Footer
Clear: typeof Clear
Cancel: typeof Cancel
Close: typeof Close
} }
export { Footer, Clear, Cancel, Close } const PickerShell = PickerShellRoot as PickerShellNamespace
PickerShell.Root = PickerShellRoot
PickerShell.Header = Header
PickerShell.Body = Body
PickerShell.Footer = Footer
PickerShell.Clear = Clear
PickerShell.Cancel = Cancel
PickerShell.Close = Close
export { PickerShell }
export default PickerShell
export { PickerShellRoot as Root, Header, Body, Footer, Clear, Cancel, Close }
export type { export type {
PickerShellProps as RootProps,
PickerShellProps,
PickerShellHeaderProps as HeaderProps,
PickerShellBodyProps as BodyProps,
PickerShellFooterProps as FooterProps, PickerShellFooterProps as FooterProps,
PickerShellClearProps as ClearProps, PickerShellClearProps as ClearProps,
PickerShellCancelProps as CancelProps, PickerShellCancelProps as CancelProps,

@ -0,0 +1,12 @@
<script lang="ts">
import type { PickerShellBodyProps } from './types';
// Body zone — the scrollable middle area that contains the
// picker-specific content (calendar / clock / color area + sliders).
// Pure layout, no behaviour.
let { children, ...rest }: PickerShellBodyProps = $props();
</script>
<div data-picker-body {...rest}>
{@render children?.()}
</div>

@ -0,0 +1,12 @@
<script lang="ts">
import type { PickerShellHeaderProps } from './types';
// Header zone — optional top strip inside a picker popover. Mirrors
// the Footer's bottom border by emitting a bottom-border + spacing
// block via [data-picker-header] CSS. Pure layout, no behaviour.
let { children, ...rest }: PickerShellHeaderProps = $props();
</script>
<div data-picker-header {...rest}>
{@render children?.()}
</div>

@ -1,12 +1,51 @@
/* PickerShell — visual layer for the canonical Footer + action row /* PickerShell — visual layer for the canonical 4-zone layout
* shared by every composite picker (date, time, color, range). * (Root → Header / Body / Footer) + action row shared by every
* composite picker (date, time, color, range).
* *
* The host picker scopes us via the parent `[data-{x}-picker]` / * The host picker scopes us via the parent `[data-{x}-picker]` /
* `[data-{x}-picker-content]` element, but the action selectors * `[data-{x}-picker-content]` element, but the layout selectors
* (`[data-picker-footer/clear/cancel/close]`) are intentionally generic * (`[data-picker-shell/header/body/footer/clear/cancel/close]`) are
* so every picker gets the same visual contract for free. * intentionally generic so every picker gets the same visual contract
* for free.
*/ */
[data-picker-shell] {
/* Outer column wrapper. Size-scaling vars default to `sm`; host
* pickers re-tune via `[data-popover-content][data-picker-size='X']`
* scopes below. */
--_picker-shell-section-gap: var(--space-3);
display: flex;
flex-direction: column;
gap: var(--_picker-shell-section-gap);
min-block-size: 0;
}
[data-picker-header] {
/* Mirror the Footer's bottom border by emitting a bottom-border +
* spacing block. Reuses the same `--_picker-shell-row-spacing` token
* so xs/sm/md/lg scale symmetrically with the action row below. */
--_picker-shell-row-spacing: var(--space-3);
display: flex;
align-items: center;
gap: var(--space-2);
padding-block-end: var(--_picker-shell-row-spacing);
border-block-end: var(--border-width) solid var(--color-border-subtle);
color: var(--color-content-primary);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
line-height: var(--leading-ui);
}
[data-picker-body] {
display: flex;
flex-direction: column;
gap: var(--space-3);
flex: 1 1 auto;
min-block-size: 0;
}
[data-picker-footer] { [data-picker-footer] {
/* Size-scaling vars. Defaults match `sm`. The host picker scopes these /* Size-scaling vars. Defaults match `sm`. The host picker scopes these
* via `[data-popover-content][data-picker-size='X']` so xs/md/lg tune * via `[data-popover-content][data-picker-size='X']` so xs/md/lg tune
@ -21,10 +60,15 @@
align-items: center; align-items: center;
gap: var(--_picker-shell-gap); gap: var(--_picker-shell-gap);
padding-block-start: var(--_picker-shell-row-spacing); padding-block-start: var(--_picker-shell-row-spacing);
margin-block-start: var(--_picker-shell-row-spacing);
border-block-start: var(--border-width) solid var(--color-border-subtle); border-block-start: var(--border-width) solid var(--color-border-subtle);
} }
/* Standalone Footer (legacy composition without Root) keeps its own
* top margin so it still feels separated from picker content above. */
[data-picker-footer]:not([data-picker-shell] > *) {
margin-block-start: var(--_picker-shell-row-spacing);
}
[data-picker-clear], [data-picker-clear],
[data-picker-cancel] { [data-picker-cancel] {
margin-inline-end: auto; margin-inline-end: auto;
@ -51,9 +95,17 @@
} }
/* Size variants — host pickers emit `data-picker-size` on the popover /* Size variants — host pickers emit `data-picker-size` on the popover
* content so portaled descendants can scale. The button row inherits * content so portaled descendants can scale. The whole shell (gap,
* the picker's size; popover chrome (padding / min-width) keeps its * header spacing, button row) inherits the picker's size; popover
* own size variant via [data-size]. */ * chrome (padding / min-width) keeps its own size variant via
* [data-size]. */
[data-popover-content][data-picker-size='xs'] [data-picker-shell] {
--_picker-shell-section-gap: var(--space-2);
}
[data-popover-content][data-picker-size='xs'] [data-picker-header] {
--_picker-shell-row-spacing: var(--space-2);
font-size: var(--font-size-xs);
}
[data-popover-content][data-picker-size='xs'] [data-picker-footer] { [data-popover-content][data-picker-size='xs'] [data-picker-footer] {
--_picker-shell-control-h: var(--control-height-xxs); --_picker-shell-control-h: var(--control-height-xxs);
--_picker-shell-padding-x: var(--space-2); --_picker-shell-padding-x: var(--space-2);
@ -62,12 +114,25 @@
--_picker-shell-row-spacing: var(--space-2); --_picker-shell-row-spacing: var(--space-2);
} }
[data-popover-content][data-picker-size='md'] [data-picker-shell] {
--_picker-shell-section-gap: var(--space-3-5);
}
[data-popover-content][data-picker-size='md'] [data-picker-header] {
font-size: var(--font-size-md);
}
[data-popover-content][data-picker-size='md'] [data-picker-footer] { [data-popover-content][data-picker-size='md'] [data-picker-footer] {
--_picker-shell-control-h: var(--control-height-md); --_picker-shell-control-h: var(--control-height-md);
--_picker-shell-padding-x: var(--space-3-5); --_picker-shell-padding-x: var(--space-3-5);
--_picker-shell-font: var(--font-size-md); --_picker-shell-font: var(--font-size-md);
} }
[data-popover-content][data-picker-size='lg'] [data-picker-shell] {
--_picker-shell-section-gap: var(--space-4);
}
[data-popover-content][data-picker-size='lg'] [data-picker-header] {
--_picker-shell-row-spacing: var(--space-4);
font-size: var(--font-size-md);
}
[data-popover-content][data-picker-size='lg'] [data-picker-footer] { [data-popover-content][data-picker-size='lg'] [data-picker-footer] {
--_picker-shell-control-h: var(--control-height-lg); --_picker-shell-control-h: var(--control-height-lg);
--_picker-shell-padding-x: var(--space-4); --_picker-shell-padding-x: var(--space-4);

@ -0,0 +1,17 @@
<script lang="ts">
import { getPickerShellHandle } from '$soma/components/picker-shell';
import type { PickerShellProps } from './types';
// PickerShell root — outer flex column wrapping the canonical
// Header / Body / Footer zones for every composite picker. Pure
// composition; reads `mode` off `pickerShellContext` when available so
// the CSS can react to inline vs modal.
let { children, ...rest }: PickerShellProps = $props();
const shell = getPickerShellHandle();
const mode = $derived(shell?.getMode() ?? 'inline');
</script>
<div data-picker-shell data-mode={mode} {...rest}>
{@render children?.()}
</div>

@ -1,6 +1,21 @@
import type { Snippet } from 'svelte' import type { Snippet } from 'svelte'
import type { HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements' import type { HTMLAttributes, HTMLButtonAttributes } from 'svelte/elements'
/** Props for `<PickerShell>` / `<PickerShell.Root>` — outer 4-zone layout. */
export type PickerShellProps = HTMLAttributes<HTMLDivElement> & {
children?: Snippet
}
/** Props for `<PickerShell.Header>`. */
export type PickerShellHeaderProps = HTMLAttributes<HTMLDivElement> & {
children?: Snippet
}
/** Props for `<PickerShell.Body>`. */
export type PickerShellBodyProps = HTMLAttributes<HTMLDivElement> & {
children?: Snippet
}
/** Props for `<PickerShell.Footer>`. */ /** Props for `<PickerShell.Footer>`. */
export type PickerShellFooterProps = HTMLAttributes<HTMLDivElement> & { export type PickerShellFooterProps = HTMLAttributes<HTMLDivElement> & {
children?: Snippet children?: Snippet

@ -2,6 +2,7 @@
import { ActiveEidos } from '$uix/eidos'; import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style'; import { composeInlineStyle } from '$uix/eidos/lib/style';
import PopoverContent from '../popover/popover-content.svelte'; import PopoverContent from '../popover/popover-content.svelte';
import PickerShellRoot from '../picker-shell/picker-shell.svelte';
import { getTimePickerVisualContext } from './context.svelte'; import { getTimePickerVisualContext } from './context.svelte';
import type { TimePickerContentProps } from './types'; import type { TimePickerContentProps } from './types';
@ -67,6 +68,8 @@
style={composedStyle} style={composedStyle}
> >
{#snippet children(snippetProps)} {#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)} <PickerShellRoot>
{@render bodyContent?.(snippetProps)}
</PickerShellRoot>
{/snippet} {/snippet}
</PopoverContent> </PopoverContent>

@ -2,6 +2,7 @@
import { ActiveEidos } from '$uix/eidos'; import { ActiveEidos } from '$uix/eidos';
import { composeInlineStyle } from '$uix/eidos/lib/style'; import { composeInlineStyle } from '$uix/eidos/lib/style';
import PopoverContent from '../popover/popover-content.svelte'; import PopoverContent from '../popover/popover-content.svelte';
import PickerShellRoot from '../picker-shell/picker-shell.svelte';
import { getTimeRangePickerVisualContext } from './context.svelte'; import { getTimeRangePickerVisualContext } from './context.svelte';
import type { TimeRangePickerContentProps } from './types'; import type { TimeRangePickerContentProps } from './types';
@ -67,6 +68,8 @@
style={composedStyle} style={composedStyle}
> >
{#snippet children(snippetProps)} {#snippet children(snippetProps)}
{@render bodyContent?.(snippetProps)} <PickerShellRoot>
{@render bodyContent?.(snippetProps)}
</PickerShellRoot>
{/snippet} {/snippet}
</PopoverContent> </PopoverContent>

@ -6,6 +6,7 @@
type ColorPickerVariant, type ColorPickerVariant,
type ChannelInputSnippetProps type ChannelInputSnippetProps
} from '$uix/eidos/components/color-picker'; } from '$uix/eidos/components/color-picker';
import { PickerShell } from '$uix/eidos/components/picker-shell';
import { compileMorfo } from '$uix/morfo'; import { compileMorfo } from '$uix/morfo';
import { colorPickerMorfo } from '@/uix/morfo/components/color-picker'; import { colorPickerMorfo } from '@/uix/morfo/components/color-picker';
import { colorFieldMorfo } from '@/uix/morfo/components/color-field'; import { colorFieldMorfo } from '@/uix/morfo/components/color-field';
@ -149,25 +150,27 @@
' <ColorPicker.Trigger><ColorPicker.ValueText /></ColorPicker.Trigger>', ' <ColorPicker.Trigger><ColorPicker.ValueText /></ColorPicker.Trigger>',
' </ColorPicker.Control>', ' </ColorPicker.Control>',
' <ColorPicker.Content>', ' <ColorPicker.Content>',
' <ColorPicker.Area>', ' <PickerShell.Body>',
' <ColorPicker.AreaBackground />', ' <ColorPicker.Area>',
' <ColorPicker.AreaThumb />', ' <ColorPicker.AreaBackground />',
' </ColorPicker.Area>', ' <ColorPicker.AreaThumb />',
' <ColorPicker.ChannelSlider channel="hue">', ' </ColorPicker.Area>',
' <ColorPicker.ChannelSliderTrack /><ColorPicker.ChannelSliderThumb />', ' <ColorPicker.ChannelSlider channel="hue">',
' </ColorPicker.ChannelSlider>', ' <ColorPicker.ChannelSliderTrack /><ColorPicker.ChannelSliderThumb />',
enableAlpha && ' <ColorPicker.ChannelSlider channel="alpha">', ' </ColorPicker.ChannelSlider>',
enableAlpha && ' <ColorPicker.ChannelSliderTrack /><ColorPicker.ChannelSliderThumb />', enableAlpha && ' <ColorPicker.ChannelSlider channel="alpha">',
enableAlpha && ' </ColorPicker.ChannelSlider>', enableAlpha && ' <ColorPicker.ChannelSliderTrack /><ColorPicker.ChannelSliderThumb />',
' <ColorPicker.ValueSwatch />', enableAlpha && ' </ColorPicker.ChannelSlider>',
' <ColorPicker.ChannelInput />', ' <ColorPicker.ValueSwatch />',
' <ColorPicker.EyeDropper />', ' <ColorPicker.ChannelInput />',
' <ColorPicker.SwatchGroup>', ' <ColorPicker.EyeDropper />',
' {#each presets as c}<ColorPicker.SwatchTrigger color={c}>', ' <ColorPicker.SwatchGroup>',
' <ColorPicker.Swatch color={c} />', ' {#each presets as c}<ColorPicker.SwatchTrigger color={c}>',
' <ColorPicker.SwatchIndicator />', ' <ColorPicker.Swatch color={c} />',
' </ColorPicker.SwatchTrigger>{/each}', ' <ColorPicker.SwatchIndicator />',
' </ColorPicker.SwatchGroup>', ' </ColorPicker.SwatchTrigger>{/each}',
' </ColorPicker.SwatchGroup>',
' </PickerShell.Body>',
showFooter && (showClear || showCancel || showClose) && ' <PickerShell.Footer>', showFooter && (showClear || showCancel || showClose) && ' <PickerShell.Footer>',
showFooter && showClear && ' <PickerShell.Clear />', showFooter && showClear && ' <PickerShell.Clear />',
showFooter && showCancel && ' <PickerShell.Cancel />', showFooter && showCancel && ' <PickerShell.Cancel />',
@ -184,6 +187,7 @@
[ [
"<script lang='ts'>", "<script lang='ts'>",
" import { ColorPicker } from '$uix/eidos/components/color-picker';", " import { ColorPicker } from '$uix/eidos/components/color-picker';",
" import { PickerShell } from '$uix/eidos/components/picker-shell';",
" import { colorValueFromRgb, type ColorValue } from '$libs/color';", " import { colorValueFromRgb, type ColorValue } from '$libs/color';",
'', '',
' let value = $state<ColorValue | undefined>(colorValueFromRgb({ r: 30, g: 144, b: 255, a: 1 }));', ' let value = $state<ColorValue | undefined>(colorValueFromRgb({ r: 30, g: 144, b: 255, a: 1 }));',
@ -207,16 +211,18 @@
' </ColorPicker.Control>', ' </ColorPicker.Control>',
' <ColorPicker.Portal>', ' <ColorPicker.Portal>',
' <ColorPicker.Content>', ' <ColorPicker.Content>',
' <ColorPicker.Area />', ' <PickerShell.Body>',
' <ColorPicker.ChannelSlider channel="hue" />', ' <ColorPicker.Area />',
enableAlpha && ' <ColorPicker.ChannelSlider channel="alpha" />', ' <ColorPicker.ChannelSlider channel="hue" />',
' <!-- compact value row: swatch + channel input + eyedropper -->', enableAlpha && ' <ColorPicker.ChannelSlider channel="alpha" />',
' <ColorPicker.ValueSwatch />', ' <!-- compact value row: swatch + channel input + eyedropper -->',
' <ColorPicker.ChannelInput />', ' <ColorPicker.ValueSwatch />',
' <ColorPicker.EyeDropper />', ' <ColorPicker.ChannelInput />',
' <ColorPicker.SwatchGroup>', ' <ColorPicker.EyeDropper />',
' {#each presets as c}<ColorPicker.SwatchTrigger color={c} />{/each}', ' <ColorPicker.SwatchGroup>',
' </ColorPicker.SwatchGroup>', ' {#each presets as c}<ColorPicker.SwatchTrigger color={c} />{/each}',
' </ColorPicker.SwatchGroup>',
' </PickerShell.Body>',
showFooter && (showClear || showCancel || showClose) && ' <ColorPicker.Footer>', showFooter && (showClear || showCancel || showClose) && ' <ColorPicker.Footer>',
showFooter && showClear && ' <ColorPicker.Clear />', showFooter && showClear && ' <ColorPicker.Clear />',
showFooter && showCancel && ' <ColorPicker.Cancel />', showFooter && showCancel && ' <ColorPicker.Cancel />',
@ -279,45 +285,47 @@
<ColorPicker.Portal> <ColorPicker.Portal>
<ColorPicker.Content> <ColorPicker.Content>
<ColorPicker.Area /> <PickerShell.Body>
<ColorPicker.Area />
<!-- Channel sliders with explicit row labels so users
know what each gradient drives. --> <!-- Channel sliders with explicit row labels so users
<div class="cp-slider-row"> know what each gradient drives. -->
<span class="cp-slider-label">Hue</span>
<ColorPicker.ChannelSlider channel="hue" />
</div>
{#if enableAlpha}
<div class="cp-slider-row"> <div class="cp-slider-row">
<span class="cp-slider-label">Alpha</span> <span class="cp-slider-label">Hue</span>
<ColorPicker.ChannelSlider channel="alpha" /> <ColorPicker.ChannelSlider channel="hue" />
</div>
{#if enableAlpha}
<div class="cp-slider-row">
<span class="cp-slider-label">Alpha</span>
<ColorPicker.ChannelSlider channel="alpha" />
</div>
{/if}
<!-- Compact value row: live preview swatch + segmented channel
input + format select + eyedropper, all on one line. -->
<div class="cp-value-row">
<ColorPicker.ValueSwatch />
<ColorPicker.ChannelInput>
{#snippet children({ segments }: ChannelInputSnippetProps)}
{#each segments as { part, value }, i (`${part}-${i}`)}
<ColorPicker.ChannelSegment {part}>{value}</ColorPicker.ChannelSegment>
{/each}
<ColorPicker.FormatSelect />
{/snippet}
</ColorPicker.ChannelInput>
<ColorPicker.EyeDropper aria-label="Pick color from screen" />
</div> </div>
{/if}
<!-- Compact value row: live preview swatch + segmented channel <div class="cp-section-label">Preset colors</div>
input + format select + eyedropper, all on one line. --> <ColorPicker.SwatchGroup aria-label="Preset colors">
<div class="cp-value-row"> {#each presets as preset}
<ColorPicker.ValueSwatch /> <ColorPicker.SwatchTrigger color={preset}>
<ColorPicker.ChannelInput> <ColorPicker.Swatch color={preset} />
{#snippet children({ segments }: ChannelInputSnippetProps)} <ColorPicker.SwatchIndicator />
{#each segments as { part, value }, i (`${part}-${i}`)} </ColorPicker.SwatchTrigger>
<ColorPicker.ChannelSegment {part}>{value}</ColorPicker.ChannelSegment> {/each}
{/each} </ColorPicker.SwatchGroup>
<ColorPicker.FormatSelect /> </PickerShell.Body>
{/snippet}
</ColorPicker.ChannelInput>
<ColorPicker.EyeDropper aria-label="Pick color from screen" />
</div>
<div class="cp-section-label">Preset colors</div>
<ColorPicker.SwatchGroup aria-label="Preset colors">
{#each presets as preset}
<ColorPicker.SwatchTrigger color={preset}>
<ColorPicker.Swatch color={preset} />
<ColorPicker.SwatchIndicator />
</ColorPicker.SwatchTrigger>
{/each}
</ColorPicker.SwatchGroup>
{#if showFooter && (showClear || showCancel || showClose)} {#if showFooter && (showClear || showCancel || showClose)}
<ColorPicker.Footer> <ColorPicker.Footer>

@ -8,6 +8,7 @@
type DatePickerSize, type DatePickerSize,
type DatePickerVariant type DatePickerVariant
} from '$uix/eidos/components/date-picker'; } from '$uix/eidos/components/date-picker';
import { PickerShell } from '$uix/eidos/components/picker-shell';
import { compileMorfo } from '$uix/morfo'; import { compileMorfo } from '$uix/morfo';
import { datePickerMorfo } from '@/uix/morfo/components/date-picker'; import { datePickerMorfo } from '@/uix/morfo/components/date-picker';
import { calendarMorfo } from '@/uix/morfo/components/calendar'; import { calendarMorfo } from '@/uix/morfo/components/calendar';
@ -65,6 +66,10 @@
let showClear = $state(true); let showClear = $state(true);
let showCancel = $state(true); let showCancel = $state(true);
let showClose = $state(true); let showClose = $state(true);
// PickerShell.Header toggle — demonstrates the optional top zone of
// the canonical 4-part picker layout. The Header sits above Body and
// renders with its own bottom border, mirroring the Footer.
let showShellHeader = $state(false);
let disableDaysOutsideMonth = $state(true); let disableDaysOutsideMonth = $state(true);
let fixedWeeks = $state(false); let fixedWeeks = $state(false);
@ -301,17 +306,25 @@
' </DatePicker.Input>', ' </DatePicker.Input>',
' <DatePicker.Portal>', ' <DatePicker.Portal>',
' <DatePicker.Content align="end" sideOffset={10} collisionPadding={12}>', ' <DatePicker.Content align="end" sideOffset={10} collisionPadding={12}>',
' <DatePicker.Calendar>', showShellHeader && ' <PickerShell.Header>Pick a date</PickerShell.Header>',
' {#snippet children({ months, weekdays })}', ' <PickerShell.Body>',
' <DatePicker.Header>...</DatePicker.Header>', ' <DatePicker.Calendar>',
' {#each months as month}', ' {#snippet children({ months, weekdays })}',
' {#if months.length > 1}', ' <DatePicker.Header>...</DatePicker.Header>',
' <DatePicker.Heading id={monthHeadingId(month.value)} value={month.value} />', ' {#each months as month}',
' {/if}', ' {#if months.length > 1}',
' <DatePicker.Grid>...</DatePicker.Grid>', ' <DatePicker.Heading id={monthHeadingId(month.value)} value={month.value} />',
' {/each}', ' {/if}',
' {/snippet}', ' <DatePicker.Grid>...</DatePicker.Grid>',
' </DatePicker.Calendar>', ' {/each}',
' {/snippet}',
' </DatePicker.Calendar>',
' </PickerShell.Body>',
showFooter && (showClear || showCancel || showClose) && ' <DatePicker.Footer>',
showFooter && showClear && ' <DatePicker.Clear />',
showFooter && showCancel && ' <DatePicker.Cancel />',
showFooter && showClose && ' <DatePicker.Close />',
showFooter && (showClear || showCancel || showClose) && ' </DatePicker.Footer>',
' </DatePicker.Content>', ' </DatePicker.Content>',
' </DatePicker.Portal>', ' </DatePicker.Portal>',
'</DatePicker.Provider>' '</DatePicker.Provider>'
@ -324,6 +337,7 @@
[ [
"<script lang='ts'>", "<script lang='ts'>",
" import { DatePicker } from '$uix/eidos/components/date-picker';", " import { DatePicker } from '$uix/eidos/components/date-picker';",
" import { PickerShell } from '$uix/eidos/components/picker-shell';",
" import { CalendarDate, type DateValue } from '$libs/days';", " import { CalendarDate, type DateValue } from '$libs/days';",
'', '',
` const minValue = ${dateCtor(minValue)};`, ` const minValue = ${dateCtor(minValue)};`,
@ -373,17 +387,25 @@
' </DatePicker.Input>', ' </DatePicker.Input>',
' <DatePicker.Portal>', ' <DatePicker.Portal>',
' <DatePicker.Content align="end" sideOffset={10} collisionPadding={12}>', ' <DatePicker.Content align="end" sideOffset={10} collisionPadding={12}>',
' <DatePicker.Calendar>', showShellHeader && ' <PickerShell.Header>Pick a date</PickerShell.Header>',
' {#snippet children({ months, weekdays })}', ' <PickerShell.Body>',
' <DatePicker.Header>...</DatePicker.Header>', ' <DatePicker.Calendar>',
' {#each months as month}', ' {#snippet children({ months, weekdays })}',
' {#if months.length > 1}', ' <DatePicker.Header>...</DatePicker.Header>',
' <DatePicker.Heading id={monthHeadingId(month.value)} value={month.value} />', ' {#each months as month}',
' {/if}', ' {#if months.length > 1}',
' <DatePicker.Grid>...</DatePicker.Grid>', ' <DatePicker.Heading id={monthHeadingId(month.value)} value={month.value} />',
' {/each}', ' {/if}',
' {/snippet}', ' <DatePicker.Grid>...</DatePicker.Grid>',
' </DatePicker.Calendar>', ' {/each}',
' {/snippet}',
' </DatePicker.Calendar>',
' </PickerShell.Body>',
showFooter && (showClear || showCancel || showClose) && ' <DatePicker.Footer>',
showFooter && showClear && ' <DatePicker.Clear />',
showFooter && showCancel && ' <DatePicker.Cancel />',
showFooter && showClose && ' <DatePicker.Close />',
showFooter && (showClear || showCancel || showClose) && ' </DatePicker.Footer>',
' </DatePicker.Content>', ' </DatePicker.Content>',
' </DatePicker.Portal>', ' </DatePicker.Portal>',
'</DatePicker>' '</DatePicker>'
@ -467,17 +489,22 @@
</div> </div>
<DatePicker.Portal> <DatePicker.Portal>
<DatePicker.Content align="end" sideOffset={10} collisionPadding={12}> <DatePicker.Content align="end" sideOffset={10} collisionPadding={12}>
{#if kind === 'year'} {#if showShellHeader}
<DatePicker.YearView /> <PickerShell.Header>Pick a date</PickerShell.Header>
{:else if kind === 'month'}
<DatePicker.MonthView />
{:else}
<DatePicker.Calendar>
{#snippet children(args: DatePickerCalendarSnippetProps)}
{@render calendarBody(args)}
{/snippet}
</DatePicker.Calendar>
{/if} {/if}
<PickerShell.Body>
{#if kind === 'year'}
<DatePicker.YearView />
{:else if kind === 'month'}
<DatePicker.MonthView />
{:else}
<DatePicker.Calendar>
{#snippet children(args: DatePickerCalendarSnippetProps)}
{@render calendarBody(args)}
{/snippet}
</DatePicker.Calendar>
{/if}
</PickerShell.Body>
{@render pickerFooter()} {@render pickerFooter()}
</DatePicker.Content> </DatePicker.Content>
</DatePicker.Portal> </DatePicker.Portal>
@ -671,6 +698,18 @@
></span ></span
> >
</label> </label>
<label data-uix-control>
<span data-uix-control-label
>shell header <span data-uix-control-hint
>optional &lt;PickerShell.Header&gt; zone</span
></span
>
<span data-uix-switch
><input type="checkbox" bind:checked={showShellHeader} /><span
data-uix-switch-label>Header</span
></span
>
</label>
<label data-uix-control> <label data-uix-control>
<span data-uix-control-label>calendar behavior</span> <span data-uix-control-label>calendar behavior</span>
<span data-uix-switch><input type="checkbox" bind:checked={closeOnDateSelect} /><span data-uix-switch-label>close on select</span></span> <span data-uix-switch><input type="checkbox" bind:checked={closeOnDateSelect} /><span data-uix-switch-label>close on select</span></span>

@ -8,6 +8,7 @@
type DateRangePickerSize, type DateRangePickerSize,
type DateRangePickerVariant type DateRangePickerVariant
} from '$uix/eidos/components/date-range-picker'; } from '$uix/eidos/components/date-range-picker';
import { PickerShell } from '$uix/eidos/components/picker-shell';
import { compileMorfo } from '$uix/morfo'; import { compileMorfo } from '$uix/morfo';
import { dateRangePickerMorfo } from '@/uix/morfo/components/date-range-picker'; import { dateRangePickerMorfo } from '@/uix/morfo/components/date-range-picker';
import { dateRangeFieldMorfo } from '@/uix/morfo/components/date-range-field'; import { dateRangeFieldMorfo } from '@/uix/morfo/components/date-range-field';
@ -387,7 +388,14 @@
' </DateRangePicker.Input>', ' </DateRangePicker.Input>',
' <DateRangePicker.Portal>', ' <DateRangePicker.Portal>',
' <DateRangePicker.Content align="end" sideOffset={10} collisionPadding={12}>', ' <DateRangePicker.Content align="end" sideOffset={10} collisionPadding={12}>',
' <DateRangePicker.Calendar>...</DateRangePicker.Calendar>', ' <PickerShell.Body>',
' <DateRangePicker.Calendar>...</DateRangePicker.Calendar>',
' </PickerShell.Body>',
' <DateRangePicker.Footer>',
' <DateRangePicker.Clear />',
' <DateRangePicker.Cancel />',
' <DateRangePicker.Close />',
' </DateRangePicker.Footer>',
' </DateRangePicker.Content>', ' </DateRangePicker.Content>',
' </DateRangePicker.Portal>', ' </DateRangePicker.Portal>',
'</DateRangePicker.Provider>' '</DateRangePicker.Provider>'
@ -400,6 +408,7 @@
[ [
"<script lang='ts'>", "<script lang='ts'>",
" import { DateRangePicker } from '$uix/eidos/components/date-range-picker';", " import { DateRangePicker } from '$uix/eidos/components/date-range-picker';",
" import { PickerShell } from '$uix/eidos/components/picker-shell';",
" import { CalendarDate, type DateRange, type DateValue } from '$libs/days';", " import { CalendarDate, type DateRange, type DateValue } from '$libs/days';",
'', '',
` const minValue = ${dateCtor(minValue)};`, ` const minValue = ${dateCtor(minValue)};`,
@ -449,7 +458,14 @@
' </div>', ' </div>',
' <DateRangePicker.Portal>', ' <DateRangePicker.Portal>',
' <DateRangePicker.Content align="end" sideOffset={10} collisionPadding={12}>', ' <DateRangePicker.Content align="end" sideOffset={10} collisionPadding={12}>',
' <DateRangePicker.Calendar>...</DateRangePicker.Calendar>', ' <PickerShell.Body>',
' <DateRangePicker.Calendar>...</DateRangePicker.Calendar>',
' </PickerShell.Body>',
' <DateRangePicker.Footer>',
' <DateRangePicker.Clear />',
' <DateRangePicker.Cancel />',
' <DateRangePicker.Close />',
' </DateRangePicker.Footer>',
' </DateRangePicker.Content>', ' </DateRangePicker.Content>',
' </DateRangePicker.Portal>', ' </DateRangePicker.Portal>',
'</DateRangePicker>' '</DateRangePicker>'
@ -539,17 +555,19 @@
</div> </div>
<DateRangePicker.Portal> <DateRangePicker.Portal>
<DateRangePicker.Content align="end" sideOffset={10} collisionPadding={12}> <DateRangePicker.Content align="end" sideOffset={10} collisionPadding={12}>
{#if kind === 'year'} <PickerShell.Body>
<DateRangePicker.YearView /> {#if kind === 'year'}
{:else if kind === 'month'} <DateRangePicker.YearView />
<DateRangePicker.MonthView /> {:else if kind === 'month'}
{:else} <DateRangePicker.MonthView />
<DateRangePicker.Calendar> {:else}
{#snippet children(args: DateRangePickerCalendarSnippetProps)} <DateRangePicker.Calendar>
{@render calendarBody(args)} {#snippet children(args: DateRangePickerCalendarSnippetProps)}
{/snippet} {@render calendarBody(args)}
</DateRangePicker.Calendar> {/snippet}
{/if} </DateRangePicker.Calendar>
{/if}
</PickerShell.Body>
{@render pickerFooter()} {@render pickerFooter()}
</DateRangePicker.Content> </DateRangePicker.Content>
</DateRangePicker.Portal> </DateRangePicker.Portal>

@ -6,6 +6,7 @@
type TimePickerVariant, type TimePickerVariant,
type InputSnippetProps type InputSnippetProps
} from '$uix/eidos/components/time-picker'; } from '$uix/eidos/components/time-picker';
import { PickerShell } from '$uix/eidos/components/picker-shell';
import { compileMorfo } from '$uix/morfo'; import { compileMorfo } from '$uix/morfo';
import { timePickerMorfo } from '@/uix/morfo/components/time-picker'; import { timePickerMorfo } from '@/uix/morfo/components/time-picker';
import { timeFieldMorfo } from '@/uix/morfo/components/time-field'; import { timeFieldMorfo } from '@/uix/morfo/components/time-field';
@ -156,6 +157,7 @@
[ [
"<script lang='ts'>", "<script lang='ts'>",
" import { TimePicker } from '$uix/eidos/components/time-picker';", " import { TimePicker } from '$uix/eidos/components/time-picker';",
" import { PickerShell } from '$uix/eidos/components/picker-shell';",
" import { Time, type TimeValue } from '$libs/days';", " import { Time, type TimeValue } from '$libs/days';",
'', '',
' let value = $state<TimeValue | undefined>(new Time(9, 30, 0));', ' let value = $state<TimeValue | undefined>(new Time(9, 30, 0));',
@ -186,17 +188,19 @@
' </TimePicker.Input>', ' </TimePicker.Input>',
' <TimePicker.Portal>', ' <TimePicker.Portal>',
' <TimePicker.Content>', ' <TimePicker.Content>',
' <TimePicker.Clock>', ' <PickerShell.Body>',
' <TimePicker.HourSlider />', ' <TimePicker.Clock>',
' <TimePicker.MinuteSlider />', ' <TimePicker.HourSlider />',
granularity === 'second' && ' <TimePicker.SecondSlider />', ' <TimePicker.MinuteSlider />',
hourCycle === 12 && ' <TimePicker.DayPeriodToggle />', granularity === 'second' && ' <TimePicker.SecondSlider />',
showFooter && (showClear || showCancel || showClose) && ' <TimePicker.Footer>', hourCycle === 12 && ' <TimePicker.DayPeriodToggle />',
showFooter && showClear && ' <TimePicker.Clear />', ' </TimePicker.Clock>',
showFooter && showCancel && ' <TimePicker.Cancel />', ' </PickerShell.Body>',
showFooter && showClose && ' <TimePicker.Close />', showFooter && (showClear || showCancel || showClose) && ' <TimePicker.Footer>',
showFooter && (showClear || showCancel || showClose) && ' </TimePicker.Footer>', showFooter && showClear && ' <TimePicker.Clear />',
' </TimePicker.Clock>', showFooter && showCancel && ' <TimePicker.Cancel />',
showFooter && showClose && ' <TimePicker.Close />',
showFooter && (showClear || showCancel || showClose) && ' </TimePicker.Footer>',
' </TimePicker.Content>', ' </TimePicker.Content>',
' </TimePicker.Portal>', ' </TimePicker.Portal>',
'</TimePicker>' '</TimePicker>'
@ -259,24 +263,26 @@
<TimePicker.Portal> <TimePicker.Portal>
<TimePicker.Content> <TimePicker.Content>
<TimePicker.Clock> <PickerShell.Body>
<TimePicker.HourSlider /> <TimePicker.Clock>
<TimePicker.MinuteSlider /> <TimePicker.HourSlider />
{#if granularity === 'second'} <TimePicker.MinuteSlider />
<TimePicker.SecondSlider /> {#if granularity === 'second'}
{/if} <TimePicker.SecondSlider />
{#if hourCycle === 12} {/if}
<TimePicker.DayPeriodToggle /> {#if hourCycle === 12}
{/if} <TimePicker.DayPeriodToggle />
{/if}
</TimePicker.Clock>
</PickerShell.Body>
{#if showFooter && (showClear || showCancel || showClose)} {#if showFooter && (showClear || showCancel || showClose)}
<TimePicker.Footer> <TimePicker.Footer>
{#if showClear}<TimePicker.Clear />{/if} {#if showClear}<TimePicker.Clear />{/if}
{#if showCancel}<TimePicker.Cancel />{/if} {#if showCancel}<TimePicker.Cancel />{/if}
{#if showClose}<TimePicker.Close />{/if} {#if showClose}<TimePicker.Close />{/if}
</TimePicker.Footer> </TimePicker.Footer>
{/if} {/if}
</TimePicker.Clock>
</TimePicker.Content> </TimePicker.Content>
</TimePicker.Portal> </TimePicker.Portal>
</TimePicker> </TimePicker>

@ -6,6 +6,7 @@
type TimeRangePickerVariant, type TimeRangePickerVariant,
type InputSnippetProps type InputSnippetProps
} from '$uix/eidos/components/time-range-picker'; } from '$uix/eidos/components/time-range-picker';
import { PickerShell } from '$uix/eidos/components/picker-shell';
import { compileMorfo } from '$uix/morfo'; import { compileMorfo } from '$uix/morfo';
import { timeRangePickerMorfo } from '@/uix/morfo/components/time-range-picker'; import { timeRangePickerMorfo } from '@/uix/morfo/components/time-range-picker';
import { timeRangeFieldMorfo } from '@/uix/morfo/components/time-range-field'; import { timeRangeFieldMorfo } from '@/uix/morfo/components/time-range-field';
@ -170,6 +171,7 @@
[ [
"<script lang='ts'>", "<script lang='ts'>",
" import { TimeRangePicker } from '$uix/eidos/components/time-range-picker';", " import { TimeRangePicker } from '$uix/eidos/components/time-range-picker';",
" import { PickerShell } from '$uix/eidos/components/picker-shell';",
" import { Time, type TimeRange } from '$libs/days';", " import { Time, type TimeRange } from '$libs/days';",
'', '',
' let value = $state<TimeRange | undefined>({', ' let value = $state<TimeRange | undefined>({',
@ -211,31 +213,29 @@
' </TimeRangePicker.Input>', ' </TimeRangePicker.Input>',
' <TimeRangePicker.Portal>', ' <TimeRangePicker.Portal>',
' <TimeRangePicker.Content>', ' <TimeRangePicker.Content>',
' <TimeRangePicker.Clock>', ' <PickerShell.Body>',
' <div class="time-range-picker-endpoint">', ' <TimeRangePicker.Clock>',
' <span class="time-range-picker-endpoint-label">Start</span>', ' <div class="time-range-picker-endpoint">',
' <TimeRangePicker.HourSlider type="start" />', ' <span class="time-range-picker-endpoint-label">Start</span>',
' <TimeRangePicker.MinuteSlider type="start" />', ' <TimeRangePicker.HourSlider type="start" />',
granularity === 'second' && ' <TimeRangePicker.SecondSlider type="start" />', ' <TimeRangePicker.MinuteSlider type="start" />',
hourCycle === 12 && ' <TimeRangePicker.DayPeriodToggle type="start" />', granularity === 'second' && ' <TimeRangePicker.SecondSlider type="start" />',
' </div>', hourCycle === 12 && ' <TimeRangePicker.DayPeriodToggle type="start" />',
' <div class="time-range-picker-endpoint">', ' </div>',
' <span class="time-range-picker-endpoint-label">End</span>', ' <div class="time-range-picker-endpoint">',
' <TimeRangePicker.HourSlider type="end" />', ' <span class="time-range-picker-endpoint-label">End</span>',
' <TimeRangePicker.MinuteSlider type="end" />', ' <TimeRangePicker.HourSlider type="end" />',
granularity === 'second' && ' <TimeRangePicker.SecondSlider type="end" />', ' <TimeRangePicker.MinuteSlider type="end" />',
hourCycle === 12 && ' <TimeRangePicker.DayPeriodToggle type="end" />', granularity === 'second' && ' <TimeRangePicker.SecondSlider type="end" />',
' </div>', hourCycle === 12 && ' <TimeRangePicker.DayPeriodToggle type="end" />',
showFooter && ' </div>',
(showClear || showCancel || showClose) && ' </TimeRangePicker.Clock>',
' <TimeRangePicker.Footer>', ' </PickerShell.Body>',
showFooter && showClear && ' <TimeRangePicker.Clear />', showFooter && (showClear || showCancel || showClose) && ' <TimeRangePicker.Footer>',
showFooter && showCancel && ' <TimeRangePicker.Cancel />', showFooter && showClear && ' <TimeRangePicker.Clear />',
showFooter && showClose && ' <TimeRangePicker.Close />', showFooter && showCancel && ' <TimeRangePicker.Cancel />',
showFooter && showFooter && showClose && ' <TimeRangePicker.Close />',
(showClear || showCancel || showClose) && showFooter && (showClear || showCancel || showClose) && ' </TimeRangePicker.Footer>',
' </TimeRangePicker.Footer>',
' </TimeRangePicker.Clock>',
' </TimeRangePicker.Content>', ' </TimeRangePicker.Content>',
' </TimeRangePicker.Portal>', ' </TimeRangePicker.Portal>',
'</TimeRangePicker>' '</TimeRangePicker>'
@ -308,38 +308,40 @@
<TimeRangePicker.Portal> <TimeRangePicker.Portal>
<TimeRangePicker.Content> <TimeRangePicker.Content>
<TimeRangePicker.Clock> <PickerShell.Body>
<div class="time-range-picker-endpoint"> <TimeRangePicker.Clock>
<span class="time-range-picker-endpoint-label">Start</span> <div class="time-range-picker-endpoint">
<TimeRangePicker.HourSlider type="start" /> <span class="time-range-picker-endpoint-label">Start</span>
<TimeRangePicker.MinuteSlider type="start" /> <TimeRangePicker.HourSlider type="start" />
{#if granularity === 'second'} <TimeRangePicker.MinuteSlider type="start" />
<TimeRangePicker.SecondSlider type="start" /> {#if granularity === 'second'}
{/if} <TimeRangePicker.SecondSlider type="start" />
{#if hourCycle === 12} {/if}
<TimeRangePicker.DayPeriodToggle type="start" /> {#if hourCycle === 12}
{/if} <TimeRangePicker.DayPeriodToggle type="start" />
</div> {/if}
<div class="time-range-picker-endpoint"> </div>
<span class="time-range-picker-endpoint-label">End</span> <div class="time-range-picker-endpoint">
<TimeRangePicker.HourSlider type="end" /> <span class="time-range-picker-endpoint-label">End</span>
<TimeRangePicker.MinuteSlider type="end" /> <TimeRangePicker.HourSlider type="end" />
{#if granularity === 'second'} <TimeRangePicker.MinuteSlider type="end" />
<TimeRangePicker.SecondSlider type="end" /> {#if granularity === 'second'}
{/if} <TimeRangePicker.SecondSlider type="end" />
{#if hourCycle === 12} {/if}
<TimeRangePicker.DayPeriodToggle type="end" /> {#if hourCycle === 12}
{/if} <TimeRangePicker.DayPeriodToggle type="end" />
</div> {/if}
</div>
</TimeRangePicker.Clock>
</PickerShell.Body>
{#if showFooter && (showClear || showCancel || showClose)} {#if showFooter && (showClear || showCancel || showClose)}
<TimeRangePicker.Footer> <TimeRangePicker.Footer>
{#if showClear}<TimeRangePicker.Clear />{/if} {#if showClear}<TimeRangePicker.Clear />{/if}
{#if showCancel}<TimeRangePicker.Cancel />{/if} {#if showCancel}<TimeRangePicker.Cancel />{/if}
{#if showClose}<TimeRangePicker.Close />{/if} {#if showClose}<TimeRangePicker.Close />{/if}
</TimeRangePicker.Footer> </TimeRangePicker.Footer>
{/if} {/if}
</TimeRangePicker.Clock>
</TimeRangePicker.Content> </TimeRangePicker.Content>
</TimeRangePicker.Portal> </TimeRangePicker.Portal>
</TimeRangePicker> </TimeRangePicker>

Loading…
Cancel
Save

Powered by TurnKey Linux.