Duet Date Picker

repository·master·Indexed 23 days ago

https://github.com/duetds/date-picker

A 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.

Tokens
7.8K
Snippets
18
Records
34
Agent score
68%

What's inside @duetds/date-picker

  1. Overview of Duet Date Picker

    master
    Duet Date Picker is an accessible, open-source date picker built using Web Components and Stencil.js. It is designed to be framework-agnostic, meaning it can be used with any JavaScript framework or in plain HTML. It focuses heavily on accessibility (WCAG 2.1 compliance) and provides built-in support for setting minimum and maximum allowed dates using ISO-8601 format (YYYY-MM-DD).
  2. Keyboard Navigation for Duet Date Picker

    master

    The component follows W3C Date Picker Dialog patterns to ensure accessibility. Key interactions include:

    Choose date button

    • Space, Enter: Opens the dialog and focuses the first select menu.

    Date picker dialog

    • 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.

    Month/year buttons

    • Space, Enter: Changes the displayed month or year.

    Date grid

    • 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.

    Close button

    • Space, Enter: Closes the dialog without updating the input value.
  3. Quickstart: Use Duet Date Picker in plain HTML

    master

    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>
  4. Use Duet Date Picker with Angular

    master

    To integrate Duet Date Picker into an Angular application:

    1. Enable Custom Elements: Add CUSTOM_ELEMENTS_SCHEMA to your module (e.g., AppModule) to prevent the compiler from erroring on web components.
    2. Register the Component: Call defineCustomElements(window) during application bootstrapping, typically in main.ts.
    3. Styling: Import 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);
  5. Localize the Duet Date Picker

    master

    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>
  6. Add polyfills for IE11 and Edge 17/18

    master

    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)
    })
  7. Use Duet Date Picker with Ember

    master

    Integration with Ember is facilitated by the ember-cli-stencil addon.

    1. Install Addons: ember install ember-cli-stencil ember-auto-import.
    2. Handle Build Errors: If you encounter 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',
      },
    },
    1. Usage: In Ember Octane, use the component in your template, passing properties using the {{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',
      },
    },
  8. Use Duet Date Picker with React

    master

    For React applications (e.g., create-react-app), follow these steps:

    1. Register the Component: Call defineCustomElements(window) in your index.js.
    2. Create a Wrapper: Because React handles DOM elements differently than Web Components, it is recommended to create a thin wrapper component. This wrapper should:
      • Use useRef to get a reference to the DOM element.
      • Use useEffect to attach event listeners for custom events (like duetChange, duetFocus, etc.).
      • Use useEffect to sync React props (like localization and dateAdapter) to the element's properties.
    3. Styling: Import 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>
    }
  9. Install Duet Date Picker via npm

    master

    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-picker
  10. Theme the Duet Date Picker with CSS Custom Properties

    master

    Duet 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;
    }
  11. Use Duet Date Picker with Vue.js

    master

    To integrate Duet Date Picker into a Vue.js application:

    1. Register the Component: Import defineCustomElements from @duetds/date-picker/dist/loader and call defineCustomElements(window) in your main.js.
    2. Configure Vue: Tell Vue to ignore Duet components by setting Vue.config.ignoredElements = [/duet-\w*/];.
    3. Styling: Import duet.css separately for the default theme.
    4. Properties vs Attributes: When passing custom properties (like 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");
  12. Implement Server Side Rendering (SSR) with `@duetds/date-picker/hydrate`

    master

    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: boolean
    • removeScripts: boolean
    • removeUnusedStyles: boolean

    Note: 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
    })