CheckboxTokens' icon size is 18px, the same as the box it fills; the tick and the dash were 16. Every icon these components draw at 20px or under — the tick, the switch's check and cross, the chip's leading, trailing, remove and filter-check icons, the date picker's menu arrows and the sort arrow — now asks for the optical size 20 cut, which is drawn for that size rather than scaled down from 24. Plan step 20, finding IN-12 (and core C16's optical sizes). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
479 lines
26 KiB
PHP
479 lines
26 KiB
PHP
{{-- A date field with M3's date pickers: docked under the field, modal, or modal text input.
|
||
|
||
`mode` picks M3's three:
|
||
|
||
- `docked` (the default): a text field that takes a typed date in the locale's numeric format
|
||
(`13.09.2026` in `de`, `09/13/2026` in `en-US`), with the calendar dropping open under it —
|
||
on a press of the field, its calendar button, or ArrowDown. On a compact window (below
|
||
`sm`) the calendar opens as the modal picker instead, where a docked one would not fit.
|
||
- `modal`: the field only shows the date; pressing it (or Enter, Space, ArrowDown) opens the
|
||
calendar in a dialog, with a pencil to switch to typing.
|
||
- `input`: the same dialog, opened on its text field, with a calendar icon to switch back.
|
||
|
||
The calendar and the dialog's text field pick a draft; OK (or Enter on a day) makes it the
|
||
value, Cancel or Escape leaves the value alone and returns focus to the field. What is typed
|
||
into the docked field is the value as soon as it is a whole, allowed date.
|
||
|
||
`wire:model` stores `Y-m-d` strings (`x-model` without Livewire). `range` picks a start and an
|
||
end, bound as one array, `['start' => 'Y-m-d', 'end' => 'Y-m-d']` (either may be null) —
|
||
one property rather than two, because a `wire:model` names one property, Livewire sends a
|
||
range as one update so `after_or_equal:period.start` validates against the end it came
|
||
with, and the errors for `period`, `period.start` and `period.end` all belong to this field.
|
||
`min` and `max` (`Y-m-d` or a date object) disable the days outside them and keep the
|
||
keyboard inside them. `label`, `hint`, `icon`, `variant` (`outlined`, `filled`) and `size`
|
||
are the field's; `clearable` adds a button that empties it (a date, or both ends of a range)
|
||
once it holds one; `name` adds hidden inputs carrying `Y-m-d` for a plain form post, and every
|
||
other attribute (`required`, `disabled`, `readonly`) reaches the text field. Errors under
|
||
the `wire:model` name replace the hint, and so does a typed date that cannot be read.
|
||
|
||
Month and weekday names, the first day of the week and the typed format come from `Intl` for
|
||
`app()->getLocale()` (resources/js/datepicker.js). An application that lets each person choose
|
||
overrides the last two: `week-start` is the first day of the week, 0 (Sunday) to 6 (Saturday),
|
||
and `format` the typed and displayed format, `dd`, `MM` and `yyyy` in any order around one
|
||
delimiter (`.`, `/` or `-`: `dd.MM.yyyy`, `MM/dd/yyyy`, `yyyy-MM-dd`). The field, the typed-date
|
||
reader, the calendar's columns and weekday header, Home and End, and the dialog's text fields
|
||
all follow them; the names stay the locale's, and `wire:model` still stores `Y-m-d`. A value
|
||
that is neither (`week-start="7"`, `format="d.M.yy"`) is ignored, as `null` is. Replaces
|
||
ReStride's flatpickr picker: its `config` becomes `min`, `max`, `range` and `mode`.
|
||
|
||
M3's date pickers (DatePickerModalTokens and DateInputModalTokens from androidx Compose
|
||
Material 3, androidx commit 27cf9a7d5788aa0f5f2d8b6699ce279560daf326, with the layout of
|
||
DatePicker.kt, DateRangePicker.kt and DateInput.kt; the docked picker's values from
|
||
material-web's md-comp-date-picker-docked tokens, v0_192, as Compose has none; all Apache-2.0):
|
||
surface-container-high at elevation 3, 360px wide, the extra-large corner (modal) or large
|
||
(docked), 40px days in 48px cells, today outlined in primary, the chosen day in primary and a
|
||
range's middle in secondary-container. The styles are resources/css/components/datepicker.css. --}}
|
||
|
||
@props([
|
||
'label' => null,
|
||
'hint' => null,
|
||
'icon' => null,
|
||
'size' => 'md',
|
||
'variant' => null,
|
||
'mode' => 'docked',
|
||
'range' => false,
|
||
'min' => null,
|
||
'max' => null,
|
||
'value' => null,
|
||
'clearable' => false,
|
||
'weekStart' => null,
|
||
'format' => null,
|
||
])
|
||
|
||
@php
|
||
$model = $attributes->wire('model')->value() ?: null;
|
||
$mode = in_array($mode, ['docked', 'modal', 'input'], true) ? $mode : 'docked';
|
||
$id = $attributes->get('id') ?? 'field-'.substr(md5($model.'|'.$label.'|datepicker'), 0, 12);
|
||
$anchor = '--material-datepicker-'.preg_replace('/[^A-Za-z0-9_-]/', '-', $id);
|
||
// A plain form's field is named, not bound: its errors are under its name (`files[]` → `files`, `a[b]` → `a.b`).
|
||
$errorKey = $model ?? (filled($attributes->get('name')) ? str_replace(['[]', '[', ']'], ['', '.', ''], (string) $attributes->get('name')) : null);
|
||
$messages = $errorKey !== null && isset($errors)
|
||
? array_values(array_unique(\Illuminate\Support\Arr::flatten([$errors->get($errorKey), $errors->get($errorKey.'.*')])))
|
||
: [];
|
||
$locale = str_replace('_', '-', app()->getLocale());
|
||
$weekStart = (is_int($weekStart) || is_string($weekStart)) && preg_match('/^[0-6]\z/', (string) $weekStart) === 1 ? (int) $weekStart : null;
|
||
$format = is_string($format) && preg_match('/^(dd|MM|yyyy)([.\/-])(dd|MM|yyyy)\2(dd|MM|yyyy)\z/', $format, $units) === 1 && count(array_unique([$units[1], $units[3], $units[4]])) === 3
|
||
? $format
|
||
: null;
|
||
|
||
$toIso = function (mixed $date): ?string {
|
||
if ($date instanceof \DateTimeInterface) {
|
||
return $date->format('Y-m-d');
|
||
}
|
||
|
||
if (is_string($date) && preg_match('/^(\d{4})-(\d{2})-(\d{2})(?:$|[T ])/', $date, $match) === 1 && checkdate((int) $match[2], (int) $match[3], (int) $match[1])) {
|
||
return substr($date, 0, 10);
|
||
}
|
||
|
||
return null;
|
||
};
|
||
|
||
// Rendered as the bound property already says, so the field does not change once Alpine starts.
|
||
$current = $value;
|
||
if ($model !== null && ($component = \Livewire\Livewire::current()) !== null) {
|
||
$current = data_get($component, $model);
|
||
}
|
||
$current = $range
|
||
? ['start' => $toIso(data_get($current, 'start')), 'end' => $toIso(data_get($current, 'end'))]
|
||
: $toIso($current);
|
||
|
||
// The typed format: `format`, or as resources/js/datepicker.js derives it, from ICU's short date when PHP has intl.
|
||
$pattern = $format ?? 'yyyy-MM-dd';
|
||
if ($format === null && class_exists(\IntlDateFormatter::class)) {
|
||
$short = (string) (new \IntlDateFormatter(str_replace('-', '_', $locale), \IntlDateFormatter::SHORT, \IntlDateFormatter::NONE))->getPattern();
|
||
$candidate = rtrim(str_replace('My', 'M/y', (string) preg_replace(['/[^dMy\/\-.]/', '/d{1,2}/', '/M{1,2}/', '/y{1,4}/'], ['', 'dd', 'MM', 'yyyy'], $short)), '.');
|
||
if (preg_match('/^(?=.*dd)(?=.*MM)(?=.*yyyy)[dMy]+[\/\-.][dMy]+[\/\-.][dMy]+$/', $candidate) === 1) {
|
||
$pattern = $candidate;
|
||
}
|
||
}
|
||
$typed = fn (?string $date): string => $date === null ? '' : strtr($pattern, ['yyyy' => substr($date, 0, 4), 'MM' => substr($date, 5, 2), 'dd' => substr($date, 8, 2)]);
|
||
$display = $range
|
||
? ($current['start'] === null && $current['end'] === null ? '' : $typed($current['start']).' – '.$typed($current['end']))
|
||
: $typed($current);
|
||
|
||
$title = $range ? __('Select dates') : __('Select date');
|
||
$inputTitle = $range ? __('Enter dates') : __('Select date');
|
||
$fieldName = $attributes->get('name');
|
||
$invalid = $messages !== [];
|
||
|
||
$config = [
|
||
'locale' => $locale,
|
||
'mode' => $mode,
|
||
'range' => (bool) $range,
|
||
'min' => $toIso($min),
|
||
'max' => $toIso($max),
|
||
'weekStart' => $weekStart,
|
||
'format' => $format,
|
||
'disabled' => (bool) $attributes->get('disabled'),
|
||
'readonly' => (bool) $attributes->get('readonly'),
|
||
'strings' => [
|
||
'selected' => __('Selected date'),
|
||
'entered' => __('Entered date'),
|
||
'start' => __('Start date'),
|
||
'end' => __('End date'),
|
||
'pattern' => __('Date does not match expected pattern: :pattern'),
|
||
'yearRange' => __('Date out of expected year range :start - :end'),
|
||
'notAllowed' => __('Date not allowed: :date'),
|
||
'invalidRange' => __('Invalid date range input'),
|
||
],
|
||
];
|
||
@endphp
|
||
|
||
<div
|
||
{{ $attributes->only(['class', 'wire:key', 'x-model']) }}
|
||
x-data="materialDatepicker({
|
||
@if ($model !== null) value: @entangle($attributes->wire('model')), @else value: @js($current), @endif
|
||
...@js($config),
|
||
})"
|
||
@if ($model === null) x-modelable="value" @endif
|
||
x-on:focusout="leave($event)"
|
||
x-on:pointerdown.outside="open && presentation === 'docked' && cancel(false)"
|
||
data-datepicker
|
||
>
|
||
<div style="anchor-name: {{ $anchor }}">
|
||
<x-livewire-material::field
|
||
:$id
|
||
:$label
|
||
:$icon
|
||
:$size
|
||
:$variant
|
||
:data-invalid="$invalid ? '' : null"
|
||
:data-readonly="$attributes->get('readonly') ? '' : null"
|
||
x-bind:data-invalid="(fieldError !== '' || {{ $invalid ? 'true' : 'false' }}) ? '' : null"
|
||
>
|
||
<input
|
||
{{ $attributes->whereDoesntStartWith('wire:model')->except(['class', 'id', 'wire:key', 'x-model', 'name', 'value', 'placeholder', 'type']) }}
|
||
x-ref="input"
|
||
id="{{ $id }}"
|
||
type="text"
|
||
role="combobox"
|
||
aria-haspopup="dialog"
|
||
aria-controls="{{ $id }}-picker"
|
||
aria-expanded="false"
|
||
x-bind:aria-expanded="open.toString()"
|
||
aria-describedby="{{ $id }}-support"
|
||
@if ($invalid) aria-invalid="true" @endif
|
||
x-bind:aria-invalid="(fieldError !== '' || {{ $invalid ? 'true' : 'false' }}) ? 'true' : null"
|
||
autocomplete="off"
|
||
value="{{ $display }}"
|
||
placeholder=" "
|
||
class="field-control"
|
||
x-model="text"
|
||
@if ($mode === 'docked')
|
||
x-bind:placeholder="placeholder"
|
||
x-on:input="typeInField()"
|
||
x-on:change="commitField()"
|
||
x-on:keydown.enter="commitField()"
|
||
x-on:click="show(false)"
|
||
x-on:keydown.arrow-down.prevent="open ? focusInside() : show()"
|
||
x-on:keydown.escape="if (open) { $event.preventDefault(); $event.stopPropagation(); cancel(); }"
|
||
@else
|
||
readonly
|
||
x-on:click="show()"
|
||
x-on:keydown.enter.prevent="show()"
|
||
x-on:keydown.space.prevent="show()"
|
||
x-on:keydown.arrow-down.prevent="show()"
|
||
@endif
|
||
/>
|
||
|
||
<x-slot:trailing>
|
||
@if ($clearable)
|
||
<button
|
||
type="button"
|
||
class="field-trailing field-clear field-button"
|
||
aria-label="{{ __('Clear') }}"
|
||
x-on:click="clear()"
|
||
@disabled($attributes->get('disabled') || $attributes->get('readonly'))
|
||
data-field-clear
|
||
>
|
||
<x-livewire-material::icon name="close" class="size-(--field-icon)" />
|
||
</button>
|
||
@endif
|
||
|
||
<button
|
||
type="button"
|
||
class="field-trailing field-button"
|
||
aria-label="{{ $title }}"
|
||
aria-haspopup="dialog"
|
||
aria-controls="{{ $id }}-picker"
|
||
x-bind:aria-expanded="open.toString()"
|
||
x-on:click="open ? cancel() : show()"
|
||
@if ($mode !== 'docked') tabindex="-1" @endif
|
||
@disabled($attributes->get('disabled') || $attributes->get('readonly'))
|
||
data-datepicker-toggle
|
||
>
|
||
<x-livewire-material::icon name="calendar_today" class="size-(--field-icon)" />
|
||
</button>
|
||
</x-slot:trailing>
|
||
</x-livewire-material::field>
|
||
</div>
|
||
|
||
<div id="{{ $id }}-support" data-datepicker-support data-size="{{ in_array($size, ['sm', 'xs'], true) ? $size : 'md' }}">
|
||
<p x-cloak x-show="fieldError !== ''" x-text="fieldError" role="alert" data-datepicker-error></p>
|
||
|
||
@if ($invalid)
|
||
<div x-show="fieldError === ''" role="alert" data-datepicker-error>
|
||
@foreach ($messages as $message)
|
||
<p>{{ $message }}</p>
|
||
@endforeach
|
||
</div>
|
||
@elseif (filled($hint))
|
||
<p x-show="fieldError === ''">{{ $hint }}</p>
|
||
@endif
|
||
</div>
|
||
|
||
@if (filled($fieldName))
|
||
@if ($range)
|
||
<input type="hidden" name="{{ $fieldName }}[start]" value="{{ $current['start'] }}" x-bind:value="serialised.start ?? ''" />
|
||
<input type="hidden" name="{{ $fieldName }}[end]" value="{{ $current['end'] }}" x-bind:value="serialised.end ?? ''" />
|
||
@else
|
||
<input type="hidden" name="{{ $fieldName }}" value="{{ $current }}" x-bind:value="serialised" />
|
||
@endif
|
||
@endif
|
||
|
||
<dialog
|
||
wire:ignore
|
||
x-ref="dialog"
|
||
id="{{ $id }}-picker"
|
||
popover="manual"
|
||
aria-label="{{ $title }}"
|
||
style="position-anchor: {{ $anchor }}"
|
||
x-bind:data-presentation="presentation"
|
||
x-on:cancel.prevent="cancel()"
|
||
x-on:click.self="presentation === 'modal' && cancel()"
|
||
x-on:keydown.escape.prevent.stop="cancel()"
|
||
data-datepicker-picker
|
||
>
|
||
<div tabindex="-1" data-datepicker-surface>
|
||
<div data-datepicker-header x-show="presentation === 'modal'" @if ($range) data-range @endif>
|
||
<p data-datepicker-title x-text="typing ? @js($inputTitle) : @js($title)">{{ $title }}</p>
|
||
|
||
<div data-datepicker-headline-row>
|
||
<p data-datepicker-headline aria-live="polite" x-text="headline"></p>
|
||
|
||
<span x-show="! typing">
|
||
<x-livewire-material::button icon="edit" :tooltip="__('Switch to text input mode')" x-on:click="toggleTyping()" data-datepicker-switch />
|
||
</span>
|
||
<span x-show="typing">
|
||
<x-livewire-material::button icon="date_range" :tooltip="__('Switch to calendar input mode')" x-on:click="toggleTyping()" data-datepicker-switch />
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div x-show="! typing" data-datepicker-calendar>
|
||
<span id="{{ $id }}-month" class="sr-only" aria-live="polite" x-text="monthYear"></span>
|
||
|
||
<div data-datepicker-nav x-show="presentation === 'modal'">
|
||
<button
|
||
type="button"
|
||
data-datepicker-menu-button
|
||
x-on:click="toggleView('years')"
|
||
x-bind:aria-expanded="(view === 'years').toString()"
|
||
x-bind:aria-label="monthYear + ', ' + @js(__('Switch to selecting a year'))"
|
||
>
|
||
<span x-text="monthYear"></span>
|
||
<x-livewire-material::icon name="arrow_drop_down" class="size-4.5" optical="20" data-datepicker-menu-arrow />
|
||
</button>
|
||
|
||
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
|
||
<x-livewire-material::button icon="chevron_left" :tooltip="__('Previous month')" x-on:click="step(-1)" x-bind:disabled="view !== 'days' || ! canStep(-1)" data-datepicker-previous />
|
||
<x-livewire-material::button icon="chevron_right" :tooltip="__('Next month')" x-on:click="step(1)" x-bind:disabled="view !== 'days' || ! canStep(1)" data-datepicker-next />
|
||
</span>
|
||
</div>
|
||
|
||
<div data-datepicker-nav data-docked x-show="presentation === 'docked'">
|
||
<span data-datepicker-stepper>
|
||
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
|
||
<x-livewire-material::button icon="chevron_left" :tooltip="__('Previous month')" x-on:click="step(-1)" x-bind:disabled="view !== 'days' || ! canStep(-1)" />
|
||
</span>
|
||
<button
|
||
type="button"
|
||
data-datepicker-menu-button
|
||
x-on:click="toggleView('months')"
|
||
x-bind:aria-expanded="(view === 'months').toString()"
|
||
x-bind:aria-label="monthLabel + ', ' + @js(__('Switch to selecting a month'))"
|
||
>
|
||
<span x-text="monthLabel"></span>
|
||
<x-livewire-material::icon name="arrow_drop_down" class="size-4.5" optical="20" data-datepicker-menu-arrow />
|
||
</button>
|
||
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
|
||
<x-livewire-material::button icon="chevron_right" :tooltip="__('Next month')" x-on:click="step(1)" x-bind:disabled="view !== 'days' || ! canStep(1)" />
|
||
</span>
|
||
</span>
|
||
|
||
<span data-datepicker-stepper>
|
||
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
|
||
<x-livewire-material::button icon="chevron_left" :tooltip="__('Previous year')" x-on:click="step(-12)" x-bind:disabled="view !== 'days' || ! canStep(-12)" />
|
||
</span>
|
||
<button
|
||
type="button"
|
||
data-datepicker-menu-button
|
||
x-on:click="toggleView('years')"
|
||
x-bind:aria-expanded="(view === 'years').toString()"
|
||
x-bind:aria-label="yearLabel + ', ' + @js(__('Switch to selecting a year'))"
|
||
>
|
||
<span x-text="yearLabel"></span>
|
||
<x-livewire-material::icon name="arrow_drop_down" class="size-4.5" optical="20" data-datepicker-menu-arrow />
|
||
</button>
|
||
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
|
||
<x-livewire-material::button icon="chevron_right" :tooltip="__('Next year')" x-on:click="step(12)" x-bind:disabled="view !== 'days' || ! canStep(12)" />
|
||
</span>
|
||
</span>
|
||
</div>
|
||
|
||
<table role="grid" aria-labelledby="{{ $id }}-month" x-show="view === 'days'" x-on:keydown="gridKey($event)" data-datepicker-grid>
|
||
<thead>
|
||
<tr>
|
||
<template x-for="weekday in weekdays" :key="weekday.long">
|
||
<th scope="col" x-bind:abbr="weekday.long" x-text="weekday.narrow"></th>
|
||
</template>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<template x-for="(week, row) in weeks" :key="row">
|
||
<tr>
|
||
<template x-for="(cell, column) in week" :key="column">
|
||
<td
|
||
role="gridcell"
|
||
x-bind:tabindex="cell.blank ? null : (cell.focused ? 0 : -1)"
|
||
x-bind:aria-label="cell.blank ? null : cell.name"
|
||
x-bind:aria-selected="! cell.blank && (cell.selected || cell.between) ? 'true' : null"
|
||
x-bind:aria-disabled="! cell.blank && cell.disabled ? 'true' : null"
|
||
x-bind:aria-current="! cell.blank && cell.today ? 'date' : null"
|
||
x-bind:data-value="cell.blank ? null : cell.value"
|
||
x-bind:data-blank="cell.blank ? '' : null"
|
||
x-bind:data-outside="cell.outside ? '' : null"
|
||
x-bind:data-today="cell.today ? '' : null"
|
||
x-bind:data-selected="cell.selected ? '' : null"
|
||
x-bind:data-start="cell.start ? '' : null"
|
||
x-bind:data-end="cell.end ? '' : null"
|
||
x-bind:data-between="cell.between ? '' : null"
|
||
x-on:click="choose(cell)"
|
||
data-datepicker-day
|
||
><span x-text="cell.blank ? '' : cell.label"></span></td>
|
||
</template>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div role="listbox" aria-label="{{ __('Years') }}" x-show="view === 'years' && presentation === 'modal'" x-on:keydown="listKey($event, 3)" data-datepicker-list data-datepicker-years>
|
||
<template x-for="year in years" :key="year.value">
|
||
<button
|
||
type="button"
|
||
role="option"
|
||
x-bind:aria-selected="year.selected.toString()"
|
||
x-bind:tabindex="year.selected ? 0 : -1"
|
||
x-bind:data-current="year.current ? '' : null"
|
||
x-on:click="showMonthOf(year.value, +shown.slice(5, 7))"
|
||
x-text="year.label"
|
||
data-datepicker-year
|
||
></button>
|
||
</template>
|
||
</div>
|
||
|
||
<div role="listbox" aria-label="{{ __('Months') }}" x-show="view === 'months' && presentation === 'docked'" x-on:keydown="listKey($event)" data-datepicker-list data-datepicker-menu>
|
||
<template x-for="month in months" :key="month.value">
|
||
<button
|
||
type="button"
|
||
role="option"
|
||
x-bind:aria-selected="month.selected.toString()"
|
||
x-bind:aria-disabled="month.disabled ? 'true' : null"
|
||
x-bind:tabindex="month.selected ? 0 : -1"
|
||
x-on:click="month.disabled || showMonthOf(+shown.slice(0, 4), month.value)"
|
||
data-datepicker-option
|
||
>
|
||
<x-livewire-material::icon name="check" data-datepicker-check />
|
||
<span x-text="month.label"></span>
|
||
</button>
|
||
</template>
|
||
</div>
|
||
|
||
<div role="listbox" aria-label="{{ __('Years') }}" x-show="view === 'years' && presentation === 'docked'" x-on:keydown="listKey($event)" data-datepicker-list data-datepicker-menu>
|
||
<template x-for="year in years" :key="year.value">
|
||
<button
|
||
type="button"
|
||
role="option"
|
||
x-bind:aria-selected="year.selected.toString()"
|
||
x-bind:tabindex="year.selected ? 0 : -1"
|
||
x-on:click="showMonthOf(year.value, +shown.slice(5, 7))"
|
||
data-datepicker-option
|
||
>
|
||
<x-livewire-material::icon name="check" data-datepicker-check />
|
||
<span x-text="year.label"></span>
|
||
</button>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
|
||
<div x-show="typing" data-datepicker-entry>
|
||
<div @if ($range) data-range @endif data-datepicker-entry-fields>
|
||
<x-livewire-material::field id="{{ $id }}-entry" :label="$range ? __('Start date') : __('Date')" :$variant x-bind:data-invalid="entryError !== '' ? '' : null">
|
||
<input
|
||
id="{{ $id }}-entry"
|
||
type="text"
|
||
autocomplete="off"
|
||
placeholder=" "
|
||
x-bind:placeholder="format.placeholder"
|
||
x-bind:aria-invalid="entryError !== '' ? 'true' : null"
|
||
aria-describedby="{{ $id }}-entry-support"
|
||
x-ref="entry"
|
||
x-model="entry"
|
||
x-on:input="typeEntry()"
|
||
x-on:keydown.enter.prevent="confirm()"
|
||
class="field-control"
|
||
/>
|
||
</x-livewire-material::field>
|
||
|
||
@if ($range)
|
||
<x-livewire-material::field id="{{ $id }}-entry-end" :label="__('End date')" :$variant x-bind:data-invalid="entryError !== '' ? '' : null">
|
||
<input
|
||
id="{{ $id }}-entry-end"
|
||
type="text"
|
||
autocomplete="off"
|
||
placeholder=" "
|
||
x-bind:placeholder="format.placeholder"
|
||
x-bind:aria-invalid="entryError !== '' ? 'true' : null"
|
||
aria-describedby="{{ $id }}-entry-support"
|
||
x-model="entryEnd"
|
||
x-on:input="typeEntry()"
|
||
x-on:keydown.enter.prevent="confirm()"
|
||
class="field-control"
|
||
/>
|
||
</x-livewire-material::field>
|
||
@endif
|
||
</div>
|
||
|
||
<div id="{{ $id }}-entry-support" data-datepicker-support>
|
||
<p x-show="entryError !== ''" x-text="entryError" role="alert" data-datepicker-error></p>
|
||
</div>
|
||
</div>
|
||
|
||
<div x-show="view === 'days' || typing || presentation === 'modal'" data-datepicker-actions>
|
||
<x-livewire-material::button :label="__('Cancel')" x-on:click="cancel()" data-datepicker-cancel />
|
||
<x-livewire-material::button :label="__('OK')" x-on:click="confirm()" data-datepicker-confirm />
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
</div>
|