@octane-xplat/date-picker 0.0.0-nightly.g14544fe

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,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Alec Larson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # `@octane-xplat/date-picker`
2
+
3
+ Platform-specific date and time pickers for Octane xplat apps: a SwiftUI
4
+ `DatePicker` on iOS, Material 3 `DatePicker`/`TimePicker` built with Jetpack
5
+ Compose on Android, and a real `NSDatePicker` on macOS. For portable form
6
+ controls that work on every target, use `Calendar`, `DateInput`,
7
+ `TimeInput`, `DateTimeInput`, and `DateRangeInput` from `@octane-xplat/ui`.
8
+
9
+ ```tsx
10
+ import { useState } from 'octane'
11
+ import { DateInput } from '@octane-xplat/ui'
12
+
13
+ export function DepartureDate() {
14
+ const [date, setDate] = useState<string | null>('2026-10-02')
15
+ return <DateInput value={date} onChange={setDate} label="Departure" />
16
+ }
17
+ ```
18
+
19
+ There is no shared picker component. Install the package in the app that
20
+ renders a control, then import `SwiftUIDatePicker` from
21
+ `@octane-xplat/date-picker/ios`, `MaterialDatePicker` from
22
+ `@octane-xplat/date-picker/android`, or `AppKitDatePicker` from
23
+ `@octane-xplat/date-picker/macos` in the matching platform-suffixed file.
24
+ These native entries have platform-specific contracts.
25
+ The old `@octane-xplat/date-picker/web` `DateInput` subpath was removed because
26
+ its browser-only string contract conflicted with the shared `DateInput` API.
27
+
28
+ ```tsx
29
+ /** @jsxImportSource @nativescript-community/octane */
30
+ // SwiftUIDatePickerExample.ios.tsx
31
+ import { SwiftUIDatePicker } from '@octane-xplat/date-picker/ios'
32
+
33
+ export function Picker() {
34
+ return (
35
+ <SwiftUIDatePicker
36
+ defaultSelection={new Date(2026, 9, 2)}
37
+ displayedComponents={['date']}
38
+ pickerStyle="graphical"
39
+ onSelectionChange={console.log}
40
+ />
41
+ )
42
+ }
43
+ ```
44
+
45
+ ```tsx
46
+ /** @jsxImportSource @nativescript-community/octane */
47
+ // MaterialDatePickerExample.android.tsx
48
+ import { MaterialDatePicker } from '@octane-xplat/date-picker/android'
49
+
50
+ const initialDate = new Date(2026, 9, 2)
51
+ const bounds = { start: new Date(2026, 0, 1), end: new Date(2026, 11, 31) }
52
+
53
+ export function Picker() {
54
+ return (
55
+ <MaterialDatePicker
56
+ initialDate={initialDate}
57
+ selectableDates={bounds}
58
+ onDateSelected={console.log}
59
+ />
60
+ )
61
+ }
62
+ ```
63
+
64
+ ```tsx
65
+ // AppKitDatePickerExample.macos.tsx
66
+ import { AppKitDatePicker } from '@octane-xplat/date-picker/macos'
67
+
68
+ export function Picker() {
69
+ return (
70
+ <AppKitDatePicker
71
+ components="date"
72
+ pickerStyle="graphical"
73
+ defaultSelection={new Date(2026, 9, 2)}
74
+ onSelectionChange={console.log}
75
+ />
76
+ )
77
+ }
78
+ ```
79
+
80
+ The platform APIs use their own selection vocabulary: the iOS entry takes a
81
+ controlled `selection`/`defaultSelection` `Date` with `displayedComponents`
82
+ and `pickerStyle`; the Android entry follows the uncontrolled
83
+ `initialDate`/`onDateSelected` model of the Material 3 pickers with
84
+ `selectableDates` range bounds; the macOS entry takes
85
+ `components`/`pickerStyle` (`'graphical'` embeds the
86
+ inline calendar) with a controlled `selection` `Date`.
87
+
88
+ ```tsx
89
+ // Departure.macos.tsx
90
+ import { useState } from 'octane'
91
+ import { AppKitDatePicker } from '@octane-xplat/date-picker/macos'
92
+
93
+ export function Departure() {
94
+ const [selection, setSelection] = useState(new Date(2026, 9, 2))
95
+ return (
96
+ <AppKitDatePicker
97
+ components="date"
98
+ pickerStyle="graphical"
99
+ selection={selection}
100
+ onSelectionChange={setSelection}
101
+ />
102
+ )
103
+ }
104
+ ```
105
+
106
+ Android notes: give `MaterialDatePicker` a bounded height (the M3 calendar's
107
+ lazy grid collapses under unbounded height), and keep `initialDate` and
108
+ `selectableDates` identity-stable — the provider keys its state `remember`
109
+ on them, so a per-render `new Date()` resets the selection.
110
+ See the framework guide for details and maintained examples:
111
+ [`docs/platform/date-picker.md`](../../docs/platform/date-picker.md).
112
+
113
+ ```tsx
114
+ /** @jsxImportSource @nativescript-community/octane */
115
+ // Departure.android.tsx — reuse these objects while the picker is mounted.
116
+ import { MaterialDatePicker } from '@octane-xplat/date-picker/android'
117
+
118
+ const initialDate = new Date(2026, 9, 2)
119
+ const selectableDates = { start: new Date(2026, 0, 1) }
120
+ export function Departure() {
121
+ return (
122
+ <MaterialDatePicker
123
+ className="departure-calendar"
124
+ initialDate={initialDate}
125
+ selectableDates={selectableDates}
126
+ onDateSelected={console.log}
127
+ />
128
+ )
129
+ }
130
+ ```
131
+
132
+ ```css
133
+ /* App stylesheet: give the Android calendar room for its grid. */
134
+ .departure-calendar {
135
+ height: 480px;
136
+ }
137
+ ```
138
+
139
+ The native implementations are adapted from `@expo/ui` (MIT,
140
+ `packages/expo-ui` sdk-57): `ios/DatePickerView.swift` and
141
+ `android/.../ui/DatePickerView.kt`. Attribution headers sit on the ported
142
+ files.
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@octane-xplat/date-picker",
3
+ "version": "0.0.0-nightly.g14544fe",
4
+ "description": "Platform-specific date and time selection controls for Octane xplat",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/octane-xplat/octane-xplat",
8
+ "directory": "packages/date-picker"
9
+ },
10
+ "files": [
11
+ "types",
12
+ "src",
13
+ "platforms",
14
+ "!src/**/*.test.*",
15
+ "!src/**/*.spec.*"
16
+ ],
17
+ "type": "module",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./types/index.d.ts",
21
+ "web": "./src/index.ts",
22
+ "native": "./src/index.ts",
23
+ "default": "./src/index.ts"
24
+ },
25
+ "./ios": {
26
+ "types": "./types/ios/index.d.ts",
27
+ "native": "./src/ios/index.ts"
28
+ },
29
+ "./android": {
30
+ "types": "./types/android/index.d.ts",
31
+ "native": "./src/android/index.ts"
32
+ },
33
+ "./macos": {
34
+ "types": "./types/macos/index.d.ts",
35
+ "macos": "./src/macos/index.ts"
36
+ }
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "@nativescript/jetpack-compose": "1.0.0-beta.0",
43
+ "@nativescript/swift-ui": "4.0.2"
44
+ },
45
+ "devDependencies": {
46
+ "@nativescript-community/octane": "0.2.5",
47
+ "@nativescript/core": "9.1.3",
48
+ "@octane-xplat/macos-renderer": "0.0.0-nightly.g14544fe",
49
+ "@octanejs/vite-plugin": "0.1.61",
50
+ "octane": "0.11.0",
51
+ "tsrx-typegen": "0.3.0",
52
+ "typescript": "5.9.3",
53
+ "vite": "8.3.0"
54
+ },
55
+ "peerDependencies": {
56
+ "@nativescript-community/octane": ">=0.2.1 <1",
57
+ "@nativescript/core": ">=9.1.0 <10",
58
+ "octane": ">=0.1.51 <1"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@nativescript-community/octane": {
62
+ "optional": true
63
+ },
64
+ "@nativescript/core": {
65
+ "optional": true
66
+ },
67
+ "octane": {
68
+ "optional": true
69
+ }
70
+ },
71
+ "nativescript": {
72
+ "platforms": {
73
+ "android": "9.1.0",
74
+ "ios": "9.1.0"
75
+ }
76
+ },
77
+ "scripts": {
78
+ "test:packed": "node tests/packed-consumer.mjs",
79
+ "pack:check": "tsrx-typegen --pack-check",
80
+ "typegen": "tsc -p tsconfig.types.json"
81
+ }
82
+ }
@@ -0,0 +1,15 @@
1
+ apply plugin: 'org.jetbrains.kotlin.plugin.compose'
2
+
3
+ android {
4
+ buildFeatures {
5
+ compose true
6
+ }
7
+ kotlinOptions {
8
+ jvmTarget = '17'
9
+ }
10
+ }
11
+
12
+ dependencies {
13
+ implementation platform('androidx.compose:compose-bom:2024.12.01')
14
+ implementation 'androidx.compose.material3:material3'
15
+ }
@@ -0,0 +1,348 @@
1
+ // Adapted from @expo/ui (MIT) — Copyright 2025-present 650 Industries.
2
+ // Upstream: expo/packages/expo-ui/android/.../ui/DatePickerView.kt (sdk-57).
3
+ // The ExpoModules glue (@OptimizedRecord/@OptimizedComposeProps records,
4
+ // ModifierRegistry, AsyncFunctionHandle) is replaced by the
5
+ // @nativescript/jetpack-compose provider contract: updateData(Map) + onEvent.
6
+ // Colors arrive as CSS hex strings instead of expo ColorValue ints. The
7
+ // dialog wrappers (DatePickerDialog/TimePickerDialog) are not ported; the
8
+ // inline DatePicker/TimePicker composables cover this leaf's contract.
9
+
10
+ package com.octanexplat.datepicker
11
+
12
+ import androidx.compose.material3.DatePicker
13
+ import androidx.compose.material3.DatePickerDefaults
14
+ import androidx.compose.material3.DatePickerState
15
+ import androidx.compose.material3.DisplayMode
16
+ import androidx.compose.material3.ExperimentalMaterial3Api
17
+ import androidx.compose.material3.LocalContentColor
18
+ import androidx.compose.material3.MaterialTheme
19
+ import androidx.compose.material3.SelectableDates
20
+ import androidx.compose.material3.TimePicker
21
+ import androidx.compose.material3.TimePickerDefaults
22
+ import androidx.compose.material3.TimePickerLayoutType
23
+ import androidx.compose.material3.TimePickerState
24
+ import androidx.compose.material3.darkColorScheme
25
+ import androidx.compose.material3.lightColorScheme
26
+ import androidx.compose.runtime.Composable
27
+ import androidx.compose.runtime.CompositionLocalProvider
28
+ import androidx.compose.runtime.LaunchedEffect
29
+ import androidx.compose.runtime.getValue
30
+ import androidx.compose.runtime.mutableStateOf
31
+ import androidx.compose.runtime.remember
32
+ import androidx.compose.runtime.setValue
33
+ import androidx.compose.ui.Modifier
34
+ import androidx.compose.ui.graphics.Color
35
+ import androidx.compose.ui.platform.ComposeView
36
+ import androidx.compose.ui.platform.LocalConfiguration
37
+ import androidx.compose.foundation.isSystemInDarkTheme
38
+ import org.json.JSONObject
39
+ import java.util.Calendar
40
+ import java.util.Date
41
+
42
+ private fun Any?.asBoolean(): Boolean = when (this) {
43
+ is Boolean -> this
44
+ is Number -> toInt() != 0
45
+ else -> false
46
+ }
47
+
48
+ private fun Any?.asLong(): Long? = when (this) {
49
+ is Number -> toLong()
50
+ else -> null
51
+ }
52
+
53
+ // android.graphics.Color.parseColor only accepts #RRGGBB/#AARRGGBB (and
54
+ // named colors); the elementColors doc also advertises the CSS-style #rgb
55
+ // shorthand — expand it so a documented value doesn't silently fall back.
56
+ private fun expandShortHex(value: String): String =
57
+ if (value.length == 4 && value.startsWith('#')) {
58
+ "#" + value.substring(1).map { "$it$it" }.joinToString("")
59
+ } else {
60
+ value
61
+ }
62
+
63
+ private fun Any?.asColor(): Color? = when (this) {
64
+ is String -> runCatching { Color(android.graphics.Color.parseColor(expandShortHex(this))) }.getOrNull()
65
+ else -> null
66
+ }
67
+
68
+ private data class SelectableRange(
69
+ val start: Long? = null,
70
+ val end: Long? = null,
71
+ )
72
+
73
+ private data class DatePickerUiState(
74
+ val initialDate: Long? = null,
75
+ val variant: String = "picker",
76
+ val displayedComponents: String = "date",
77
+ val showVariantToggle: Boolean = true,
78
+ val is24Hour: Boolean = true,
79
+ val color: Color? = null,
80
+ val elementColors: Map<*, *> = emptyMap<Any?, Any?>(),
81
+ val selectableDates: SelectableRange = SelectableRange(),
82
+ )
83
+
84
+ private fun toUtcDayMillis(localMillis: Long): Long {
85
+ val cal = Calendar.getInstance()
86
+ cal.timeInMillis = localMillis
87
+ val utcCal = Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))
88
+ utcCal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0)
89
+ utcCal.set(Calendar.MILLISECOND, 0)
90
+ return utcCal.timeInMillis
91
+ }
92
+
93
+ // Material3 stores selectedDateMillis as UTC-midnight epoch; emitting it raw
94
+ // makes `new Date(ms)` resolve to the previous day in negative-UTC zones.
95
+ // Convert back to local-midnight millis so the JS-side Date lands on the
96
+ // calendar day the user picked.
97
+ private fun toLocalDayMillis(utcMillis: Long): Long {
98
+ val utcCal = Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))
99
+ utcCal.timeInMillis = utcMillis
100
+ val localCal = Calendar.getInstance()
101
+ localCal.clear()
102
+ localCal.set(utcCal.get(Calendar.YEAR), utcCal.get(Calendar.MONTH), utcCal.get(Calendar.DAY_OF_MONTH))
103
+ return localCal.timeInMillis
104
+ }
105
+
106
+ @OptIn(ExperimentalMaterial3Api::class)
107
+ @Composable
108
+ private fun rememberSelectableDates(range: SelectableRange): SelectableDates {
109
+ val start = range.start
110
+ val end = range.end
111
+ return remember(start, end) {
112
+ if (start != null || end != null) {
113
+ val startUtcDayMillis = start?.let { toUtcDayMillis(it) }
114
+ val endUtcDayMillis = end?.let { toUtcDayMillis(it) }
115
+ val startYear = start?.let {
116
+ val cal = Calendar.getInstance()
117
+ cal.timeInMillis = it
118
+ cal.get(Calendar.YEAR)
119
+ }
120
+ val endYear = end?.let {
121
+ val cal = Calendar.getInstance()
122
+ cal.timeInMillis = it
123
+ cal.get(Calendar.YEAR)
124
+ }
125
+ object : SelectableDates {
126
+ override fun isSelectableDate(utcTimeMillis: Long): Boolean {
127
+ if (startUtcDayMillis != null && utcTimeMillis < startUtcDayMillis) return false
128
+ if (endUtcDayMillis != null && utcTimeMillis > endUtcDayMillis) return false
129
+ return true
130
+ }
131
+
132
+ override fun isSelectableYear(year: Int): Boolean {
133
+ if (startYear != null && year < startYear) return false
134
+ if (endYear != null && year > endYear) return false
135
+ return true
136
+ }
137
+ }
138
+ } else {
139
+ DatePickerDefaults.AllDates
140
+ }
141
+ }
142
+ }
143
+
144
+ // `selectableDates` only greys out unselectable cells; the calendar's actual
145
+ // extent (months list, year grid, range content description) comes from
146
+ // `yearRange`. Derive it from min/max so the picker reflects the constraints
147
+ // instead of always spanning the 1900..2100 default.
148
+ // https://github.com/expo/expo/issues/47206
149
+ @OptIn(ExperimentalMaterial3Api::class)
150
+ @Composable
151
+ private fun rememberDatePickerYearRange(range: SelectableRange, initialDateMillis: Long): IntRange {
152
+ val start = range.start
153
+ val end = range.end
154
+ return remember(start, end, initialDateMillis) {
155
+ val defaults = DatePickerDefaults.YearRange
156
+ val yearOf = { millis: Long ->
157
+ Calendar.getInstance().apply { timeInMillis = millis }.get(Calendar.YEAR)
158
+ }
159
+ val startYear = start?.let(yearOf) ?: defaults.first
160
+ val endYear = end?.let(yearOf) ?: defaults.last
161
+ val initialYear = yearOf(initialDateMillis)
162
+ minOf(startYear, initialYear)..maxOf(endYear, initialYear)
163
+ }
164
+ }
165
+
166
+ private fun Map<*, *>.color(name: String): Color? = this[name].asColor()
167
+
168
+ @OptIn(ExperimentalMaterial3Api::class)
169
+ @Composable
170
+ private fun buildDatePickerColors(
171
+ elementColors: Map<*, *>,
172
+ colorProp: Color?,
173
+ ): androidx.compose.material3.DatePickerColors {
174
+ val defaults = DatePickerDefaults.colors()
175
+ return defaults.copy(
176
+ containerColor = elementColors.color("containerColor") ?: defaults.containerColor,
177
+ titleContentColor = elementColors.color("titleContentColor") ?: colorProp ?: defaults.titleContentColor,
178
+ headlineContentColor = elementColors.color("headlineContentColor") ?: colorProp ?: defaults.headlineContentColor,
179
+ weekdayContentColor = elementColors.color("weekdayContentColor") ?: defaults.weekdayContentColor,
180
+ subheadContentColor = elementColors.color("subheadContentColor") ?: defaults.subheadContentColor,
181
+ navigationContentColor = elementColors.color("navigationContentColor") ?: defaults.navigationContentColor,
182
+ yearContentColor = elementColors.color("yearContentColor") ?: defaults.yearContentColor,
183
+ disabledYearContentColor = elementColors.color("disabledYearContentColor") ?: defaults.disabledYearContentColor,
184
+ currentYearContentColor = elementColors.color("currentYearContentColor") ?: defaults.currentYearContentColor,
185
+ selectedYearContentColor = elementColors.color("selectedYearContentColor") ?: defaults.selectedYearContentColor,
186
+ disabledSelectedYearContentColor = elementColors.color("disabledSelectedYearContentColor") ?: defaults.disabledSelectedYearContentColor,
187
+ selectedYearContainerColor = elementColors.color("selectedYearContainerColor") ?: defaults.selectedYearContainerColor,
188
+ disabledSelectedYearContainerColor = elementColors.color("disabledSelectedYearContainerColor") ?: defaults.disabledSelectedYearContainerColor,
189
+ dayContentColor = elementColors.color("dayContentColor") ?: defaults.dayContentColor,
190
+ disabledDayContentColor = elementColors.color("disabledDayContentColor") ?: defaults.disabledDayContentColor,
191
+ selectedDayContentColor = elementColors.color("selectedDayContentColor") ?: defaults.selectedDayContentColor,
192
+ disabledSelectedDayContentColor = elementColors.color("disabledSelectedDayContentColor") ?: defaults.disabledSelectedDayContentColor,
193
+ selectedDayContainerColor = elementColors.color("selectedDayContainerColor") ?: colorProp ?: defaults.selectedDayContainerColor,
194
+ disabledSelectedDayContainerColor = elementColors.color("disabledSelectedDayContainerColor") ?: defaults.disabledSelectedDayContainerColor,
195
+ todayContentColor = elementColors.color("todayContentColor") ?: defaults.todayContentColor,
196
+ todayDateBorderColor = elementColors.color("todayDateBorderColor") ?: colorProp ?: defaults.todayDateBorderColor,
197
+ dayInSelectionRangeContentColor = elementColors.color("dayInSelectionRangeContentColor") ?: defaults.dayInSelectionRangeContentColor,
198
+ dayInSelectionRangeContainerColor = elementColors.color("dayInSelectionRangeContainerColor") ?: defaults.dayInSelectionRangeContainerColor,
199
+ dividerColor = elementColors.color("dividerColor") ?: defaults.dividerColor,
200
+ )
201
+ }
202
+
203
+ @OptIn(ExperimentalMaterial3Api::class)
204
+ @Composable
205
+ private fun buildTimePickerColors(
206
+ elementColors: Map<*, *>,
207
+ colorProp: Color?,
208
+ ): androidx.compose.material3.TimePickerColors {
209
+ val defaults = TimePickerDefaults.colors()
210
+ return defaults.copy(
211
+ containerColor = elementColors.color("containerColor") ?: defaults.containerColor,
212
+ clockDialColor = elementColors.color("clockDialColor") ?: colorProp?.copy(alpha = 0.3f) ?: defaults.clockDialColor,
213
+ clockDialSelectedContentColor = elementColors.color("clockDialSelectedContentColor") ?: defaults.clockDialSelectedContentColor,
214
+ clockDialUnselectedContentColor = elementColors.color("clockDialUnselectedContentColor") ?: defaults.clockDialUnselectedContentColor,
215
+ selectorColor = elementColors.color("selectorColor") ?: colorProp ?: defaults.selectorColor,
216
+ periodSelectorBorderColor = elementColors.color("periodSelectorBorderColor") ?: defaults.periodSelectorBorderColor,
217
+ periodSelectorSelectedContainerColor = elementColors.color("periodSelectorSelectedContainerColor") ?: defaults.periodSelectorSelectedContainerColor,
218
+ periodSelectorUnselectedContainerColor = elementColors.color("periodSelectorUnselectedContainerColor") ?: defaults.periodSelectorUnselectedContainerColor,
219
+ periodSelectorSelectedContentColor = elementColors.color("periodSelectorSelectedContentColor") ?: defaults.periodSelectorSelectedContentColor,
220
+ periodSelectorUnselectedContentColor = elementColors.color("periodSelectorUnselectedContentColor") ?: defaults.periodSelectorUnselectedContentColor,
221
+ timeSelectorSelectedContainerColor = elementColors.color("timeSelectorSelectedContainerColor") ?: colorProp ?: defaults.timeSelectorSelectedContainerColor,
222
+ timeSelectorUnselectedContainerColor = elementColors.color("timeSelectorUnselectedContainerColor") ?: defaults.timeSelectorUnselectedContainerColor,
223
+ timeSelectorSelectedContentColor = elementColors.color("timeSelectorSelectedContentColor") ?: defaults.timeSelectorSelectedContentColor,
224
+ timeSelectorUnselectedContentColor = elementColors.color("timeSelectorUnselectedContentColor") ?: defaults.timeSelectorUnselectedContentColor,
225
+ )
226
+ }
227
+
228
+ @OptIn(ExperimentalMaterial3Api::class)
229
+ @Composable
230
+ private fun ExpoDatePicker(modifier: Modifier = Modifier, state: DatePickerUiState, onDateSelected: (Long?) -> Unit) {
231
+ val locale = LocalConfiguration.current.locales[0]
232
+ val displayMode = if (state.variant == "input") DisplayMode.Input else DisplayMode.Picker
233
+ val fallbackDate = remember { Date().time }
234
+ val initialDate = state.initialDate ?: fallbackDate
235
+ val selectableDates = rememberSelectableDates(state.selectableDates)
236
+ val yearRange = rememberDatePickerYearRange(state.selectableDates, initialDate)
237
+
238
+ val pickerState = remember(displayMode, initialDate, selectableDates, yearRange) {
239
+ DatePickerState(
240
+ initialDisplayMode = displayMode,
241
+ locale = locale,
242
+ initialSelectedDateMillis = state.initialDate,
243
+ initialDisplayedMonthMillis = initialDate,
244
+ yearRange = yearRange,
245
+ selectableDates = selectableDates,
246
+ )
247
+ }
248
+
249
+ LaunchedEffect(pickerState.selectedDateMillis) {
250
+ onDateSelected(pickerState.selectedDateMillis)
251
+ }
252
+
253
+ val colors = buildDatePickerColors(state.elementColors, state.color)
254
+
255
+ // Material3's year-selector chevron tints from the ambient
256
+ // LocalContentColor (which defaults to black), not
257
+ // `navigationContentColor`; bind the local so the chevron honors the
258
+ // navigation color.
259
+ CompositionLocalProvider(LocalContentColor provides colors.navigationContentColor) {
260
+ DatePicker(
261
+ modifier = modifier,
262
+ state = pickerState,
263
+ showModeToggle = state.showVariantToggle,
264
+ colors = colors,
265
+ )
266
+ }
267
+ }
268
+
269
+ @OptIn(ExperimentalMaterial3Api::class)
270
+ @Composable
271
+ private fun ExpoTimePicker(modifier: Modifier = Modifier, state: DatePickerUiState, onDateSelected: (Long?) -> Unit) {
272
+ val initialDate = state.initialDate
273
+
274
+ val pickerState = remember(initialDate, state.is24Hour) {
275
+ val cal = Calendar.getInstance()
276
+ cal.isLenient = false
277
+ if (initialDate != null) {
278
+ cal.timeInMillis = initialDate
279
+ }
280
+ TimePickerState(
281
+ initialHour = cal.get(Calendar.HOUR_OF_DAY),
282
+ initialMinute = cal.get(Calendar.MINUTE),
283
+ is24Hour = state.is24Hour,
284
+ )
285
+ }
286
+
287
+ LaunchedEffect(pickerState.hour, pickerState.minute) {
288
+ val cal = Calendar.getInstance()
289
+ cal.isLenient = false
290
+ if (initialDate != null) {
291
+ cal.timeInMillis = initialDate
292
+ }
293
+ cal.set(Calendar.HOUR_OF_DAY, pickerState.hour)
294
+ cal.set(Calendar.MINUTE, pickerState.minute)
295
+ onDateSelected(cal.time.time)
296
+ }
297
+
298
+ TimePicker(
299
+ modifier = modifier,
300
+ state = pickerState,
301
+ layoutType = TimePickerLayoutType.Vertical,
302
+ colors = buildTimePickerColors(state.elementColors, state.color),
303
+ )
304
+ }
305
+
306
+ class XplatDatePickerProvider {
307
+ private var state by mutableStateOf(DatePickerUiState())
308
+ var onEvent: ((Any) -> Unit)? = null
309
+
310
+ fun generateComposeView(view: ComposeView) {
311
+ view.setContent {
312
+ MaterialTheme {
313
+ if (state.displayedComponents == "hourAndMinute") {
314
+ ExpoTimePicker(state = state) { millis ->
315
+ onEvent?.invoke(JSONObject().put("date", millis ?: JSONObject.NULL))
316
+ }
317
+ } else {
318
+ ExpoDatePicker(state = state) { millis ->
319
+ onEvent?.invoke(
320
+ JSONObject().put(
321
+ "date",
322
+ millis?.let { toLocalDayMillis(it) } ?: JSONObject.NULL,
323
+ ),
324
+ )
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ fun updateData(input: Map<Any, Any>) {
332
+ val data = input["data"] as? Map<*, *> ?: input
333
+ val range = data["selectableDates"] as? Map<*, *>
334
+ state = DatePickerUiState(
335
+ initialDate = data["initialDate"].asLong(),
336
+ variant = data["variant"] as? String ?: "picker",
337
+ displayedComponents = data["displayedComponents"] as? String ?: "date",
338
+ showVariantToggle = data["showVariantToggle"]?.asBoolean() ?: true,
339
+ is24Hour = data["is24Hour"]?.asBoolean() ?: true,
340
+ color = data["color"].asColor(),
341
+ elementColors = data["elementColors"] as? Map<*, *> ?: emptyMap<Any?, Any?>(),
342
+ selectableDates = SelectableRange(
343
+ start = range?.get("start").asLong(),
344
+ end = range?.get("end").asLong(),
345
+ ),
346
+ )
347
+ }
348
+ }
@@ -0,0 +1,148 @@
1
+ // Adapted from @expo/ui (MIT) — Copyright 2025-present 650 Industries.
2
+ // Upstream: expo/packages/expo-ui/ios/DatePickerView.swift (sdk-57).
3
+ // The ExpoModules glue (@Field props record, EventDispatcher, Children label
4
+ // slot) is replaced by the @nativescript/swift-ui provider contract:
5
+ // updateData(NSDictionary) + onEvent. Children-as-label is not ported; the
6
+ // title string prop covers the label slot.
7
+
8
+ import SwiftUI
9
+
10
+ private final class DatePickerModel: ObservableObject {
11
+ @Published var title: String? = nil
12
+ @Published var selection: Date? = nil
13
+ @Published var rangeStart: Date? = nil
14
+ @Published var rangeEnd: Date? = nil
15
+ @Published var displayedComponents: DatePicker.Components = [.date]
16
+ @Published var pickerStyle: String = "automatic"
17
+ @Published var disabled = false
18
+ var onDateChange: ((Date) -> Void)?
19
+ }
20
+
21
+ private struct DatePickerContent: View {
22
+ @ObservedObject var model: DatePickerModel
23
+ @State private var date = Date()
24
+
25
+ // `.graphical` has an AutoLayout bug (it uses UICalendarView under the
26
+ // hood): it shrinks height when the user taps a date.
27
+ // https://github.com/expo/expo/issues/47062
28
+ // Fixed min width of 320 while the style is graphical.
29
+ private var isGraphicalStyle: Bool {
30
+ model.pickerStyle == "graphical"
31
+ }
32
+
33
+ var body: some View {
34
+ styledPicker()
35
+ .disabled(model.disabled)
36
+ .frame(minWidth: isGraphicalStyle ? 320 : nil)
37
+ .onChange(of: date) { newDate in
38
+ if model.selection == newDate { return }
39
+ model.onDateChange?(newDate)
40
+ }
41
+ .onChange(of: model.selection) { newValue in
42
+ date = newValue ?? Date()
43
+ }
44
+ .onAppear {
45
+ date = model.selection ?? Date()
46
+ }
47
+ }
48
+
49
+ @ViewBuilder
50
+ private func styledPicker() -> some View {
51
+ switch model.pickerStyle {
52
+ case "compact":
53
+ createDatePicker().datePickerStyle(.compact)
54
+ case "graphical":
55
+ createDatePicker().datePickerStyle(.graphical)
56
+ case "wheel":
57
+ createDatePicker().datePickerStyle(.wheel)
58
+ default:
59
+ createDatePicker().datePickerStyle(.automatic)
60
+ }
61
+ }
62
+
63
+ @ViewBuilder
64
+ private func createDatePicker() -> some View {
65
+ let components = model.displayedComponents
66
+ let start = model.rangeStart
67
+ let end = model.rangeEnd
68
+ let title = model.title ?? ""
69
+
70
+ let picker = Group {
71
+ if let start, let end {
72
+ DatePicker(title, selection: $date, in: start...end, displayedComponents: components)
73
+ } else if let start {
74
+ DatePicker(title, selection: $date, in: start..., displayedComponents: components)
75
+ } else if let end {
76
+ DatePicker(title, selection: $date, in: ...end, displayedComponents: components)
77
+ } else {
78
+ DatePicker(title, selection: $date, displayedComponents: components)
79
+ }
80
+ }
81
+
82
+ if model.title == nil {
83
+ picker.labelsHidden()
84
+ } else {
85
+ picker
86
+ }
87
+ }
88
+ }
89
+
90
+ @objc(XplatDatePickerProvider)
91
+ public final class XplatDatePickerProvider: UIViewController, SwiftUIProvider {
92
+ private let model = DatePickerModel()
93
+
94
+ public required init() {
95
+ super.init(nibName: nil, bundle: nil)
96
+ model.onDateChange = { [weak self] date in
97
+ self?.onEvent?(["date": date.timeIntervalSince1970 * 1000])
98
+ }
99
+ }
100
+
101
+ public required init?(coder: NSCoder) {
102
+ super.init(coder: coder)
103
+ model.onDateChange = { [weak self] date in
104
+ self?.onEvent?(["date": date.timeIntervalSince1970 * 1000])
105
+ }
106
+ }
107
+
108
+ public override func viewDidLoad() {
109
+ super.viewDidLoad()
110
+ setupSwiftUIView(content: DatePickerContent(model: model))
111
+ }
112
+
113
+ private static func millisToDate(_ value: Any?) -> Date? {
114
+ guard let millis = value as? NSNumber else { return nil }
115
+ return Date(timeIntervalSince1970: millis.doubleValue / 1000)
116
+ }
117
+
118
+ public func updateData(data: NSDictionary) {
119
+ // dataSerialize drops undefined keys — an absent key is a revert to
120
+ // the unset state, not "leave the previous value".
121
+ if data.object(forKey: "title") == nil {
122
+ model.title = nil
123
+ } else {
124
+ let title = data["title"] as? String
125
+ model.title = title?.isEmpty == false ? title : nil
126
+ }
127
+ model.selection = Self.millisToDate(data["selection"])
128
+
129
+ if let range = data["range"] as? [String: Any] {
130
+ model.rangeStart = Self.millisToDate(range["start"])
131
+ model.rangeEnd = Self.millisToDate(range["end"])
132
+ }
133
+ if let components = data["displayedComponents"] as? [String] {
134
+ var mapped: DatePicker.Components = []
135
+ if components.contains("date") { mapped.insert(.date) }
136
+ if components.contains("hourAndMinute") { mapped.insert(.hourAndMinute) }
137
+ if !mapped.isEmpty { model.displayedComponents = mapped }
138
+ }
139
+ if let pickerStyle = data["pickerStyle"] as? String {
140
+ model.pickerStyle = pickerStyle
141
+ }
142
+ if let disabled = data["disabled"] as? NSNumber {
143
+ model.disabled = disabled.boolValue
144
+ }
145
+ }
146
+
147
+ public var onEvent: ((NSDictionary) -> Void)?
148
+ }
@@ -0,0 +1,57 @@
1
+ /** @jsxImportSource @octane-xplat/macos-renderer */
2
+ import { useEffect, useRef } from 'octane'
3
+ import type { AppKitDatePickerProps } from './macos/types'
4
+
5
+ const host = (): any => (globalThis as any).__xplatAppKit ?? {}
6
+
7
+ /** Embeds a real NSDatePicker in the leaf's backing view via the
8
+ * `__xplatAppKit` bridge. 'date' uses a text field (or the inline
9
+ * graphical calendar when `pickerStyle` is 'graphical'); 'time' uses the
10
+ * clock-and-calendar field; 'dateAndTime' shows both element groups.
11
+ * The host element carries an explicit size — the picker is pinned to
12
+ * its edges. */
13
+ export function AppKitDatePicker(props: AppKitDatePickerProps) @{
14
+ const anchor = useRef<any>(null)
15
+ const handle = useRef<any>(null)
16
+
17
+ const options = () => ({
18
+ mode: props.components,
19
+ style: props.pickerStyle,
20
+ selectionMillis: props.selection?.getTime?.(),
21
+ minimumMillis: props.minimumDate?.getTime?.(),
22
+ maximumMillis: props.maximumDate?.getTime?.(),
23
+ enabled: !props.disabled,
24
+ })
25
+
26
+ useEffect(() => {
27
+ const view = anchor.current
28
+ const attach = host().attachDatePicker
29
+ if (!view || typeof attach !== 'function') return
30
+ // defaultSelection seeds the initial date; after that the user's
31
+ // pick owns the value — only the controlled `selection` is pushed.
32
+ handle.current = attach(
33
+ view,
34
+ { ...options(), selectionMillis: (props.selection ?? props.defaultSelection)?.getTime?.() },
35
+ (millis: number) => {
36
+ props.onSelectionChange?.(new Date(millis))
37
+ },
38
+ )
39
+
40
+ return () => {
41
+ handle.current?.detach()
42
+ handle.current = null
43
+ }
44
+ }, [props.components, props.pickerStyle])
45
+
46
+ // Push controlled selection/range updates into the mounted picker; the
47
+ // bridge update is echo-suppressed by comparing against dateValue.
48
+ useEffect(() => {
49
+ handle.current?.update(options())
50
+ })
51
+
52
+ <masonview ref={(v: any) => { anchor.current = v }} id={props.id} className={props.className} style={{display: "flex", ...({
53
+ minWidth: 240,
54
+ minHeight: props.pickerStyle === 'graphical' ? 300 : 26,
55
+ ...props.style,
56
+ })}}/>;
57
+ }
@@ -0,0 +1,49 @@
1
+ /** @jsxImportSource @nativescript-community/octane */
2
+ import { registerElement } from '@nativescript-community/octane'
3
+ import type { Attributes } from '@nativescript-community/octane/intrinsics'
4
+ import { ComposeDataDriver, JetpackCompose, registerJetpackCompose } from '@nativescript/jetpack-compose'
5
+ import type { MaterialDatePickerProps } from './android/types'
6
+
7
+ declare const com: any
8
+
9
+ declare module '@nativescript-community/octane/intrinsics' {
10
+ interface NativeScriptElements {
11
+ xplatandroiddatepicker: Attributes<typeof JetpackCompose>
12
+ }
13
+ }
14
+
15
+ registerElement('xplatandroiddatepicker', JetpackCompose)
16
+ registerJetpackCompose('xplat-material-datepicker', (owner: any) =>
17
+ new ComposeDataDriver(new com.octanexplat.datepicker.XplatDatePickerProvider(), owner),
18
+ )
19
+
20
+ /** Renders a Material 3 DatePicker/TimePicker through the NativeScript Jetpack Compose bridge. */
21
+ export function MaterialDatePicker(props: MaterialDatePickerProps) @{
22
+ const data = {
23
+ initialDate: props.initialDate?.getTime() ?? null,
24
+ variant: props.variant ?? 'picker',
25
+ displayedComponents: props.displayedComponents ?? 'date',
26
+ // The bridge's dataSerialize throws on booleans; send numbers.
27
+ showVariantToggle: (props.showVariantToggle ?? true) ? 1 : 0,
28
+ is24Hour: (props.is24Hour ?? true) ? 1 : 0,
29
+ color: props.color,
30
+ elementColors: props.elementColors,
31
+ selectableDates: {
32
+ start: props.selectableDates?.start?.getTime() ?? null,
33
+ end: props.selectableDates?.end?.getTime() ?? null,
34
+ },
35
+ }
36
+
37
+ <xplatandroiddatepicker
38
+ id={props.id}
39
+ className={props.className}
40
+ style={props.style}
41
+ accessibilityLabel={props.accessibilityLabel}
42
+ composeId="xplat-material-datepicker"
43
+ data={data}
44
+ onComposeEvent={(event: any) => {
45
+ const millis = event.data?.date
46
+ props.onDateSelected?.(typeof millis === 'number' ? new Date(millis) : null)
47
+ }}
48
+ />;
49
+ }
@@ -0,0 +1,51 @@
1
+ /** @jsxImportSource @nativescript-community/octane */
2
+ import { registerElement } from '@nativescript-community/octane'
3
+ import type { Attributes } from '@nativescript-community/octane/intrinsics'
4
+ import { SwiftUI, UIDataDriver, registerSwiftUI } from '@nativescript/swift-ui'
5
+ import { useState } from 'octane'
6
+ import type { SwiftUIDatePickerProps } from './ios/types'
7
+
8
+ declare const XplatDatePickerProvider: any
9
+
10
+ declare module '@nativescript-community/octane/intrinsics' {
11
+ interface NativeScriptElements {
12
+ xplatiosdatepicker: Attributes<typeof SwiftUI>
13
+ }
14
+ }
15
+
16
+ registerElement('xplatiosdatepicker', SwiftUI)
17
+ registerSwiftUI('xplat-swiftui-datepicker', (owner: any) =>
18
+ new UIDataDriver(new XplatDatePickerProvider(), owner),
19
+ )
20
+
21
+ /** Renders a SwiftUI DatePicker through NativeScript's SwiftUI bridge. */
22
+ export function SwiftUIDatePicker(props: SwiftUIDatePickerProps) @{
23
+ const controlled = props.selection !== undefined
24
+ const [internalSelection, setInternalSelection] = useState<Date | undefined>(props.defaultSelection)
25
+ const selection = controlled ? props.selection! : internalSelection
26
+
27
+ <xplatiosdatepicker
28
+ id={props.id}
29
+ className={props.className}
30
+ style={props.style}
31
+ swiftId="xplat-swiftui-datepicker"
32
+ data={{
33
+ title: props.title,
34
+ selection: selection?.getTime(),
35
+ range: {
36
+ start: props.minimumDate?.getTime(),
37
+ end: props.maximumDate?.getTime(),
38
+ },
39
+ displayedComponents: props.displayedComponents ?? ['date'],
40
+ pickerStyle: props.pickerStyle ?? 'automatic',
41
+ disabled: props.disabled ?? false,
42
+ }}
43
+ onSwiftUIEvent={(event: any) => {
44
+ const millis = event.data?.date
45
+ if (typeof millis !== 'number') return
46
+ const next = new Date(millis)
47
+ if (!controlled) setInternalSelection(next)
48
+ props.onSelectionChange?.(next)
49
+ }}
50
+ />;
51
+ }
@@ -0,0 +1,7 @@
1
+ export { MaterialDatePicker } from '../MaterialDatePicker.android.tsrx'
2
+ export type {
3
+ MaterialDatePickerComponents,
4
+ MaterialDatePickerElementColors,
5
+ MaterialDatePickerProps,
6
+ MaterialDatePickerVariant,
7
+ } from './types'
@@ -0,0 +1,80 @@
1
+ export type MaterialDatePickerVariant = 'picker' | 'input'
2
+
3
+ export type MaterialDatePickerComponents = 'date' | 'hourAndMinute' | 'dateAndTime'
4
+
5
+ /**
6
+ * Color overrides for the Material 3 picker elements. Every field is a CSS
7
+ * hex string (`#rgb`, `#rrggbb`, or `#aarrggbb`); unset fields use the
8
+ * Material 3 theme defaults.
9
+ */
10
+ export interface MaterialDatePickerElementColors {
11
+ containerColor?: string
12
+ titleContentColor?: string
13
+ headlineContentColor?: string
14
+ weekdayContentColor?: string
15
+ subheadContentColor?: string
16
+ navigationContentColor?: string
17
+ yearContentColor?: string
18
+ disabledYearContentColor?: string
19
+ currentYearContentColor?: string
20
+ selectedYearContentColor?: string
21
+ disabledSelectedYearContentColor?: string
22
+ selectedYearContainerColor?: string
23
+ disabledSelectedYearContainerColor?: string
24
+ dayContentColor?: string
25
+ disabledDayContentColor?: string
26
+ selectedDayContentColor?: string
27
+ disabledSelectedDayContentColor?: string
28
+ selectedDayContainerColor?: string
29
+ disabledSelectedDayContainerColor?: string
30
+ todayContentColor?: string
31
+ todayDateBorderColor?: string
32
+ dayInSelectionRangeContentColor?: string
33
+ dayInSelectionRangeContainerColor?: string
34
+ dividerColor?: string
35
+ clockDialColor?: string
36
+ clockDialSelectedContentColor?: string
37
+ clockDialUnselectedContentColor?: string
38
+ selectorColor?: string
39
+ periodSelectorBorderColor?: string
40
+ periodSelectorSelectedContainerColor?: string
41
+ periodSelectorUnselectedContainerColor?: string
42
+ periodSelectorSelectedContentColor?: string
43
+ periodSelectorUnselectedContentColor?: string
44
+ timeSelectorSelectedContainerColor?: string
45
+ timeSelectorUnselectedContainerColor?: string
46
+ timeSelectorSelectedContentColor?: string
47
+ timeSelectorUnselectedContentColor?: string
48
+ }
49
+
50
+ export interface MaterialDatePickerProps {
51
+ id?: string
52
+ className?: any
53
+ style?: any
54
+ /** Date the picker starts on. The picker keeps its own selection after mount. */
55
+ initialDate?: Date
56
+ /** Calendar grid or text-field input. @default 'picker' */
57
+ variant?: MaterialDatePickerVariant
58
+ /**
59
+ * `'date'` renders the M3 DatePicker, `'hourAndMinute'` the M3 TimePicker.
60
+ * `'dateAndTime'` renders the date picker (combined input is not supported).
61
+ * @default 'date'
62
+ */
63
+ displayedComponents?: MaterialDatePickerComponents
64
+ /** Show the picker/input mode toggle. @default true */
65
+ showVariantToggle?: boolean
66
+ /** 24-hour dial for the time picker. @default true */
67
+ is24Hour?: boolean
68
+ /** Accent applied to a subset of elements when `elementColors` is unset. */
69
+ color?: string
70
+ elementColors?: MaterialDatePickerElementColors
71
+ /** Bounds the selectable date range; also derives the calendar year range. */
72
+ selectableDates?: { start?: Date; end?: Date }
73
+ accessibilityLabel?: string
74
+ /**
75
+ * Called with the selected timestamp (null when the selection clears). The
76
+ * DatePicker emits the chosen UTC day; the TimePicker emits the chosen
77
+ * hour/minute on the initial date's day.
78
+ */
79
+ onDateSelected?: (date: Date | null) => void
80
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ // NativeScript vendors each dependency by its package root. Widgets remain
2
+ // on their explicit OS subpaths; this entry must not load another OS leaf.
3
+ export {}
@@ -0,0 +1,6 @@
1
+ export { SwiftUIDatePicker } from '../SwiftUIDatePicker.ios.tsrx'
2
+ export type {
3
+ SwiftUIDatePickerComponent,
4
+ SwiftUIDatePickerProps,
5
+ SwiftUIDatePickerStyle,
6
+ } from './types'
@@ -0,0 +1,26 @@
1
+ export type SwiftUIDatePickerStyle = 'automatic' | 'compact' | 'graphical' | 'wheel'
2
+
3
+ export type SwiftUIDatePickerComponent = 'date' | 'hourAndMinute'
4
+
5
+ export interface SwiftUIDatePickerProps {
6
+ id?: string
7
+ className?: any
8
+ style?: any
9
+ /** Label shown next to the picker. When omitted, the label is hidden. */
10
+ title?: string
11
+ /** Controlled selection. Pair with `onSelectionChange`. */
12
+ selection?: Date
13
+ /** Initial selection when `selection` is not provided. */
14
+ defaultSelection?: Date
15
+ /** Earliest selectable date. */
16
+ minimumDate?: Date
17
+ /** Latest selectable date. */
18
+ maximumDate?: Date
19
+ /** Which components the picker displays. @default ['date'] */
20
+ displayedComponents?: readonly SwiftUIDatePickerComponent[]
21
+ /** SwiftUI presentation style. `graphical` renders an inline calendar. @default 'automatic' */
22
+ pickerStyle?: SwiftUIDatePickerStyle
23
+ disabled?: boolean
24
+ accessibilityLabel?: string
25
+ onSelectionChange?: (date: Date) => void
26
+ }
@@ -0,0 +1,6 @@
1
+ export { AppKitDatePicker } from '../AppKitDatePicker.macos.tsrx'
2
+ export type {
3
+ AppKitDatePickerComponents,
4
+ AppKitDatePickerProps,
5
+ AppKitDatePickerStyle,
6
+ } from './types'
@@ -0,0 +1,24 @@
1
+ export type AppKitDatePickerComponents = 'date' | 'time' | 'dateAndTime'
2
+
3
+ export type AppKitDatePickerStyle = 'textField' | 'graphical'
4
+
5
+ export interface AppKitDatePickerProps {
6
+ id?: string
7
+ className?: any
8
+ style?: any
9
+ /** Which element groups the NSDatePicker shows. @default 'date' */
10
+ components?: AppKitDatePickerComponents
11
+ /** 'graphical' embeds the inline calendar (date mode only). @default 'textField' */
12
+ pickerStyle?: AppKitDatePickerStyle
13
+ /** Controlled selection. Pair with `onSelectionChange`. */
14
+ selection?: Date
15
+ /** Initial selection when `selection` is not provided. */
16
+ defaultSelection?: Date
17
+ /** Earliest selectable date. */
18
+ minimumDate?: Date
19
+ /** Latest selectable date. */
20
+ maximumDate?: Date
21
+ disabled?: boolean
22
+ accessibilityLabel?: string
23
+ onSelectionChange?: (date: Date) => void
24
+ }
@@ -0,0 +1,61 @@
1
+ export type MaterialDatePickerVariant = 'picker' | 'input'
2
+
3
+ export type MaterialDatePickerComponents = 'date' | 'hourAndMinute' | 'dateAndTime'
4
+
5
+ export interface MaterialDatePickerElementColors {
6
+ containerColor?: string
7
+ titleContentColor?: string
8
+ headlineContentColor?: string
9
+ weekdayContentColor?: string
10
+ subheadContentColor?: string
11
+ navigationContentColor?: string
12
+ yearContentColor?: string
13
+ disabledYearContentColor?: string
14
+ currentYearContentColor?: string
15
+ selectedYearContentColor?: string
16
+ disabledSelectedYearContentColor?: string
17
+ selectedYearContainerColor?: string
18
+ disabledSelectedYearContainerColor?: string
19
+ dayContentColor?: string
20
+ disabledDayContentColor?: string
21
+ selectedDayContentColor?: string
22
+ disabledSelectedDayContentColor?: string
23
+ selectedDayContainerColor?: string
24
+ disabledSelectedDayContainerColor?: string
25
+ todayContentColor?: string
26
+ todayDateBorderColor?: string
27
+ dayInSelectionRangeContentColor?: string
28
+ dayInSelectionRangeContainerColor?: string
29
+ dividerColor?: string
30
+ clockDialColor?: string
31
+ clockDialSelectedContentColor?: string
32
+ clockDialUnselectedContentColor?: string
33
+ selectorColor?: string
34
+ periodSelectorBorderColor?: string
35
+ periodSelectorSelectedContainerColor?: string
36
+ periodSelectorUnselectedContainerColor?: string
37
+ periodSelectorSelectedContentColor?: string
38
+ periodSelectorUnselectedContentColor?: string
39
+ timeSelectorSelectedContainerColor?: string
40
+ timeSelectorUnselectedContainerColor?: string
41
+ timeSelectorSelectedContentColor?: string
42
+ timeSelectorUnselectedContentColor?: string
43
+ }
44
+
45
+ export interface MaterialDatePickerProps {
46
+ id?: string
47
+ className?: any
48
+ style?: any
49
+ initialDate?: Date
50
+ variant?: MaterialDatePickerVariant
51
+ displayedComponents?: MaterialDatePickerComponents
52
+ showVariantToggle?: boolean
53
+ is24Hour?: boolean
54
+ color?: string
55
+ elementColors?: MaterialDatePickerElementColors
56
+ selectableDates?: { start?: Date; end?: Date }
57
+ accessibilityLabel?: string
58
+ onDateSelected?: (date: Date | null) => void
59
+ }
60
+
61
+ export declare function MaterialDatePicker(props: MaterialDatePickerProps): unknown
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,21 @@
1
+ export type SwiftUIDatePickerStyle = 'automatic' | 'compact' | 'graphical' | 'wheel'
2
+
3
+ export type SwiftUIDatePickerComponent = 'date' | 'hourAndMinute'
4
+
5
+ export interface SwiftUIDatePickerProps {
6
+ id?: string
7
+ className?: any
8
+ style?: any
9
+ title?: string
10
+ selection?: Date
11
+ defaultSelection?: Date
12
+ minimumDate?: Date
13
+ maximumDate?: Date
14
+ displayedComponents?: readonly SwiftUIDatePickerComponent[]
15
+ pickerStyle?: SwiftUIDatePickerStyle
16
+ disabled?: boolean
17
+ accessibilityLabel?: string
18
+ onSelectionChange?: (date: Date) => void
19
+ }
20
+
21
+ export declare function SwiftUIDatePicker(props: SwiftUIDatePickerProps): unknown
@@ -0,0 +1,20 @@
1
+ export type AppKitDatePickerComponents = 'date' | 'time' | 'dateAndTime'
2
+
3
+ export type AppKitDatePickerStyle = 'textField' | 'graphical'
4
+
5
+ export interface AppKitDatePickerProps {
6
+ id?: string
7
+ className?: any
8
+ style?: any
9
+ components?: AppKitDatePickerComponents
10
+ pickerStyle?: AppKitDatePickerStyle
11
+ selection?: Date
12
+ defaultSelection?: Date
13
+ minimumDate?: Date
14
+ maximumDate?: Date
15
+ disabled?: boolean
16
+ accessibilityLabel?: string
17
+ onSelectionChange?: (date: Date) => void
18
+ }
19
+
20
+ export declare function AppKitDatePicker(props: AppKitDatePickerProps): unknown