@zeyos/zx 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +15 -0
- package/dist/types/compat/base.d.ts +156 -0
- package/dist/types/compat/globals.d.ts +20 -0
- package/dist/types/compat/index.d.ts +128 -0
- package/dist/types/compat/map/basic.d.ts +175 -0
- package/dist/types/compat/map/data.d.ts +102 -0
- package/dist/types/compat/map/date-time.d.ts +80 -0
- package/dist/types/compat/map/factory.d.ts +9 -0
- package/dist/types/compat/map/form.d.ts +98 -0
- package/dist/types/compat/map/helpers.d.ts +12 -0
- package/dist/types/compat/map/http.d.ts +36 -0
- package/dist/types/compat/map/layout.d.ts +106 -0
- package/dist/types/compat/map/options.d.ts +80 -0
- package/dist/types/compat/map/select.d.ts +95 -0
- package/dist/types/compat/map/stubs.d.ts +32 -0
- package/dist/types/compat/map/utilities.d.ts +37 -0
- package/dist/types/compat-entry.d.ts +5 -0
- package/dist/types/components/badge/badge.d.ts +46 -0
- package/dist/types/components/breadcrumb/breadcrumb.d.ts +200 -0
- package/dist/types/components/button/button.d.ts +42 -0
- package/dist/types/components/check-button/check-button.d.ts +98 -0
- package/dist/types/components/checklist/checklist.d.ts +143 -0
- package/dist/types/components/checklist/field-adapter.d.ts +5 -0
- package/dist/types/components/context-menu/context-menu.d.ts +130 -0
- package/dist/types/components/copy/copy-button.d.ts +48 -0
- package/dist/types/components/copy/copy-input.d.ts +91 -0
- package/dist/types/components/data-filter/data-filter.d.ts +250 -0
- package/dist/types/components/data-filter/filter-core.d.ts +52 -0
- package/dist/types/components/date-picker/date-picker.d.ts +137 -0
- package/dist/types/components/date-picker/date-range-picker.d.ts +350 -0
- package/dist/types/components/date-picker/field-adapter.d.ts +6 -0
- package/dist/types/components/date-picker/month-picker.d.ts +75 -0
- package/dist/types/components/date-picker/range-field-adapter.d.ts +7 -0
- package/dist/types/components/date-picker/time-picker.d.ts +127 -0
- package/dist/types/components/datebox/date-range-box.d.ts +218 -0
- package/dist/types/components/datebox/datebox.d.ts +160 -0
- package/dist/types/components/datebox/field-adapter.d.ts +5 -0
- package/dist/types/components/dialog/dialog.d.ts +284 -0
- package/dist/types/components/dropdown/dropdown.d.ts +118 -0
- package/dist/types/components/empty-state/empty-state.d.ts +38 -0
- package/dist/types/components/field/field.d.ts +231 -0
- package/dist/types/components/field-upload/field-adapter.d.ts +5 -0
- package/dist/types/components/field-upload/field-upload.d.ts +213 -0
- package/dist/types/components/fieldset/fieldset.d.ts +125 -0
- package/dist/types/components/finder/finder.d.ts +252 -0
- package/dist/types/components/form/form.d.ts +194 -0
- package/dist/types/components/groupbox/groupbox.d.ts +89 -0
- package/dist/types/components/layout/layout.d.ts +132 -0
- package/dist/types/components/loading/inline-loading.d.ts +86 -0
- package/dist/types/components/loading/progress-bar.d.ts +168 -0
- package/dist/types/components/loading/spinner.d.ts +34 -0
- package/dist/types/components/master-panel/master-panel.d.ts +112 -0
- package/dist/types/components/menu-button/menu-button.d.ts +142 -0
- package/dist/types/components/message/message.d.ts +212 -0
- package/dist/types/components/modal/modal.d.ts +105 -0
- package/dist/types/components/multi-value-editor/field-adapter.d.ts +5 -0
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +66 -0
- package/dist/types/components/navigation-bar/navigation-bar.d.ts +229 -0
- package/dist/types/components/number-field/field-adapter.d.ts +5 -0
- package/dist/types/components/number-field/number-field.d.ts +292 -0
- package/dist/types/components/pagination/pagination.d.ts +267 -0
- package/dist/types/components/panel/panel.d.ts +194 -0
- package/dist/types/components/permission/permission.d.ts +93 -0
- package/dist/types/components/rating/field-adapter.d.ts +5 -0
- package/dist/types/components/rating/rating.d.ts +213 -0
- package/dist/types/components/search/search.d.ts +98 -0
- package/dist/types/components/select/field-adapter.d.ts +5 -0
- package/dist/types/components/select/filter.d.ts +11 -0
- package/dist/types/components/select/select.d.ts +235 -0
- package/dist/types/components/skeleton/skeleton.d.ts +97 -0
- package/dist/types/components/slider/field-adapter.d.ts +5 -0
- package/dist/types/components/slider/slider.d.ts +216 -0
- package/dist/types/components/split-view/split-view.d.ts +335 -0
- package/dist/types/components/stepper/stepper.d.ts +314 -0
- package/dist/types/components/stepper/steps.d.ts +131 -0
- package/dist/types/components/tabbox/tabbox.d.ts +230 -0
- package/dist/types/components/table/sort.d.ts +49 -0
- package/dist/types/components/table/table.d.ts +872 -0
- package/dist/types/components/tag-picker/field-adapter.d.ts +5 -0
- package/dist/types/components/tag-picker/tag-picker.d.ts +375 -0
- package/dist/types/components/timebox/field-adapter.d.ts +5 -0
- package/dist/types/components/timebox/timebox.d.ts +121 -0
- package/dist/types/components/toggle/field-adapter.d.ts +5 -0
- package/dist/types/components/toggle/toggle.d.ts +94 -0
- package/dist/types/components/toolbar/toolbar.d.ts +230 -0
- package/dist/types/components/tooltip/tooltip.d.ts +183 -0
- package/dist/types/components/tree/hierarchy.d.ts +113 -0
- package/dist/types/components/tree/tree.d.ts +383 -0
- package/dist/types/components/truncate/truncate.d.ts +61 -0
- package/dist/types/components/value-list/field-adapter.d.ts +5 -0
- package/dist/types/components/value-list/value-list.d.ts +143 -0
- package/dist/types/core/breakpoint.d.ts +68 -0
- package/dist/types/core/component.d.ts +115 -0
- package/dist/types/core/date.d.ts +50 -0
- package/dist/types/core/dom.d.ts +86 -0
- package/dist/types/core/export.d.ts +70 -0
- package/dist/types/core/fontawesome.d.ts +107 -0
- package/dist/types/core/format.d.ts +107 -0
- package/dist/types/core/http.d.ts +122 -0
- package/dist/types/core/i18n.d.ts +35 -0
- package/dist/types/core/icons.d.ts +137 -0
- package/dist/types/core/keyboard.d.ts +72 -0
- package/dist/types/core/position.d.ts +45 -0
- package/dist/types/core/storage.d.ts +46 -0
- package/dist/types/core/util.d.ts +117 -0
- package/dist/types/elements/define.d.ts +73 -0
- package/dist/types/elements/reflect.d.ts +27 -0
- package/dist/types/index.d.ts +79 -0
- package/dist/types/zeyos/connect.d.ts +65 -0
- package/dist/types/zeyos/form.d.ts +140 -0
- package/dist/types/zeyos/icons.d.ts +104 -0
- package/dist/types/zeyos/index.d.ts +12 -0
- package/dist/types/zeyos/modules.d.ts +110 -0
- package/dist/types/zeyos/query.d.ts +157 -0
- package/dist/types/zeyos/schema.d.ts +79 -0
- package/dist/types/zeyos/select.d.ts +167 -0
- package/dist/types/zeyos/table.d.ts +205 -0
- package/dist/zx-compat.esm.js +362 -33
- package/dist/zx-compat.esm.min.js +1 -1
- package/dist/zx-compat.global.js +359 -67
- package/dist/zx-compat.global.min.js +3 -3
- package/dist/zx-zeyos.esm.js +342 -43
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +68 -6
- package/dist/zx.esm.js +346 -67
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +346 -67
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +48 -2
- package/docs/llms.md +28 -0
- package/package.json +37 -9
- package/src/compat/base.js +291 -0
- package/src/compat/globals.js +165 -0
- package/src/compat/index.js +84 -0
- package/src/compat/map/basic.js +464 -0
- package/src/compat/map/data.js +232 -0
- package/src/compat/map/date-time.js +177 -0
- package/src/compat/map/factory.js +45 -0
- package/src/compat/map/form.js +330 -0
- package/src/compat/map/helpers.js +46 -0
- package/src/compat/map/http.js +117 -0
- package/src/compat/map/layout.js +256 -0
- package/src/compat/map/options.js +267 -0
- package/src/compat/map/select.js +198 -0
- package/src/compat/map/stubs.js +26 -0
- package/src/compat/map/utilities.js +113 -0
- package/src/compat-entry.js +11 -0
- package/src/components/badge/badge.css +147 -0
- package/src/components/badge/badge.js +92 -0
- package/src/components/breadcrumb/breadcrumb.css +105 -0
- package/src/components/breadcrumb/breadcrumb.js +331 -0
- package/src/components/button/button.css +104 -0
- package/src/components/button/button.js +76 -0
- package/src/components/check-button/check-button.css +66 -0
- package/src/components/check-button/check-button.js +167 -0
- package/src/components/checklist/checklist.css +65 -0
- package/src/components/checklist/checklist.js +282 -0
- package/src/components/checklist/field-adapter.js +46 -0
- package/src/components/context-menu/context-menu.css +54 -0
- package/src/components/context-menu/context-menu.js +334 -0
- package/src/components/copy/copy-button.css +27 -0
- package/src/components/copy/copy-button.js +78 -0
- package/src/components/copy/copy-input.css +59 -0
- package/src/components/copy/copy-input.js +123 -0
- package/src/components/data-filter/data-filter.css +53 -0
- package/src/components/data-filter/data-filter.js +400 -0
- package/src/components/data-filter/filter-core.js +128 -0
- package/src/components/date-picker/date-picker.css +181 -0
- package/src/components/date-picker/date-picker.js +500 -0
- package/src/components/date-picker/date-range-picker.css +264 -0
- package/src/components/date-picker/date-range-picker.js +990 -0
- package/src/components/date-picker/field-adapter.js +36 -0
- package/src/components/date-picker/month-picker.js +223 -0
- package/src/components/date-picker/range-field-adapter.js +31 -0
- package/src/components/date-picker/time-picker.css +191 -0
- package/src/components/date-picker/time-picker.js +406 -0
- package/src/components/datebox/date-range-box.css +128 -0
- package/src/components/datebox/date-range-box.js +492 -0
- package/src/components/datebox/datebox.css +108 -0
- package/src/components/datebox/datebox.js +358 -0
- package/src/components/datebox/field-adapter.js +34 -0
- package/src/components/dialog/dialog.css +74 -0
- package/src/components/dialog/dialog.js +450 -0
- package/src/components/dropdown/dropdown.css +24 -0
- package/src/components/dropdown/dropdown.js +288 -0
- package/src/components/empty-state/empty-state.css +80 -0
- package/src/components/empty-state/empty-state.js +92 -0
- package/src/components/field/field.css +127 -0
- package/src/components/field/field.js +605 -0
- package/src/components/field-upload/field-adapter.js +21 -0
- package/src/components/field-upload/field-upload.css +108 -0
- package/src/components/field-upload/field-upload.js +475 -0
- package/src/components/fieldset/fieldset.css +37 -0
- package/src/components/fieldset/fieldset.js +307 -0
- package/src/components/finder/finder.css +98 -0
- package/src/components/finder/finder.js +509 -0
- package/src/components/form/form.css +32 -0
- package/src/components/form/form.js +302 -0
- package/src/components/groupbox/groupbox.css +51 -0
- package/src/components/groupbox/groupbox.js +199 -0
- package/src/components/layout/layout.css +117 -0
- package/src/components/layout/layout.js +136 -0
- package/src/components/loading/inline-loading.css +34 -0
- package/src/components/loading/inline-loading.js +140 -0
- package/src/components/loading/progress-bar.css +115 -0
- package/src/components/loading/progress-bar.js +259 -0
- package/src/components/loading/spinner.css +58 -0
- package/src/components/loading/spinner.js +57 -0
- package/src/components/master-panel/master-panel.css +51 -0
- package/src/components/master-panel/master-panel.js +203 -0
- package/src/components/menu-button/menu-button.css +49 -0
- package/src/components/menu-button/menu-button.js +294 -0
- package/src/components/message/message.css +122 -0
- package/src/components/message/message.js +499 -0
- package/src/components/modal/modal.css +47 -0
- package/src/components/modal/modal.js +203 -0
- package/src/components/multi-value-editor/field-adapter.js +44 -0
- package/src/components/multi-value-editor/multi-value-editor.css +56 -0
- package/src/components/multi-value-editor/multi-value-editor.js +170 -0
- package/src/components/navigation-bar/navigation-bar.css +130 -0
- package/src/components/navigation-bar/navigation-bar.js +450 -0
- package/src/components/number-field/field-adapter.js +40 -0
- package/src/components/number-field/number-field.css +77 -0
- package/src/components/number-field/number-field.js +545 -0
- package/src/components/pagination/pagination.css +95 -0
- package/src/components/pagination/pagination.js +481 -0
- package/src/components/panel/panel.css +123 -0
- package/src/components/panel/panel.js +336 -0
- package/src/components/permission/permission.css +47 -0
- package/src/components/permission/permission.js +198 -0
- package/src/components/rating/field-adapter.js +34 -0
- package/src/components/rating/rating.css +97 -0
- package/src/components/rating/rating.js +395 -0
- package/src/components/search/search.css +70 -0
- package/src/components/search/search.js +193 -0
- package/src/components/select/field-adapter.js +24 -0
- package/src/components/select/filter.js +39 -0
- package/src/components/select/select.css +180 -0
- package/src/components/select/select.js +663 -0
- package/src/components/skeleton/skeleton.css +76 -0
- package/src/components/skeleton/skeleton.js +139 -0
- package/src/components/slider/field-adapter.js +36 -0
- package/src/components/slider/slider.css +181 -0
- package/src/components/slider/slider.js +399 -0
- package/src/components/split-view/split-view.css +139 -0
- package/src/components/split-view/split-view.js +806 -0
- package/src/components/stepper/stepper.css +234 -0
- package/src/components/stepper/stepper.js +459 -0
- package/src/components/stepper/steps.js +131 -0
- package/src/components/tabbox/tabbox.css +281 -0
- package/src/components/tabbox/tabbox.js +484 -0
- package/src/components/table/sort.js +85 -0
- package/src/components/table/table.css +364 -0
- package/src/components/table/table.js +1693 -0
- package/src/components/tag-picker/field-adapter.js +35 -0
- package/src/components/tag-picker/tag-picker.css +169 -0
- package/src/components/tag-picker/tag-picker.js +837 -0
- package/src/components/timebox/field-adapter.js +24 -0
- package/src/components/timebox/timebox.css +51 -0
- package/src/components/timebox/timebox.js +276 -0
- package/src/components/toggle/field-adapter.js +24 -0
- package/src/components/toggle/toggle.css +66 -0
- package/src/components/toggle/toggle.js +150 -0
- package/src/components/toolbar/toolbar.css +81 -0
- package/src/components/toolbar/toolbar.js +511 -0
- package/src/components/tooltip/tooltip.css +36 -0
- package/src/components/tooltip/tooltip.js +420 -0
- package/src/components/tree/hierarchy.js +151 -0
- package/src/components/tree/tree.css +99 -0
- package/src/components/tree/tree.js +784 -0
- package/src/components/truncate/truncate.css +20 -0
- package/src/components/truncate/truncate.js +92 -0
- package/src/components/value-list/field-adapter.js +24 -0
- package/src/components/value-list/value-list.css +108 -0
- package/src/components/value-list/value-list.js +336 -0
- package/src/core/breakpoint.js +150 -0
- package/src/core/component.js +292 -0
- package/src/core/date.js +216 -0
- package/src/core/dom.js +260 -0
- package/src/core/export.js +204 -0
- package/src/core/fontawesome.js +177 -0
- package/src/core/format.js +283 -0
- package/src/core/http.js +246 -0
- package/src/core/i18n.js +83 -0
- package/src/core/icons.js +283 -0
- package/src/core/keyboard.js +240 -0
- package/src/core/position.js +177 -0
- package/src/core/storage.js +175 -0
- package/src/core/util.js +336 -0
- package/src/elements/define.js +715 -0
- package/src/elements/reflect.js +79 -0
- package/src/index.js +131 -0
- package/src/zeyos/connect.js +142 -0
- package/src/zeyos/form.js +254 -0
- package/src/zeyos/icons.js +92 -0
- package/src/zeyos/index.js +21 -0
- package/src/zeyos/modules.js +269 -0
- package/src/zeyos/query.js +199 -0
- package/src/zeyos/schema.js +474 -0
- package/src/zeyos/select.js +175 -0
- package/src/zeyos/table.js +272 -0
- package/styles/base.css +124 -0
- package/styles/icons.css +34 -0
- package/styles/tokens/dark.css +84 -0
- package/styles/tokens/density.css +9 -0
- package/styles/tokens/global.css +80 -0
- package/styles/tokens/modules.css +109 -0
- package/styles/tokens/semantic.css +49 -0
- package/styles/zx.css +61 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
import { Component } from '../../core/component.js';
|
|
2
|
+
import { formatDate, parseDate } from '../../core/date.js';
|
|
3
|
+
import { h } from '../../core/dom.js';
|
|
4
|
+
import { printf, translate } from '../../core/i18n.js';
|
|
5
|
+
import { icon } from '../../core/icons.js';
|
|
6
|
+
import { position } from '../../core/position.js';
|
|
7
|
+
import { escapeRegExp, uid } from '../../core/util.js';
|
|
8
|
+
import { DateRangePicker, clampRange, normalizeRange } from '../date-picker/date-range-picker.js';
|
|
9
|
+
|
|
10
|
+
/** @typedef {import('../date-picker/date-range-picker.js').DateRange} DateRange */
|
|
11
|
+
/** @typedef {import('../date-picker/date-range-picker.js').DateRangePreset} DateRangePreset */
|
|
12
|
+
/** @typedef {{start: number|null, end: number|null}} DateRangeSeconds */
|
|
13
|
+
/** @typedef {Date|number|string|null} DateRangeInput */
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} DateRangeBoxOptions
|
|
17
|
+
* @property {Date|number|string|null} [start=null] Initial start: Date, Unix seconds, or formatted text.
|
|
18
|
+
* @property {Date|number|string|null} [end=null] Initial end: Date, Unix seconds, or formatted text.
|
|
19
|
+
* @property {string} [format='%d.%m.%Y'] Kernel date format used for both endpoints.
|
|
20
|
+
* @property {string} [separator=' – '] Text placed between the two formatted dates.
|
|
21
|
+
* @property {string|null} [placeholder=null] Input placeholder; null derives one from the format.
|
|
22
|
+
* @property {boolean} [clearable=true] Show a clear button while a range is present.
|
|
23
|
+
* @property {Date|null} [min=null] Earliest selectable local calendar day.
|
|
24
|
+
* @property {Date|null} [max=null] Latest selectable local calendar day.
|
|
25
|
+
* @property {number} [months=2] Month panels in the popover; narrow ones show only the first.
|
|
26
|
+
* @property {number} [weekStart=1] First weekday, from 0 (Sunday) through 6 (Saturday).
|
|
27
|
+
* @property {boolean} [showWeekNumbers=false] Show ISO week numbers.
|
|
28
|
+
* @property {number} [minNights=0] Smallest number of nights a complete range may span.
|
|
29
|
+
* @property {number|null} [maxNights=null] Largest number of nights a complete range may span.
|
|
30
|
+
* @property {DateRangePreset[]|boolean} [presets=[]] Preset buttons, or `true` for the built-in set.
|
|
31
|
+
* @property {boolean} [disabled=false] Disable all controls.
|
|
32
|
+
* @property {(event: CustomEvent<DateRange>) => void} [onchange] Change listener.
|
|
33
|
+
* @property {(event: CustomEvent<{text: string}>) => void} [oninvalid] Invalid-input listener.
|
|
34
|
+
* @property {(event: CustomEvent<Record<string, never>>) => void} [onopen] Open listener.
|
|
35
|
+
* @property {(event: CustomEvent<Record<string, never>>) => void} [onclose] Close listener.
|
|
36
|
+
*/
|
|
37
|
+
/** @typedef {{silent?: boolean}} DateRangeBoxSetOptions */
|
|
38
|
+
/** @typedef {'date'|'seconds'} DateRangeBoxUnit */
|
|
39
|
+
|
|
40
|
+
const PLACEHOLDER_TOKENS = {
|
|
41
|
+
'%d': 'DD',
|
|
42
|
+
'%e': 'D',
|
|
43
|
+
'%m': 'MM',
|
|
44
|
+
'%Y': 'YYYY',
|
|
45
|
+
'%y': 'YY'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parses `"<start><separator><end>"` into a range. A lone date is read as a one-day range, and a
|
|
50
|
+
* trailing separator as a start-only range, so a half-typed value is not thrown away.
|
|
51
|
+
* @param {string} text Input text.
|
|
52
|
+
* @param {string} format Kernel date format for each endpoint.
|
|
53
|
+
* @param {string} separator Separator between the two dates; surrounding spaces are optional.
|
|
54
|
+
* @returns {DateRange|null} The parsed range, or null when the text does not match.
|
|
55
|
+
*/
|
|
56
|
+
export function parseRangeText(text, format, separator) {
|
|
57
|
+
const source = String(text ?? '').trim();
|
|
58
|
+
if (source === '') return { start: null, end: null };
|
|
59
|
+
const raw = String(separator ?? ' – ');
|
|
60
|
+
const core = raw.trim();
|
|
61
|
+
// Spacing is optional unless the separator also occurs inside the format itself — a " - "
|
|
62
|
+
// separator with a `%Y-%m-%d` format only splits where the spaces are, never on a date's dashes.
|
|
63
|
+
const strict = core !== '' && String(format).includes(core) && /^\s|\s$/.test(raw);
|
|
64
|
+
const lead = strict ? '\\s+' : '\\s*';
|
|
65
|
+
const tail = strict ? '(?:\\s+|$)' : '\\s*';
|
|
66
|
+
const parts = core === ''
|
|
67
|
+
? [source]
|
|
68
|
+
: source.split(new RegExp(`${lead}${escapeRegExp(core)}${tail}`));
|
|
69
|
+
if (parts.length > 2) return null;
|
|
70
|
+
|
|
71
|
+
const first = parseDate(parts[0].trim(), format);
|
|
72
|
+
if (!first) return null;
|
|
73
|
+
if (parts.length === 1) return { start: first, end: new Date(first.getTime()) };
|
|
74
|
+
if (parts[1].trim() === '') return { start: first, end: null };
|
|
75
|
+
const second = parseDate(parts[1].trim(), format);
|
|
76
|
+
if (!second) return null;
|
|
77
|
+
return normalizeRange(first, second);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Renders a range as `"<start><separator><end>"`. An incomplete range yields the endpoint it has.
|
|
82
|
+
* @param {DateRange|null} range Range to render.
|
|
83
|
+
* @param {string} format Kernel date format for each endpoint.
|
|
84
|
+
* @param {string} separator Separator between the two dates.
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
export function formatRangeText(range, format, separator) {
|
|
88
|
+
const start = range?.start ?? null;
|
|
89
|
+
const end = range?.end ?? null;
|
|
90
|
+
if (!start && !end) return '';
|
|
91
|
+
if (!end) return formatDate(start, format);
|
|
92
|
+
if (!start) return formatDate(end, format);
|
|
93
|
+
return `${formatDate(start, format)}${separator}${formatDate(end, format)}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Formatted date-range input with an anchored two-month range calendar.
|
|
98
|
+
* @fires DateRangeBox#change
|
|
99
|
+
* @fires DateRangeBox#invalid
|
|
100
|
+
* @fires DateRangeBox#open
|
|
101
|
+
* @fires DateRangeBox#close
|
|
102
|
+
* @extends {Component<DateRangeBoxOptions>}
|
|
103
|
+
*/
|
|
104
|
+
export class DateRangeBox extends Component {
|
|
105
|
+
static cssName = 'date-range-box';
|
|
106
|
+
|
|
107
|
+
/** @type {DateRangeBoxOptions} */
|
|
108
|
+
static defaults = {
|
|
109
|
+
start: null,
|
|
110
|
+
end: null,
|
|
111
|
+
format: '%d.%m.%Y',
|
|
112
|
+
separator: ' – ',
|
|
113
|
+
placeholder: null,
|
|
114
|
+
clearable: true,
|
|
115
|
+
min: null,
|
|
116
|
+
max: null,
|
|
117
|
+
months: 2,
|
|
118
|
+
weekStart: 1,
|
|
119
|
+
showWeekNumbers: false,
|
|
120
|
+
minNights: 0,
|
|
121
|
+
maxNights: null,
|
|
122
|
+
presets: [],
|
|
123
|
+
disabled: false
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// State used by render() is initialized inside it, never as class fields: render() runs from the
|
|
127
|
+
// base constructor, before field initializers would reset it (see AGENTS.md).
|
|
128
|
+
|
|
129
|
+
/** @returns {HTMLElement} */
|
|
130
|
+
render() {
|
|
131
|
+
const root = /** @type {HTMLElement} */ (this.el ?? h('div'));
|
|
132
|
+
this.el = root;
|
|
133
|
+
this._originalState = root.getAttribute('data-state');
|
|
134
|
+
this._originalAriaDisabled = root.getAttribute('aria-disabled');
|
|
135
|
+
this._format = String(this.options.format || '%d.%m.%Y');
|
|
136
|
+
this._separator = String(this.options.separator ?? ' – ');
|
|
137
|
+
this._disabled = Boolean(this.options.disabled);
|
|
138
|
+
this._open = false;
|
|
139
|
+
this._position = null;
|
|
140
|
+
this._range = this._coerce({ start: this.options.start, end: this.options.end });
|
|
141
|
+
const popoverId = uid('zx-date-range-box-popover');
|
|
142
|
+
|
|
143
|
+
const content = h('div', { class: 'zx-date-range-box__content' },
|
|
144
|
+
h('div', { ref: 'control', class: 'zx-date-range-box__control' },
|
|
145
|
+
h('input', {
|
|
146
|
+
ref: 'input',
|
|
147
|
+
class: 'zx-date-range-box__input',
|
|
148
|
+
type: 'text',
|
|
149
|
+
autocomplete: 'off',
|
|
150
|
+
placeholder: this.options.placeholder ?? this._placeholder(),
|
|
151
|
+
'aria-haspopup': 'grid',
|
|
152
|
+
ariaControls: popoverId,
|
|
153
|
+
ariaExpanded: 'false'
|
|
154
|
+
}),
|
|
155
|
+
h('button', {
|
|
156
|
+
ref: 'clear',
|
|
157
|
+
class: 'zx-icon-btn zx-date-range-box__clear',
|
|
158
|
+
type: 'button',
|
|
159
|
+
ariaLabel: text('daterange.clear', 'Clear date range')
|
|
160
|
+
}, icon('x', { size: 13 })),
|
|
161
|
+
h('button', {
|
|
162
|
+
ref: 'toggle',
|
|
163
|
+
class: 'zx-date-range-box__toggle',
|
|
164
|
+
type: 'button',
|
|
165
|
+
ariaLabel: text('daterange.choose', 'Choose date range'),
|
|
166
|
+
'aria-haspopup': 'grid',
|
|
167
|
+
ariaControls: popoverId,
|
|
168
|
+
ariaExpanded: 'false'
|
|
169
|
+
}, icon('calendar'))
|
|
170
|
+
),
|
|
171
|
+
h('div', {
|
|
172
|
+
ref: 'popover',
|
|
173
|
+
id: popoverId,
|
|
174
|
+
class: 'zx-date-range-box__popover',
|
|
175
|
+
popover: 'manual',
|
|
176
|
+
dataset: { state: 'closed' }
|
|
177
|
+
})
|
|
178
|
+
);
|
|
179
|
+
this._content = content;
|
|
180
|
+
root.append(content);
|
|
181
|
+
|
|
182
|
+
this._picker = new DateRangePicker(null, {
|
|
183
|
+
start: this._range.start,
|
|
184
|
+
end: this._range.end,
|
|
185
|
+
min: this.options.min,
|
|
186
|
+
max: this.options.max,
|
|
187
|
+
months: this.options.months,
|
|
188
|
+
weekStart: this.options.weekStart,
|
|
189
|
+
showWeekNumbers: this.options.showWeekNumbers,
|
|
190
|
+
minNights: this.options.minNights,
|
|
191
|
+
maxNights: this.options.maxNights,
|
|
192
|
+
presets: this.options.presets
|
|
193
|
+
});
|
|
194
|
+
this.refs.popover.append(this._picker.el);
|
|
195
|
+
// `select` mirrors the half-open state into the input; `change` commits and closes.
|
|
196
|
+
this._picker.on('select', (event) => {
|
|
197
|
+
this._range = { start: event.detail.start, end: event.detail.end };
|
|
198
|
+
this._setValid();
|
|
199
|
+
this._syncDisplay();
|
|
200
|
+
});
|
|
201
|
+
this._picker.on('change', (event) => {
|
|
202
|
+
this._apply({ start: event.detail.start, end: event.detail.end }, { push: false });
|
|
203
|
+
if (event.detail.start && event.detail.end) this._close(true);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Moving focus into the open calendar blurs the input; committing then would turn a
|
|
207
|
+
// half-picked range into a one-day range behind the user's back.
|
|
208
|
+
this.listen(this.refs.input, 'blur', () => {
|
|
209
|
+
if (!this._open) this._commitText();
|
|
210
|
+
});
|
|
211
|
+
this.listen(this.refs.input, 'keydown', (event) => this._onInputKeydown(event));
|
|
212
|
+
this.listen(this.refs.toggle, 'click', () => this._open ? this.close() : this.open());
|
|
213
|
+
this.listen(this.refs.toggle, 'keydown', (event) => {
|
|
214
|
+
if (event.key !== 'ArrowDown') return;
|
|
215
|
+
event.preventDefault();
|
|
216
|
+
this.open();
|
|
217
|
+
});
|
|
218
|
+
this.listen(this.refs.clear, 'click', () => {
|
|
219
|
+
this.clear();
|
|
220
|
+
this.focus();
|
|
221
|
+
});
|
|
222
|
+
this.listen(document, 'keydown', (event) => {
|
|
223
|
+
if (!this._open || event.key !== 'Escape') return;
|
|
224
|
+
event.preventDefault();
|
|
225
|
+
this._close(true);
|
|
226
|
+
});
|
|
227
|
+
this.listen(document, 'pointerdown', (event) => {
|
|
228
|
+
if (this._open && !root.contains(event.target)) this.close();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
this._syncDisplay();
|
|
232
|
+
this._syncDisabled();
|
|
233
|
+
return root;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Returns the selected range as dates or as Unix timestamps.
|
|
238
|
+
* @param {DateRangeBoxUnit} [unit='date'] Return unit.
|
|
239
|
+
* @returns {DateRange|DateRangeSeconds}
|
|
240
|
+
*/
|
|
241
|
+
get(unit = 'date') {
|
|
242
|
+
if (unit === 'seconds') {
|
|
243
|
+
return {
|
|
244
|
+
start: this._range.start ? Math.floor(this._range.start.getTime() / 1000) : null,
|
|
245
|
+
end: this._range.end ? Math.floor(this._range.end.getTime() / 1000) : null
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
start: this._range.start ? new Date(this._range.start.getTime()) : null,
|
|
250
|
+
end: this._range.end ? new Date(this._range.end.getTime()) : null
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Sets a range. Each endpoint may be a Date, Unix seconds, or text in the configured format;
|
|
256
|
+
* a whole formatted range string and `null` are accepted too.
|
|
257
|
+
* @param {DateRange|{start: DateRangeInput, end: DateRangeInput}|string|null} range New range.
|
|
258
|
+
* @param {DateRangeBoxSetOptions} [options={}] Update behavior.
|
|
259
|
+
* @returns {this}
|
|
260
|
+
* @fires DateRangeBox#change
|
|
261
|
+
*/
|
|
262
|
+
set(range, { silent = false } = {}) {
|
|
263
|
+
this._apply(this._coerce(range), { silent });
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Clears the selection.
|
|
269
|
+
* @param {DateRangeBoxSetOptions} [options={}] Update behavior.
|
|
270
|
+
* @returns {this}
|
|
271
|
+
* @fires DateRangeBox#change
|
|
272
|
+
*/
|
|
273
|
+
clear({ silent = false } = {}) {
|
|
274
|
+
return this.set({ start: null, end: null }, { silent });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Opens and positions the range calendar, then moves focus into its grid.
|
|
279
|
+
* @returns {this}
|
|
280
|
+
* @fires DateRangeBox#open
|
|
281
|
+
*/
|
|
282
|
+
open() {
|
|
283
|
+
if (this._disabled || this._open) return this;
|
|
284
|
+
this._picker.set(this._range, { silent: true });
|
|
285
|
+
this._open = true;
|
|
286
|
+
this.refs.popover.dataset.state = 'open';
|
|
287
|
+
this.refs.input.setAttribute('aria-expanded', 'true');
|
|
288
|
+
this.refs.toggle.setAttribute('aria-expanded', 'true');
|
|
289
|
+
this._position = position(this.refs.control, this.refs.popover, {
|
|
290
|
+
placement: 'bottom-start',
|
|
291
|
+
flip: true
|
|
292
|
+
});
|
|
293
|
+
this._picker.focus();
|
|
294
|
+
this.emit('open');
|
|
295
|
+
return this;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Closes the range calendar.
|
|
300
|
+
* @returns {this}
|
|
301
|
+
* @fires DateRangeBox#close
|
|
302
|
+
*/
|
|
303
|
+
close() {
|
|
304
|
+
this._close(false);
|
|
305
|
+
return this;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Enables the box. @returns {this} */
|
|
309
|
+
enable() {
|
|
310
|
+
this._disabled = false;
|
|
311
|
+
this._syncDisabled();
|
|
312
|
+
return this;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Disables the box and closes its popover. @returns {this} */
|
|
316
|
+
disable() {
|
|
317
|
+
this._disabled = true;
|
|
318
|
+
this.close();
|
|
319
|
+
this._syncDisabled();
|
|
320
|
+
return this;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Moves focus to the text input. @returns {this} */
|
|
324
|
+
focus() {
|
|
325
|
+
this.refs.input.focus();
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Cleans up the open popover, child picker, and generated host content. @returns {void} */
|
|
330
|
+
destroy() {
|
|
331
|
+
this._position?.destroy();
|
|
332
|
+
this._position = null;
|
|
333
|
+
this._open = false;
|
|
334
|
+
this._picker?.destroy();
|
|
335
|
+
this._picker = null;
|
|
336
|
+
this._content?.remove();
|
|
337
|
+
restoreAttribute(this.el, 'data-state', this._originalState);
|
|
338
|
+
restoreAttribute(this.el, 'aria-disabled', this._originalAriaDisabled);
|
|
339
|
+
super.destroy();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* --------------------------------------------------------------------- internals -- */
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Stores a range, optionally pushing it back into the picker, and reports it.
|
|
346
|
+
* @param {DateRange} range Already-coerced range.
|
|
347
|
+
* @param {{silent?: boolean, push?: boolean}} [options={}] Update behavior.
|
|
348
|
+
* @returns {void}
|
|
349
|
+
* @fires DateRangeBox#change
|
|
350
|
+
*/
|
|
351
|
+
_apply(range, { silent = false, push = true } = {}) {
|
|
352
|
+
this._range = range;
|
|
353
|
+
if (push) this._picker?.set(this._range, { silent: true });
|
|
354
|
+
this._setValid();
|
|
355
|
+
this._syncDisplay();
|
|
356
|
+
if (!silent) this.emit('change', this.get());
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** @param {KeyboardEvent} event @returns {void} */
|
|
360
|
+
_onInputKeydown(event) {
|
|
361
|
+
if (event.key === 'ArrowDown') {
|
|
362
|
+
event.preventDefault();
|
|
363
|
+
this.open();
|
|
364
|
+
} else if (event.key === 'Enter') {
|
|
365
|
+
event.preventDefault();
|
|
366
|
+
this._commitText();
|
|
367
|
+
} else if (event.key === 'Escape' && this._open) {
|
|
368
|
+
event.preventDefault();
|
|
369
|
+
this._close(true);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** @returns {void} */
|
|
374
|
+
_commitText() {
|
|
375
|
+
if (this._disabled) return;
|
|
376
|
+
const value = this.refs.input.value.trim();
|
|
377
|
+
if (value === '') {
|
|
378
|
+
this.set({ start: null, end: null });
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const parsed = parseRangeText(value, this._format, this._separator);
|
|
382
|
+
if (!parsed) {
|
|
383
|
+
this.el.dataset.state = 'invalid';
|
|
384
|
+
this.refs.input.setAttribute('aria-invalid', 'true');
|
|
385
|
+
this.emit('invalid', { text: value });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
this._apply(this._clamp(parsed));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Coerces any accepted input shape into a bounded range of local dates.
|
|
393
|
+
* @param {unknown} value Range-ish input.
|
|
394
|
+
* @returns {DateRange}
|
|
395
|
+
*/
|
|
396
|
+
_coerce(value) {
|
|
397
|
+
if (value === null || value === undefined) return { start: null, end: null };
|
|
398
|
+
if (typeof value === 'string') {
|
|
399
|
+
return this._clamp(parseRangeText(value, this._format, this._separator) ?? { start: null, end: null });
|
|
400
|
+
}
|
|
401
|
+
if (Array.isArray(value)) {
|
|
402
|
+
return this._clamp({ start: this._toDate(value[0]), end: this._toDate(value[1]) });
|
|
403
|
+
}
|
|
404
|
+
if (typeof value !== 'object') return { start: null, end: null };
|
|
405
|
+
return this._clamp({
|
|
406
|
+
start: this._toDate(/** @type {Record<string, unknown>} */ (value).start),
|
|
407
|
+
end: this._toDate(/** @type {Record<string, unknown>} */ (value).end)
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** @param {unknown} value @returns {Date|null} */
|
|
412
|
+
_toDate(value) {
|
|
413
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : new Date(value.getTime());
|
|
414
|
+
if (typeof value === 'number' && Number.isFinite(value)) return new Date(value * 1000);
|
|
415
|
+
if (typeof value === 'string' && value.trim() !== '') return parseDate(value.trim(), this._format);
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** @param {DateRange} range @returns {DateRange} */
|
|
420
|
+
_clamp(range) {
|
|
421
|
+
return clampRange(range, {
|
|
422
|
+
min: this.options.min,
|
|
423
|
+
max: this.options.max,
|
|
424
|
+
minNights: this.options.minNights,
|
|
425
|
+
maxNights: this.options.maxNights
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** @returns {string} */
|
|
430
|
+
_placeholder() {
|
|
431
|
+
const one = this._format.replace(/%[demYy]/g, (token) => PLACEHOLDER_TOKENS[token] ?? token);
|
|
432
|
+
return `${one}${this._separator}${one}`;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** @returns {void} */
|
|
436
|
+
_syncDisplay() {
|
|
437
|
+
this.refs.input.value = formatRangeText(this._range, this._format, this._separator);
|
|
438
|
+
this.refs.clear.hidden = !this.options.clearable || !(this._range.start || this._range.end);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** @returns {void} */
|
|
442
|
+
_syncDisabled() {
|
|
443
|
+
this.refs.input.disabled = this._disabled;
|
|
444
|
+
this.refs.toggle.disabled = this._disabled;
|
|
445
|
+
this.refs.clear.disabled = this._disabled;
|
|
446
|
+
this.el.setAttribute('aria-disabled', String(this._disabled));
|
|
447
|
+
if (this._disabled) this._picker?.disable();
|
|
448
|
+
else this._picker?.enable();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** @returns {void} */
|
|
452
|
+
_setValid() {
|
|
453
|
+
if (this.el.dataset.state === 'invalid') restoreAttribute(this.el, 'data-state', this._originalState);
|
|
454
|
+
this.refs.input.removeAttribute('aria-invalid');
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** @param {boolean} returnFocus @returns {void} */
|
|
458
|
+
_close(returnFocus) {
|
|
459
|
+
if (!this._open) return;
|
|
460
|
+
this._position?.destroy();
|
|
461
|
+
this._position = null;
|
|
462
|
+
this._open = false;
|
|
463
|
+
this.refs.popover.dataset.state = 'closed';
|
|
464
|
+
this.refs.input.setAttribute('aria-expanded', 'false');
|
|
465
|
+
this.refs.toggle.setAttribute('aria-expanded', 'false');
|
|
466
|
+
if (returnFocus) this.focus();
|
|
467
|
+
this.emit('close');
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** @event DateRangeBox#change @type {CustomEvent<DateRange>} */
|
|
472
|
+
/** @event DateRangeBox#invalid @type {CustomEvent<{text: string}>} */
|
|
473
|
+
/** @event DateRangeBox#open @type {CustomEvent<Record<string, never>>} */
|
|
474
|
+
/** @event DateRangeBox#close @type {CustomEvent<Record<string, never>>} */
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Resolves a built-in string through the host translator, falling back to the English original.
|
|
478
|
+
* @param {string} key Translation key.
|
|
479
|
+
* @param {string} fallback English text used when the key is untranslated.
|
|
480
|
+
* @param {unknown[]} [args] Interpolation values for `%1`, `%2`, …
|
|
481
|
+
* @returns {string}
|
|
482
|
+
*/
|
|
483
|
+
function text(key, fallback, args) {
|
|
484
|
+
const translated = translate(key);
|
|
485
|
+
return printf(translated === key ? fallback : translated, args);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** @param {Element} element @param {string} name @param {string|null} value @returns {void} */
|
|
489
|
+
function restoreAttribute(element, name, value) {
|
|
490
|
+
if (value === null) element.removeAttribute(name);
|
|
491
|
+
else element.setAttribute(name, value);
|
|
492
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
.zx-datebox {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
color: var(--zx-color-text);
|
|
4
|
+
font-size: var(--zx-control-font-size);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.zx-datebox__content,
|
|
8
|
+
.zx-datebox__control {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: stretch;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.zx-datebox__control {
|
|
14
|
+
position: relative;
|
|
15
|
+
border: 1px solid var(--zx-color-border-control);
|
|
16
|
+
border-radius: var(--zx-control-radius);
|
|
17
|
+
background: var(--zx-color-bg-control);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.zx-datebox__control:focus-within {
|
|
21
|
+
box-shadow: var(--zx-focus-ring);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.zx-datebox[data-state="invalid"] .zx-datebox__control {
|
|
25
|
+
border-color: var(--zx-color-danger);
|
|
26
|
+
background: var(--zx-color-danger-bg);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* The input always reserves room for the clear button, which floats over its trailing edge —
|
|
30
|
+
showing or hiding the button must not change the control's total width. */
|
|
31
|
+
.zx-datebox__input {
|
|
32
|
+
min-inline-size: 10em;
|
|
33
|
+
border: 0;
|
|
34
|
+
border-radius: var(--zx-control-radius) 0 0 var(--zx-control-radius);
|
|
35
|
+
background: inherit;
|
|
36
|
+
padding-inline-end: var(--zx-control-height);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Focused controls lift above their siblings so the ring is never clipped. */
|
|
40
|
+
.zx-datebox__input:focus-visible,
|
|
41
|
+
.zx-datebox__toggle:focus-visible {
|
|
42
|
+
position: relative;
|
|
43
|
+
z-index: 1;
|
|
44
|
+
box-shadow: var(--zx-focus-ring);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.zx-datebox__clear:focus-visible {
|
|
48
|
+
box-shadow: var(--zx-focus-ring);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.zx-datebox__clear,
|
|
52
|
+
.zx-datebox__toggle {
|
|
53
|
+
display: inline-grid;
|
|
54
|
+
place-items: center;
|
|
55
|
+
min-inline-size: var(--zx-control-height);
|
|
56
|
+
padding: 0;
|
|
57
|
+
border: 0;
|
|
58
|
+
border-radius: 0;
|
|
59
|
+
background: inherit;
|
|
60
|
+
color: var(--zx-color-text-muted);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.zx-datebox__toggle {
|
|
64
|
+
border-inline-start: 1px solid var(--zx-color-border-control);
|
|
65
|
+
border-radius: 0 var(--zx-control-radius) var(--zx-control-radius) 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.zx-datebox__clear {
|
|
69
|
+
position: absolute;
|
|
70
|
+
/* The clear button floats over the input's trailing padding, so it has to outrank the input in
|
|
71
|
+
the stacking order — a focused input raises itself to z-index 1 and would otherwise cover the
|
|
72
|
+
button and swallow its clicks. */
|
|
73
|
+
z-index: 2;
|
|
74
|
+
inset-block-start: 50%;
|
|
75
|
+
/* The picker toggle sits to the right of the input; float the clear button just inside it. */
|
|
76
|
+
inset-inline-end: var(--zx-control-height);
|
|
77
|
+
transform: translateY(-50%);
|
|
78
|
+
background: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.zx-datebox__clear:hover {
|
|
82
|
+
color: var(--zx-color-text);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.zx-datebox__clear[hidden] {
|
|
86
|
+
display: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.zx-datebox__popover {
|
|
90
|
+
overflow: visible;
|
|
91
|
+
max-inline-size: calc(100vw - var(--zx-space-4));
|
|
92
|
+
border: 0;
|
|
93
|
+
background: var(--zx-color-bg-raised);
|
|
94
|
+
padding: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.zx-datebox__popover:not(:popover-open)[data-state="closed"] {
|
|
98
|
+
display: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.zx-datebox__popover[data-state="open"] {
|
|
102
|
+
display: block;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.zx-datebox__popover .zx-date-picker__surface {
|
|
106
|
+
background: var(--zx-color-bg-raised);
|
|
107
|
+
box-shadow: var(--zx-overlay-shadow);
|
|
108
|
+
}
|