dtrpicker
v2.2.2
Published
A lightweight, fluid date/time range picker rendered with SVG.
Maintainers
Readme
dtrPicker
English | 简体中文
A streaming date/time range picker component rendered with SVG, focused on being lightweight, fluid, and customizable.

Features
- Four selection modes —
date/dateTime/dateRange/dateTimeRange - SVG rendering — pixel-perfect control, CSS isolation, no external UI framework required
- Virtual scrolling — smooth paging through calendar panels, with wheel and drag browsing
- Internationalization — built-in locale packs (Simplified Chinese, English, Japanese, etc.) with BCP 47 language tags
- Multiple color schemes — Morandi / Nature / Ocean / Forest / Night, with custom color overrides
- TypeScript support — complete type declarations
- Zero runtime dependencies — only depends on esbuild at build time
Quick Start
npm install dtrpickerimport dtrPicker from 'dtrpicker';
// 1. Create
const picker = new dtrPicker('#my-input', {
mode: 'dateRange',
locale: 'zh-CN',
firstDay: 1,
colorScheme: 'nature',
});
// 2. Listen for value changes.
// NOTE: `value` is an object `{ start, end }` (strings in `YYYY-MM-DD`
// or `YYYY-MM-DD HH:mm` format), NOT a single string. Use `value.start`
// / `value.end` to display it.
picker.onChange((value, meta) => {
if (value) {
console.log(`Selected range: ${value.start} ~ ${value.end}`);
}
console.log('Change source:', meta.source, 'Action:', meta.action);
});
// 3. Panel open/close notifications
picker.onOpen(() => console.log('Panel opened'));
picker.onClose(() => console.log('Panel closed'));
// 4. Programmatic operations
picker.setValue({ start: '2026-07-01', end: '2026-07-10' });
const val = picker.getValue('date');
console.log(val.start.getFullYear()); // 2026
// 5. Cleanup
picker.destroy();Direct <script> via CDN
No build step needed — drop the IIFE bundle in and use the global dtrPicker:
<script src="https://unpkg.com/[email protected]/dist/dtrpicker.iife.js"></script>
<script>
const picker = new dtrPicker('#my-input', {
mode: 'dateRange',
});
</script>Also available on jsDelivr.
Installation & Import
ESM (recommended)
import dtrPicker from 'dtrpicker';CommonJS
const dtrPicker = require('dtrpicker').default;Script tag / CDN (IIFE)
<script src="dist/dtrpicker.iife.js"></script>
<script>
const picker = new dtrPicker('#my-input', { mode: 'dateRange' });
</script>API Reference
Constructor & Options
const picker = new dtrPicker(trigger, options);| Parameter | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| trigger | HTMLElement \| string | — | ✅ | The trigger element or its CSS selector. The component uses it as the panel positioning anchor and click toggle. |
| options | Object | {} | — | The configuration object |
| options.mode | string | — | ✅ | Selection mode: 'date' / 'dateTime' / 'dateRange' / 'dateTimeRange' |
| options.yearMonthMode | 'watermark' \| 'column' | 'watermark' | — | Year/month display mode: 'watermark' watermark overlay / 'column' separate column |
| options.locale | string | '' | — | BCP 47 language tag: 'zh-CN' 'en-US' 'ja-JP', etc. Empty string = auto-detect browser language |
| options.firstDay | 0 \| 1 | 0 | — | First day of the week: 0 = Sunday, 1 = Monday |
| options.colorScheme | string | 'morandi' | — | Color scheme: 'morandi' / 'nature' / 'ocean' / 'forest' / 'night' |
| options.todayBarHeight | number | 6 | — | Height of the today marker bar (px) |
| options.wheelStep | number | 40 | — | Wheel paging step coefficient |
⚠️
mode,yearMonthMode,locale,firstDay,colorSchemecannot be changed after construction.
Color options:
| Parameter | Default (morandi) | Description |
| --- | --- | --- |
| selectedColor | '#2f54eb' | Selected/highlight color |
| selectedTextColor | '#ffffff' | Selected-state text color |
| gridColor | '#d0d0d0' | Grid line color |
| cellColor | '#ffffff' | Cell background color |
| textColor | '#262626' | Primary text color |
| textColorDisabled | '#d9d9d9' | Disabled date text color |
| textColorSubLabel | '#595959' | Secondary label color |
| textColorWeekend | '#f04040' | Weekend date text color |
| textColorWeekendTitle | '#f08080' | Weekend header text color |
| todayBarColor | '#8c00ff' | Today marker bar color |
ValueObject (value object format)
// no selection
null
// date / dateTime
{ start: "2026-07-01 10:30" }
// dateRange / dateTimeRange
{ start: "2026-07-01 10:30", end: "2026-07-15 14:00" }Public Methods
open() / close() / toggle()
picker.open(); // Opens the panel. No-op if already open.
picker.close(); // Closes the panel. No-op if already closed.
picker.toggle(); // Toggles the panel open/closed state.setValue(value)
picker.setValue({ start: "2026-06-01", end: "2026-06-15" });
// or with time
picker.setValue({ start: "2026-06-01 08:30", end: "2026-06-15 17:00" });valueis a ValueObject (see above)- Automatically validates the format; invalid dates are silently cleared
- Triggers the
onChangecallback (meta.source = 'programmatic')
⚠️ The component does not read the trigger's display text across boundaries.
setValue()accepts a ValueObject ({start, end}), not the raw string from the trigger input. The caller is responsible for parsing the trigger's display value into the correct format before passing it in.
getValue([format])
picker.getValue();
// → { start: "2026-06-01 00:00", end: "2026-06-15 23:59" } | null
picker.getValue('string');
// → same as above (default format)
picker.getValue('date');
// → { start: Date, end: Date | null } | null
picker.getValue('object');
// → { start: { year, month, day, hour, minute }, end: { ... } | null } | null| format | Return type | Description |
| --- | --- | --- |
| 'string' (default) | ValueObject \| null | YYYY-MM-DD HH:mm formatted strings. Single-day modes have no end field |
| 'date' | {start:Date, end:Date\|null} \| null | JavaScript Date objects |
| 'object' | {start:DateParts, end:DateParts\|null} \| null | Expanded numeric objects |
⚠️ Despite the
'string'format name,getValue()returns an object ({ start, end }), not a concatenated string — only its fields are strings. Build the display text yourself, e.g.${value.start} ~ ${value.end}.
DateParts:
{ year: number, month: number, day: number, hour: number, minute: number }clear([silent])
picker.clear(); // Clears the selected value and triggers onChange(null)
picker.clear(true); // Silently clears without firing the callbackonChange(callback)
picker.onChange(function(value, meta) {
// value: ValueObject | null
// meta: { source: 'user' | 'programmatic', action: 'confirmed' | 'cleared' }
});| meta field | Value | Description |
| --- | --- | --- |
| source | 'user' | Triggered by user clicks/interactions |
| source | 'programmatic' | Triggered by setValue() / clear() |
| action | 'confirmed' | Selection completed |
| action | 'cleared' | Cleared |
destroy()
picker.destroy();- Closes the panel, unbinds all event listeners, clears the callback list, and removes the DOM
- The instance cannot be used after calling this
Lifecycle callbacks
picker.onOpen(function() { /* Panel opened */ });
picker.onClose(function() { /* Panel closed */ });Multiple-Instance Mode
The component's mode is determined at construction and cannot be changed. If a single page needs to use different modes (such as date and dateRange coexisting, or one instance in Chinese and one in English), create multiple independent instances.
Multiple triggers, multiple instances
Each instance binds to its own trigger element and works independently:
const pickerA = new dtrPicker('#input-a', { mode: 'date' });
const pickerB = new dtrPicker('#input-b', { mode: 'dateRange' });Single trigger, multiple instances
A single trigger element can also be associated with multiple instances (for example, switching between different modes at the same position, or mixing Chinese and English):
const trigger = document.querySelector('#my-input');
const instances = [];
['dateTime', 'dateTimeRange'].forEach(function(mode) {
const p = new dtrPicker(trigger, { mode: mode });
instances.push(p);
});
function switchInstance(index) {
instances.forEach(function(p, i) {
if (i === index) p.open();
else p.close();
});
}When a single trigger is associated with multiple instances, the panels will be positioned overlapping at the same location. It is recommended to open only one instance at a time to avoid visual conflicts.
Build
npm run buildThe dist/ directory contains four minified artifacts:
| File | Format | Use |
| --- | --- | --- |
| dist/dtrpicker.mjs | ESM | import / modern bundlers |
| dist/dtrpicker.js | CommonJS | require() / legacy tooling |
| dist/dtrpicker.iife.js | IIFE | <script> / CDN (global dtrPicker) |
| dist/dtrpicker.d.ts | TypeScript | type declarations |
The runtime version is injected from package.json at build time, so the version label
always matches the published version.
Local Demo
node server.js
# Visit http://localhost:16800/Tech Stack
| Layer | Technology | | --- | --- | | Language | JavaScript (ES Module) | | Rendering | SVG | | Build | esbuild | | Typing | TypeScript (type declarations) | | Runtime | Browser (no Node.js dependency) |
