Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f6170e2
refactor: Make styles an array
Artur- Jul 7, 2026
e9c5496
feat: add disabled dates support to date-picker and date-time-picker
Artur- Jul 7, 2026
79c852f
fix: rewrite disabled dates dev page loops to satisfy Sonar
Artur- Jul 7, 2026
4c3e8c9
Aura dark images
Artur- Jul 7, 2026
7aafe21
test: wait for provider resolution to settle to avoid Firefox flake
Artur- Jul 7, 2026
a12a22c
Remove date time picker from this branch
Artur- Jul 7, 2026
6a688d3
docs: describe loader/loading as generic data-loading state
Artur- Jul 7, 2026
35f76a2
fix: validate disabled dates provider without opening overlay
Artur- Jul 7, 2026
82f3e47
fix: move initial focus off provider-disabled date
Artur- Jul 7, 2026
b870b66
refactor: compose loader styles in overlay content component
Artur- Jul 7, 2026
10968aa
chore: address sonar issues in disabled dates code
Artur- Jul 7, 2026
d1dcbe8
refactor: simplify disabled dates state handling
Artur- Jul 8, 2026
41e071b
Merge remote-tracking branch 'origin/main' into feature/disable-dates
Artur- Jul 8, 2026
273e8f9
chore: add scripts for feature snapshot publishing to npm
Artur- Jul 8, 2026
8900560
Support custom registry and testing with DRY_RUN
Artur- Jul 8, 2026
d5a90aa
fix: commit version bump before publish to satisfy clean-tree check
Artur- Jul 8, 2026
14b7f66
feat: fail fast when not authenticated to npm
Artur- Jul 8, 2026
3a3c2d7
fix: authenticate to npm using NPM_TOKEN
Artur- Jul 8, 2026
974d399
feat: generalize disabled dates into a date metadata provider
Artur- Jul 13, 2026
0917be7
Merge remote-tracking branch 'origin/main' into feature/disable-dates
Artur- Jul 13, 2026
2dd0c9b
docs: document custom date part names in the parts table
Artur- Jul 13, 2026
e216d13
feat: add clearCache to reload date metadata on demand
Artur- Jul 13, 2026
7cacdc9
docs: explain why disabled and part share one provider
Artur- Jul 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions dev/date-picker.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,46 @@
picker.isDateDisabled = isDateDisabled;
picker.min = '2023-11-01';
</script>

<h3>Date metadata provider</h3>
<p>
Metadata for each date is loaded on demand (a spinner is shown while loading) via
<code>dateMetadataProvider</code>. Mondays and the 20th of each month are <code>disabled</code>; the 10th and 25th
are enabled but tagged with a custom <code>part</code> name (<code>busy</code>) and styled orange. Results are
cached and prefetched around the visible months, so scrolling back and forth does not re-fetch.
</p>

<!-- Custom part names are styled from plain page CSS. The month calendars render in the light
DOM, so `vaadin-month-calendar::part()` reaches the date cells without registerStyles. -->
<style>
vaadin-month-calendar::part(busy) {
color: var(--vaadin-warning-contrast, #a05a00);
background: var(--vaadin-warning-color-10pct, rgba(255, 153, 0, 0.15));
}
</style>

<vaadin-date-picker id="date-metadata-provider" label="Reservation date"></vaadin-date-picker>

<script type="module">
document.querySelector('#date-metadata-provider').dateMetadataProvider = ({ start, end }) =>
new Promise((resolve) => {
setTimeout(() => {
const metadata = [];
const first = new Date(start.year, start.month, start.day);
const last = new Date(end.year, end.month, end.day);
const days = Math.round((last - first) / (24 * 60 * 60 * 1000));
for (let i = 0; i <= days; i++) {
const date = new Date(first.getFullYear(), first.getMonth(), first.getDate() + i);
const entry = { year: date.getFullYear(), month: date.getMonth(), day: date.getDate() };
if (date.getDay() === 1 || date.getDate() === 20) {
metadata.push({ ...entry, disabled: true });
} else if (date.getDate() === 10 || date.getDate() === 25) {
metadata.push({ ...entry, part: 'busy' });
}
}
resolve(metadata);
}, 600);
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const overlayContentStyles = css`
height: 100%;
outline: none;
overflow: hidden;
position: relative;
}

:host([desktop]) {
Expand Down Expand Up @@ -49,6 +50,17 @@ export const overlayContentStyles = css`
display: none !important;
}

/* Loading spinner shown while data is being loaded (currently while the disabled dates provider
resolves). Positioned over the month scroller, which paints its own transformed layers, so
grid stacking is not enough. */
[part='loader'] {
position: absolute;
z-index: 1;
inset-block-start: var(--vaadin-date-picker-month-header-font-size, 0.9375rem);
inset-inline: 0;
margin-inline: auto;
}

::slotted([slot='months']) {
--vaadin-infinite-scroller-item-height: round(
var(--vaadin-date-picker-month-header-font-size, 0.9375rem) + 0.75rem +
Expand Down
75 changes: 75 additions & 0 deletions packages/date-picker/src/vaadin-date-metadata-controller.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @license
* Copyright (c) 2016 - 2026 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import type { ReactiveController, ReactiveControllerHost } from 'lit';
import type { DatePickerDateMetadata, DatePickerDateRange } from './vaadin-date-picker-mixin.js';

export type DateMetadataProvider = (
range: DatePickerDateRange,
) => DatePickerDateMetadata[] | Promise<DatePickerDateMetadata[]>;

/**
* A reactive controller that resolves the metadata (disabled state, custom part
* names, ...) for the dates shown by the date-picker's `dateMetadataProvider`.
* It calls the provider once for a range of months, caches the resolved months
* so scrolling back and forth does not re-fetch, and prefetches a buffer of
* months around the requested range.
*/
export declare class DateMetadataController implements ReactiveController {
constructor(host: ReactiveControllerHost, onChange: () => void);

/**
* The provider function, or `null` when none is set.
*/
provider: DateMetadataProvider | null;

/**
* Whether any month range is currently being fetched.
*/
readonly loading: boolean;

hostDisconnected(): void;

/**
* Sets the provider function and clears the cache. Passing the same provider
* again is a no-op. Callers should keep a stable provider reference.
*/
setProvider(provider: DateMetadataProvider | null | undefined): void;

/**
* Clears the cache and invalidates any in-flight requests.
*/
reset(): void;

/**
* Whether the month containing the given date has been fully resolved.
*/
isMonthLoaded(date: Date): boolean;

/**
* The metadata resolved for the given date, or `undefined` when the date has
* no metadata or its month has not been resolved yet.
*/
getMetadata(date: Date): DatePickerDateMetadata | undefined;

/**
* Whether the given date is disabled by the provider. Only returns `true` for
* dates in an already-resolved month.
*/
isDateDisabled(date: Date): boolean;

/**
* Whether the given date cannot be selected yet: it is disabled by the
* provider, or its month has not been resolved. Returns `false` when no
* provider is set.
*/
isDateBlocked(date: Date): boolean;

/**
* Ensures the provider has been consulted for the inclusive range between the
* given dates, expanded by a prefetch buffer of months on each side.
*/
ensureRangeLoaded(startDate: Date, endDate: Date): void;
}
Loading
Loading