svelte-datetimerange-picker 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018–2026 Wojciech Maj
4
+ Copyright (c) 2026 Adhi Ravishankar
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,142 @@
1
- # Temporary Holding Version
1
+ # svelte-datetimerange-picker
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A datetime range picker for your Svelte 5 app.
4
+
5
+ - Pick days, months, years, or even decades
6
+ - Supports virtually any language
7
+ - No moment.js needed
8
+
9
+ ## tl;dr
10
+
11
+ - Install by executing `npm install svelte-datetimerange-picker`.
12
+ - Import by adding `import DateTimeRangePicker from 'svelte-datetimerange-picker'`.
13
+ - Use by adding `<DateTimeRangePicker />`. Use the `onChange` prop for getting new values.
14
+
15
+ ## Consider native alternative
16
+
17
+ If you don't need to support legacy browsers and don't need the advanced features this package provides, consider using native datetime input instead. It's more accessible, adds no extra weight to your bundle, and works better on mobile devices.
18
+
19
+ ```svelte
20
+ <input aria-label="Date and time from" max={valueTo} min={minDatetime} type="datetime-local" />
21
+ <input aria-label="Date and time to" max={maxDatetime} min={valueFrom} type="datetime-local" />
22
+ ```
23
+
24
+ ## Getting started
25
+
26
+ ### Compatibility
27
+
28
+ Your project needs to use Svelte 5 or later. The picker relies on modern web technologies (`Intl`), so it [supports only modern browsers](https://caniuse.com/#feat=internationalization).
29
+
30
+ ### Installation
31
+
32
+ Add svelte-datetimerange-picker to your project by executing `npm install svelte-datetimerange-picker`.
33
+
34
+ ### Usage
35
+
36
+ ```svelte
37
+ <script lang="ts">
38
+ import DateTimeRangePicker from 'svelte-datetimerange-picker';
39
+
40
+ type ValuePiece = Date | null;
41
+
42
+ type Value = ValuePiece | [ValuePiece, ValuePiece];
43
+
44
+ let value = $state<Value>([new Date(), new Date()]);
45
+ </script>
46
+
47
+ <DateTimeRangePicker onChange={(nextValue) => (value = nextValue)} {value} />
48
+ ```
49
+
50
+ ### Styling
51
+
52
+ Styles for the picker, the calendar and the clock are imported automatically. All elements use BEM class names starting with `svelte-datetimerange-picker`, `svelte-calendar` and `svelte-clock`, so you can build on them.
53
+
54
+ If you prefer to import the stylesheets yourself, they are available as `svelte-datetimerange-picker/dist/DateTimeRangePicker.css`, `svelte-range-calendar/dist/Calendar.css` and `svelte-analog-clock/dist/Clock.css`.
55
+
56
+ ## User guide
57
+
58
+ ### DateTimeRangePicker
59
+
60
+ Displays an input field complete with custom inputs, native input, a calendar and a clock.
61
+
62
+ #### Props
63
+
64
+ Any other props (`id`, `data-*`, `aria-*`, event handlers such as `onclick` or `ontouchstart`, …) are passed to the main `<div>` element.
65
+
66
+ | Prop name | Description | Default value | Example values |
67
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
68
+ | amPmAriaLabel | `aria-label` for the AM/PM select input. | n/a | `"Select AM/PM"` |
69
+ | autoFocus | Automatically focuses the input on mount. | n/a | `true` |
70
+ | calendarAriaLabel | `aria-label` for the calendar button. | n/a | `"Toggle calendar"` |
71
+ | calendarIcon | Content of the calendar button. Setting the value explicitly to `null` will hide the button. | (default icon) | <ul><li>String: `"Calendar"`</li><li>Snippet: `{#snippet icon()}…{/snippet}`</li></ul> |
72
+ | calendarProps | Props to pass to the Calendar component. | n/a | See [svelte-range-calendar](../svelte-range-calendar) |
73
+ | class | Class name(s) that will be added along with `"svelte-datetimerange-picker"` to the main `<div>` element. | n/a | <ul><li>String: `"class1 class2"`</li><li>Array of strings: `["class1", "class2 class3"]`</li></ul> |
74
+ | clearAriaLabel | `aria-label` for the clear button. | n/a | `"Clear value"` |
75
+ | clearIcon | Content of the clear button. Setting the value explicitly to `null` will hide the button. | (default icon) | <ul><li>String: `"Clear"`</li><li>Snippet: `{#snippet icon()}…{/snippet}`</li></ul> |
76
+ | clockProps | Props to pass to the Clock component. | n/a | See [svelte-analog-clock](../svelte-analog-clock) |
77
+ | closeWidgets | Whether to close the widgets on value selection. **Note**: It's recommended to use `shouldCloseWidgets` function instead. | `true` | `false` |
78
+ | customInputsForm | `form` attribute for the custom day, month, year, hour, minute, second, and AM/PM inputs. Set to an empty string to exclude these inputs from their nearest ancestor form. | n/a | `"my-form"` |
79
+ | dayAriaLabel | `aria-label` for the day input. | n/a | `"Day"` |
80
+ | dayPlaceholder | `placeholder` for the day input. | `"--"` | `"dd"` |
81
+ | disableCalendar | When set to `true`, will remove the calendar and the button toggling its visibility. | `false` | `true` |
82
+ | disableClock | When set to `true`, will remove the clock. | `false` | `true` |
83
+ | disabled | Whether the datetime range picker should be disabled. | `false` | `true` |
84
+ | format | Input format based on [Unicode Technical Standard #35](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table). Supported values are: `y`, `M`, `MM`, `MMM`, `MMMM`, `d`, `dd`, `H`, `HH`, `h`, `hh`, `m`, `mm`, `s`, `ss`, `a`. **Note**: When using SSR, setting this prop may help resolving hydration mismatches caused by locale differences between server and client. | n/a | `"y-MM-dd h:mm:ss a"` |
85
+ | hourAriaLabel | `aria-label` for the hour input. | n/a | `"Hour"` |
86
+ | hourPlaceholder | `placeholder` for the hour input. | `"--"` | `"hh"` |
87
+ | isCalendarOpen | Whether the calendar should be opened. | `false` | `true` |
88
+ | isClockOpen | Whether the clock should be opened. | `false` | `true` |
89
+ | locale | Locale that should be used by the datetime range picker and the calendar. Can be any [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag). | User's browser settings | `"hu-HU"` |
90
+ | maxDate | Maximum date that the user can select. Periods partially overlapped by maxDate will also be selectable, although the picker will ensure that no later date is selected. | n/a | Date: `new Date()` |
91
+ | maxDetail | The most detailed calendar view that the user shall see. Can be `"hour"`, `"minute"` or `"second"`. | `"minute"` | `"second"` |
92
+ | minDate | Minimum date that the user can select. Periods partially overlapped by minDate will also be selectable, although the picker will ensure that no earlier date is selected. | n/a | Date: `new Date()` |
93
+ | minuteAriaLabel | `aria-label` for the minute input. | n/a | `"Minute"` |
94
+ | minutePlaceholder | `placeholder` for the minute input. | `"--"` | `"mm"` |
95
+ | monthAriaLabel | `aria-label` for the month input. | n/a | `"Month"` |
96
+ | monthPlaceholder | `placeholder` for the month input. | `"--"` | `"mm"` |
97
+ | name | Input name prefix. Date from/Date to fields will be named `"yourprefix_from"` and `"yourprefix_to"` respectively. | `"datetimerange"` | `"myCustomName"` |
98
+ | nativeInputAriaLabel | `aria-label` for the native datetime input. | n/a | `"Date"` |
99
+ | onCalendarClose | Function called when the calendar closes. | n/a | `() => alert('Calendar closed')` |
100
+ | onCalendarOpen | Function called when the calendar opens. | n/a | `() => alert('Calendar opened')` |
101
+ | onChange | Function called when the user picks a valid datetime. If any of the fields were excluded using custom `format`, `new Date(y, 0, 1, 0, 0, 0)`, where `y` is the current year, is going to serve as a "base". | n/a | `(value) => alert('New date is: ', value)` |
102
+ | onClockClose | Function called when the clock closes. | n/a | `() => alert('Clock closed')` |
103
+ | onClockOpen | Function called when the clock opens. | n/a | `() => alert('Clock opened')` |
104
+ | onFocus | Function called when the user focuses an input. | n/a | `(event) => alert('Focused input: ', event.target.name)` |
105
+ | onInvalidChange | Function called when the user picks an invalid datetime. | n/a | `() => alert('Invalid datetime')` |
106
+ | openWidgetsOnFocus | Whether to open the widgets on input focus. **Note**: It's recommended to use `shouldOpenWidgets` function instead. | `true` | `false` |
107
+ | portalContainer | Element to render the widgets in. | n/a | `document.getElementById('my-div')` |
108
+ | rangeDivider | Divider between datetime inputs. | `"–"` | <ul><li>String: `" to "`</li><li>Snippet</li></ul> |
109
+ | required | Whether datetime input should be required. | `false` | `true` |
110
+ | secondAriaLabel | `aria-label` for the second input. | n/a | `"Second"` |
111
+ | secondPlaceholder | `placeholder` for the second input. | `"--"` | `"ss"` |
112
+ | shouldCloseWidgets | Function called before the widgets close. `reason` can be `"buttonClick"`, `"escape"`, `"outsideAction"`, or `"select"`. `widget` can be `"calendar"` or `"clock"`. If it returns `false`, the widget will not close. | n/a | `({ reason, widget }) => reason !== 'outsideAction' && widget === 'calendar'` |
113
+ | shouldOpenWidgets | Function called before the widgets open. `reason` can be `"buttonClick"` or `"focus"`. `widget` can be `"calendar"` or `"clock"`. If it returns `false`, the widget will not open. | n/a | `({ reason, widget }) => reason !== 'focus' && widget === 'calendar'` |
114
+ | showLeadingZeros | Whether leading zeros should be rendered in datetime inputs. | `false` | `true` |
115
+ | value | Input value. | n/a | <ul><li>Date: `new Date(2017, 0, 1, 22, 15)`</li><li>String: `"2017-01-01T22:15:00"`</li><li>An array of dates: `[new Date(2017, 0, 1, 22, 15), new Date(2017, 0, 1, 23, 45)]`</li><li>An array of strings: `["2017-01-01T22:15:00", "2017-01-01T23:45:00"]`</li></ul> |
116
+ | yearAriaLabel | `aria-label` for the year input. | n/a | `"Year"` |
117
+ | yearPlaceholder | `placeholder` for the year input. | `"----"` | `"yyyy"` |
118
+
119
+ ### Migrating from React-DateTimeRange-Picker
120
+
121
+ - `className` → `class`; `calendarProps.className` → `calendarProps.class`; `clockProps.className` → `clockProps.class`.
122
+ - React nodes and components in `calendarIcon`, `clearIcon` and `rangeDivider` → strings or snippets.
123
+ - Event props are the lowercase DOM ones (`onclick`, `ontouchstart`, …), except `onFocus`, which is called on bubbling focus events like in React.
124
+ - `data-testid` and `id` are passed through as regular attributes.
125
+ - Stylesheets are imported automatically, and CSS classes use the `svelte-` prefix instead of `react-`.
126
+
127
+ ## Related packages
128
+
129
+ The picker is built from these packages, which you can also use on their own:
130
+
131
+ - [svelte-datetime-input](https://github.com/adhiravishankar/svelte-datetime-pickers/tree/main/packages/svelte-datetime-input): segmented datetime input fields.
132
+ - [svelte-time-input](https://github.com/adhiravishankar/svelte-datetime-pickers/tree/main/packages/svelte-time-input): segmented time input fields.
133
+ - [svelte-date-input](https://github.com/adhiravishankar/svelte-datetime-pickers/tree/main/packages/svelte-date-input): segmented date input fields and the shared engine.
134
+ - [svelte-range-calendar](https://github.com/adhiravishankar/svelte-datetime-pickers/tree/main/packages/svelte-range-calendar): calendar with range selection.
135
+ - [svelte-analog-clock](https://github.com/adhiravishankar/svelte-datetime-pickers/tree/main/packages/svelte-analog-clock): analog clock.
136
+ - [svelte-fit-action](https://github.com/adhiravishankar/svelte-datetime-pickers/tree/main/packages/svelte-fit-action): `fit` and `portal` actions.
137
+
138
+ See the [live demo and Storybook](https://adhiravishankar.github.io/svelte-datetime-pickers/).
139
+
140
+ ## License
141
+
142
+ The MIT License.
@@ -0,0 +1,133 @@
1
+ .svelte-datetimerange-picker {
2
+ display: inline-flex;
3
+ position: relative;
4
+ }
5
+
6
+ .svelte-datetimerange-picker,
7
+ .svelte-datetimerange-picker *,
8
+ .svelte-datetimerange-picker *:before,
9
+ .svelte-datetimerange-picker *:after {
10
+ -moz-box-sizing: border-box;
11
+ -webkit-box-sizing: border-box;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ .svelte-datetimerange-picker--disabled {
16
+ background-color: #f0f0f0;
17
+ color: #6d6d6d;
18
+ }
19
+
20
+ .svelte-datetimerange-picker__wrapper {
21
+ display: flex;
22
+ flex-grow: 1;
23
+ flex-shrink: 0;
24
+ align-items: center;
25
+ border: thin solid gray;
26
+ }
27
+
28
+ .svelte-datetimerange-picker__inputGroup {
29
+ min-width: calc(4px + (4px * 3) + 0.54em * 6 + 0.217em * 2);
30
+ height: 100%;
31
+ flex-grow: 1;
32
+ padding: 0 2px;
33
+ }
34
+
35
+ .svelte-datetimerange-picker__inputGroup__divider {
36
+ padding: 1px 0;
37
+ }
38
+
39
+ .svelte-datetimerange-picker__inputGroup__divider,
40
+ .svelte-datetimerange-picker__inputGroup__leadingZero {
41
+ display: inline-block;
42
+ font: inherit;
43
+ }
44
+
45
+ .svelte-datetimerange-picker__inputGroup__input {
46
+ min-width: 0.54em;
47
+ height: calc(100% - 2px);
48
+ position: relative;
49
+ padding: 1px;
50
+ border: 0;
51
+ background: none;
52
+ color: currentColor;
53
+ font: inherit;
54
+ box-sizing: content-box;
55
+ -webkit-appearance: textfield;
56
+ -moz-appearance: textfield;
57
+ appearance: textfield;
58
+ }
59
+
60
+ .svelte-datetimerange-picker__inputGroup__input::-webkit-outer-spin-button,
61
+ .svelte-datetimerange-picker__inputGroup__input::-webkit-inner-spin-button {
62
+ -webkit-appearance: none;
63
+ -moz-appearance: none;
64
+ appearance: none;
65
+ margin: 0;
66
+ }
67
+
68
+ .svelte-datetimerange-picker__inputGroup__input:invalid {
69
+ background: rgba(255, 0, 0, 0.1);
70
+ }
71
+
72
+ .svelte-datetimerange-picker__inputGroup__input--hasLeadingZero {
73
+ margin-left: -0.54em;
74
+ padding-left: calc(1px + 0.54em);
75
+ }
76
+
77
+ .svelte-datetimerange-picker__inputGroup__amPm {
78
+ font: inherit;
79
+ -webkit-appearance: menulist;
80
+ -moz-appearance: menulist;
81
+ appearance: menulist;
82
+ }
83
+
84
+ .svelte-datetimerange-picker__button {
85
+ border: 0;
86
+ background: transparent;
87
+ padding: 4px 6px;
88
+ }
89
+
90
+ .svelte-datetimerange-picker__button:enabled {
91
+ cursor: pointer;
92
+ }
93
+
94
+ .svelte-datetimerange-picker__button:enabled:hover .svelte-datetimerange-picker__button__icon,
95
+ .svelte-datetimerange-picker__button:enabled:focus .svelte-datetimerange-picker__button__icon {
96
+ stroke: #0078d7;
97
+ }
98
+
99
+ .svelte-datetimerange-picker__button:disabled .svelte-datetimerange-picker__button__icon {
100
+ stroke: #6d6d6d;
101
+ }
102
+
103
+ .svelte-datetimerange-picker__button svg {
104
+ display: inherit;
105
+ }
106
+
107
+ .svelte-datetimerange-picker__calendar,
108
+ .svelte-datetimerange-picker__clock {
109
+ z-index: 1;
110
+ }
111
+
112
+ .svelte-datetimerange-picker__calendar--closed,
113
+ .svelte-datetimerange-picker__clock--closed {
114
+ display: none;
115
+ }
116
+
117
+ .svelte-datetimerange-picker__calendar {
118
+ width: 350px;
119
+ max-width: 100vw;
120
+ }
121
+
122
+ .svelte-datetimerange-picker__calendar .svelte-calendar {
123
+ border-width: thin;
124
+ }
125
+
126
+ .svelte-datetimerange-picker__clock {
127
+ width: 200px;
128
+ height: 200px;
129
+ max-width: 100vw;
130
+ padding: 25px;
131
+ background-color: white;
132
+ border: thin solid #a0a096;
133
+ }