Overview of Duet Date Picker
masterYYYY-MM-DD).repository·master·Indexed 23 days ago
https://github.com/duetds/date-pickerA framework-agnostic, highly accessible date picker component built with Web Components and Stencil.js. Designed to meet WCAG 2.1 standards, it supports ISO-8601 date formats and can be integrated into any web project via CDN or NPM, including Angular, Vue.js, React, and Ember. It features comprehensive keyboard navigation, custom localization, and date adapters for flexible parsing and formatting.
YYYY-MM-DD).The component follows W3C Date Picker Dialog patterns to ensure accessibility. Key interactions include:
Space, Enter: Opens the dialog and focuses the first select menu.Esc: Closes the dialog and returns focus to the "choose date" button.Tab / Shift + Tab: Navigates through elements. Note that the calendar grid uses role="grid", so only one button in the grid is in the tab sequence.Space, Enter: Changes the displayed month or year.Space, Enter: Selects the date, closes the dialog, and updates the input value.Arrow keys (Up, Down, Left, Right): Moves focus between days.Home / End: Moves focus to the start or end of the current week.Page Up / Page Down: Changes the month.Shift + Page Up / Shift + Page Down: Changes the year.Space, Enter: Closes the dialog without updating the input value.To use Duet Date Picker in a simple HTML page without a JavaScript framework, include the following scripts and stylesheet in your <head> tag. The scripts use ESM for modern browsers and a nomodule fallback for older ones. The CSS import is optional and only required if you want to use the default theme.
<script type="module" src="https://cdn.jsdelivr.net/npm/@duetds/date-picker@1.4.0/dist/duet/duet.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@duetds/date-picker@1.4.0/dist/duet/duet.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/date-picker@1.4.0/dist/duet/themes/default.css" />Once the scripts are loaded, you can use the <duet-date-picker> element in your markup:
<label for="date">Choose a date</label>
<duet-date-picker identifier="date"></duet-date-picker>To integrate Duet Date Picker into an Angular application:
CUSTOM_ELEMENTS_SCHEMA to your module (e.g., AppModule) to prevent the compiler from erroring on web components.defineCustomElements(window) during application bootstrapping, typically in main.ts.duet.css separately if you want to use the default theme.Note: You can reference the component in your TypeScript code using ViewChild or ViewChildren as per standard Stencil.js/Angular integration patterns.
// In AppModule
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }
// In main.ts
import { defineCustomElements } from "@duetds/date-picker/dist/loader";
defineCustomElements(window);You can provide full localization support by setting the localization and dateAdapter properties on the duet-date-picker element.
Important: When overriding localization, you must provide the entirety of the localization properties in the object.
The dateAdapter allows you to define how dates are parsed from strings and how they are formatted back into strings, which is essential for non-standard date formats.
<label for="date">Valitse päivämäärä</label>
<duet-date-picker identifier="date"></duet-date-picker>
<script>
const picker = document.querySelector("duet-date-picker")
const DATE_FORMAT = /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/
picker.dateAdapter = {
parse(value = "", createDate) {
const matches = value.match(DATE_FORMAT)
if (matches) {
return createDate(matches[3], matches[2], matches[1])
}
},
format(date) {
return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`
},
}
picker.localization = {
buttonLabel: "Valitse päivämäärä",
placeholder: "pp.kk.vvvv",
selectedDateMessage: "Valittu päivämäärä on",
prevMonthLabel: "Edellinen kuukausi",
nextMonthLabel: "Seuraava kuukausi",
monthSelectLabel: "Kuukausi",
yearSelectLabel: "Vuosi",
closeLabel: "Sulje ikkuna",
calendarHeading: "Valitse päivämäärä",
dayNames: [
"Sunnuntai", "Maanantai", "Tiistai", "Keskiviikko",
"Torstai", "Perjantai", "Lauantai"
],
monthNames: [
"Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu",
"Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu",
"Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu"
],
monthNamesShort: [
"Tammi", "Helmi", "Maalis", "Huhti", "Touko", "Kesä",
"Heinä", "Elo", "Syys", "Loka", "Marras", "Joulu"
],
locale: "fi-FI",
}
</script>To support older browsers like IE11 and Edge 17/18, you must wrap the defineCustomElements() call with applyPolyfills() from the library's loader.
import { applyPolyfills, defineCustomElements } from "@duetds/date-picker/lib/loader";
applyPolyfills().then(() => {
defineCustomElements(window)
})import { applyPolyfills, defineCustomElements } from "@duetds/date-picker/lib/loader";
// ...
applyPolyfills().then(() => {
defineCustomElements(window)
})Integration with Ember is facilitated by the ember-cli-stencil addon.
ember install ember-cli-stencil ember-auto-import.Can't resolve errors during build, add an alias to your ember-cli-build.js:autoImport: {
alias: {
'@duetds/date-picker/loader': '@duetds/date-picker/dist/loader/index.cjs',
},
},{{prop}} helper:<duet-date-picker identifier="date" {{prop localization=this.localization}} ></duet-date-picker>// ember-cli-build.js
autoImport: {
alias: {
'@duetds/date-picker/loader': '@duetds/date-picker/dist/loader/index.cjs',
},
},For React applications (e.g., create-react-app), follow these steps:
defineCustomElements(window) in your index.js.useRef to get a reference to the DOM element.useEffect to attach event listeners for custom events (like duetChange, duetFocus, etc.).useEffect to sync React props (like localization and dateAdapter) to the element's properties.duet.css separately for the default theme.import React, { useEffect, useRef } from "react";
export function DatePicker({
onChange,
onFocus,
onBlur,
onOpen,
onClose,
dateAdapter,
localization,
...props
}) {
const ref = useRef(null)
// Example listener pattern
useListener(ref, "duetChange", onChange)
useEffect(() => {
ref.current.localization = localization
ref.current.dateAdapter = dateAdapter
}, [localization, dateAdapter])
return <duet-date-picker ref={ref} {...props}></duet-date-picker>
}To use the Duet Date Picker as a web component in HTML, Ember, Vue.js, React, Angular, or Vanilla JS, install it using npm. Ensure you have Node.js and a package.json file initialized in your project.
# WEB COMPONENT for HTML, Ember, Vue.js, React, Angular and Vanilla JS:
npm install @duetds/date-pickerDuet Date Picker uses CSS Custom Properties for theming. You can either import the default theme from NPM or a CDN, or override specific properties in your own stylesheet.
Recommendation: To customize the theme, do NOT link to the provided CSS file. Instead, copy the default Custom Properties into your own stylesheet and replace the values. This prevents issues when the library updates.
You can also override default styles using specific selectors like .duet-date__input to match your website's visual style.
/* Example of overriding default properties */
:root {
--duet-color-primary: #005fcc;
--duet-color-text: #333;
--duet-color-text-active: #fff;
--duet-color-placeholder: #666;
--duet-color-button: #f5f5f5;
--duet-color-surface: #fff;
--duet-color-overlay: rgba(0, 0, 0, 0.8);
--duet-color-border: #333;
--duet-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--duet-font-normal: 400;
--duet-font-bold: 600;
--duet-radius: 4px;
--duet-z-index: 600;
}To integrate Duet Date Picker into a Vue.js application:
defineCustomElements from @duetds/date-picker/dist/loader and call defineCustomElements(window) in your main.js.Vue.config.ignoredElements = [/duet-\w*/];.duet.css separately for the default theme.localization) in Vue, use the .prop suffix (e.g., :localization.prop="myConfig") to ensure Vue passes them as properties rather than attributes.Example localization object structure:
const localisation_uk = {
buttonLabel: 'Choose date',
placeholder: 'DD/MM/YYYY',
selectedDateMessage: 'Selected date is',
prevMonthLabel: 'Previous month',
nextMonthLabel: 'Next month',
monthSelectLabel: 'Month',
yearSelectLabel: 'Year',
closeLabel: 'Close window',
calendarHeading: 'Choose a date',
dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
}import { defineCustomElements } from "@duetds/date-picker/dist/loader";
Vue.config.ignoredElements = [/duet-\w*/];
defineCustomElements(window);
new Vue({
render: h => h(App)
}).$mount("#app");The package includes a hydrate app designed for NodeJS environments to generate static HTML and CSS. This allows components to function without JavaScript initially.
To use it, import the hydrate module:
import hydrate from "@duetds/date-picker/hydrate"When using a tool like Eleventy, you can use hydrate.renderToString(content, options) within a transform to process your content.
Options for renderToString:
clientHydrateAnnotations: booleanremoveScripts: booleanremoveUnusedStyles: booleanNote: You must separately pre-render content for each theme you intend to support.
import hydrate from "@duetds/date-picker/hydrate"
// Example Eleventy transform
eleventyConfig.addTransform("hydrate", async(content, outputPath) => {
if (process.env.ELEVENTY_ENV == "production") {
if (outputPath.endsWith(".html")) {
try {
const results = await hydrate.renderToString(content, {
clientHydrateAnnotations: true,
removeScripts: false,
removeUnusedStyles: false
})
return results.html
} catch (error) {
return error
}
}
}
return content
})