English | 简体中文
A streaming date/time range picker component rendered with SVG, focused on being lightweight, fluid, and customizable.
- 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
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();No build step needed — drop the IIFE bundle in and use the global dtrPicker:
<script src="https://unpkg.com/dtrpicker@2.2.2/dist/dtrpicker.iife.js"></script>
<script>
const picker = new dtrPicker('#my-input', {
mode: 'dateRange',
});
</script>Also available on jsDelivr.
import dtrPicker from 'dtrpicker';const dtrPicker = require('dtrpicker').default;<script src="dist/dtrpicker.iife.js"></script>
<script>
const picker = new dtrPicker('#my-input', { mode: 'dateRange' });
</script>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 |
// 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" }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.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.
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 }picker.clear(); // Clears the selected value and triggers onChange(null)
picker.clear(true); // Silently clears without firing the callbackpicker.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 |
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
picker.onOpen(function() { /* Panel opened */ });
picker.onClose(function() { /* Panel closed */ });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.
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' });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.
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.
node server.js
# Visit http://localhost:16800/| Layer | Technology |
|---|---|
| Language | JavaScript (ES Module) |
| Rendering | SVG |
| Build | esbuild |
| Typing | TypeScript (type declarations) |
| Runtime | Browser (no Node.js dependency) |
