@octane-xplat/date-picker 0.0.1 → 0.10.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,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 CHANGED
@@ -1,3 +1,141 @@
1
- # @octane-xplat/date-picker
1
+ # `@octane-xplat/date-picker`
2
2
 
3
- Version 0.0.1 is a reservation stub so the automated release can publish this package name. It has no supported API. Use a later release for the implementation.
3
+ An install boundary for platform-specific date and time selection controls:
4
+ a SwiftUI `DatePicker` on iOS, Material 3 `DatePicker`/`TimePicker` built with
5
+ Jetpack Compose on Android, and a real `NSDatePicker` on macOS. For portable
6
+ form controls, use `Calendar`, `DateInput`, `TimeInput`, `DateTimeInput`, and
7
+ `DateRangeInput` from `@octane-xplat/ui` on every target.
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
+ Install the package in the app that renders a control. Import
20
+ `SwiftUIDatePicker` from `@octane-xplat/date-picker/ios`,
21
+ `MaterialDatePicker` from `@octane-xplat/date-picker/android`, or
22
+ `AppKitDatePicker` from `@octane-xplat/date-picker/macos` in the matching
23
+ platform-suffixed file. These native entries have platform-specific contracts.
24
+ The old `@octane-xplat/date-picker/web` `DateInput` subpath was removed because
25
+ its browser-only string contract conflicted with the shared `DateInput` API.
26
+
27
+ ```tsx
28
+ /** @jsxImportSource @nativescript-community/octane */
29
+ // SwiftUIDatePickerExample.ios.tsx
30
+ import { SwiftUIDatePicker } from '@octane-xplat/date-picker/ios'
31
+
32
+ export function Picker() {
33
+ return (
34
+ <SwiftUIDatePicker
35
+ defaultSelection={new Date(2026, 9, 2)}
36
+ displayedComponents={['date']}
37
+ pickerStyle="graphical"
38
+ onSelectionChange={console.log}
39
+ />
40
+ )
41
+ }
42
+ ```
43
+
44
+ ```tsx
45
+ /** @jsxImportSource @nativescript-community/octane */
46
+ // MaterialDatePickerExample.android.tsx
47
+ import { MaterialDatePicker } from '@octane-xplat/date-picker/android'
48
+
49
+ const initialDate = new Date(2026, 9, 2)
50
+ const bounds = { start: new Date(2026, 0, 1), end: new Date(2026, 11, 31) }
51
+
52
+ export function Picker() {
53
+ return (
54
+ <MaterialDatePicker
55
+ initialDate={initialDate}
56
+ selectableDates={bounds}
57
+ onDateSelected={console.log}
58
+ />
59
+ )
60
+ }
61
+ ```
62
+
63
+ ```tsx
64
+ // AppKitDatePickerExample.macos.tsx
65
+ import { AppKitDatePicker } from '@octane-xplat/date-picker/macos'
66
+
67
+ export function Picker() {
68
+ return (
69
+ <AppKitDatePicker
70
+ components="date"
71
+ pickerStyle="graphical"
72
+ defaultSelection={new Date(2026, 9, 2)}
73
+ onSelectionChange={console.log}
74
+ />
75
+ )
76
+ }
77
+ ```
78
+
79
+ The platform APIs use their own selection vocabulary: the iOS entry takes a
80
+ controlled `selection`/`defaultSelection` `Date` with `displayedComponents`
81
+ and `pickerStyle`; the Android entry follows the uncontrolled
82
+ `initialDate`/`onDateSelected` model of the Material 3 pickers with
83
+ `selectableDates` range bounds; the macOS entry takes
84
+ `components`/`pickerStyle` (`'graphical'` embeds the
85
+ inline calendar) with a controlled `selection` `Date`.
86
+
87
+ ```tsx
88
+ // Departure.macos.tsx
89
+ import { useState } from 'octane'
90
+ import { AppKitDatePicker } from '@octane-xplat/date-picker/macos'
91
+
92
+ export function Departure() {
93
+ const [selection, setSelection] = useState(new Date(2026, 9, 2))
94
+ return (
95
+ <AppKitDatePicker
96
+ components="date"
97
+ pickerStyle="graphical"
98
+ selection={selection}
99
+ onSelectionChange={setSelection}
100
+ />
101
+ )
102
+ }
103
+ ```
104
+
105
+ Android notes: give `MaterialDatePicker` a bounded height (the M3 calendar's
106
+ lazy grid collapses under unbounded height), and keep `initialDate` and
107
+ `selectableDates` identity-stable — the provider keys its state `remember`
108
+ on them, so a per-render `new Date()` resets the selection.
109
+ See the framework guide for details and maintained examples:
110
+ [`docs/platform/date-picker.md`](../../docs/platform/date-picker.md).
111
+
112
+ ```tsx
113
+ /** @jsxImportSource @nativescript-community/octane */
114
+ // Departure.android.tsx — reuse these objects while the picker is mounted.
115
+ import { MaterialDatePicker } from '@octane-xplat/date-picker/android'
116
+
117
+ const initialDate = new Date(2026, 9, 2)
118
+ const selectableDates = { start: new Date(2026, 0, 1) }
119
+ export function Departure() {
120
+ return (
121
+ <MaterialDatePicker
122
+ className="departure-calendar"
123
+ initialDate={initialDate}
124
+ selectableDates={selectableDates}
125
+ onDateSelected={console.log}
126
+ />
127
+ )
128
+ }
129
+ ```
130
+
131
+ ```css
132
+ /* App stylesheet: give the Android calendar room for its grid. */
133
+ .departure-calendar {
134
+ height: 480px;
135
+ }
136
+ ```
137
+
138
+ The native implementations are adapted from `@expo/ui` (MIT,
139
+ `packages/expo-ui` sdk-57): `ios/DatePickerView.swift` and
140
+ `android/.../ui/DatePickerView.kt`. Attribution headers sit on the ported
141
+ files.
@@ -0,0 +1,46 @@
1
+ import { defineUniversalComponent, registerElement, universalPlan, universalValue } from "@nativescript-community/octane";
2
+ import { ComposeDataDriver, JetpackCompose, registerJetpackCompose } from "@nativescript/jetpack-compose";
3
+ //#region src/MaterialDatePicker.android.tsrx
4
+ var __octaneUniversalPlan0 = universalPlan("nativescript", {
5
+ "kind": "host",
6
+ "type": "xplatandroiddatepicker",
7
+ "props": { "composeId": "xplat-material-datepicker" },
8
+ "bindings": [
9
+ ["id", 0],
10
+ ["className", 1],
11
+ ["style", 2],
12
+ ["accessibilityLabel", 3],
13
+ ["data", 4],
14
+ ["onComposeEvent", 5]
15
+ ]
16
+ });
17
+ registerElement("xplatandroiddatepicker", JetpackCompose);
18
+ registerJetpackCompose("xplat-material-datepicker", (owner) => new ComposeDataDriver(new com.octanexplat.datepicker.XplatDatePickerProvider(), owner));
19
+ var MaterialDatePicker = defineUniversalComponent("nativescript", function MaterialDatePicker(props) {
20
+ const data = {
21
+ initialDate: props.initialDate?.getTime() ?? null,
22
+ variant: props.variant ?? "picker",
23
+ displayedComponents: props.displayedComponents ?? "date",
24
+ showVariantToggle: props.showVariantToggle ?? true ? 1 : 0,
25
+ is24Hour: props.is24Hour ?? true ? 1 : 0,
26
+ color: props.color,
27
+ elementColors: props.elementColors,
28
+ selectableDates: {
29
+ start: props.selectableDates?.start?.getTime() ?? null,
30
+ end: props.selectableDates?.end?.getTime() ?? null
31
+ }
32
+ };
33
+ return universalValue(__octaneUniversalPlan0, [
34
+ props.id,
35
+ props.className,
36
+ props.style,
37
+ props.accessibilityLabel,
38
+ data,
39
+ (event) => {
40
+ const millis = event.data?.date;
41
+ props.onDateSelected?.(typeof millis === "number" ? new Date(millis) : null);
42
+ }
43
+ ]);
44
+ }, { "module": "@nativescript-community/octane" });
45
+ //#endregion
46
+ export { MaterialDatePicker };
@@ -0,0 +1,48 @@
1
+ import { defineUniversalComponent, hookSlots, registerElement, universalPlan, universalValue, useState } from "@nativescript-community/octane";
2
+ import { SwiftUI, UIDataDriver, registerSwiftUI } from "@nativescript/swift-ui";
3
+ //#region src/SwiftUIDatePicker.ios.tsrx
4
+ var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
5
+ var __octaneUniversalPlan0 = universalPlan("nativescript", {
6
+ "kind": "host",
7
+ "type": "xplatiosdatepicker",
8
+ "props": { "swiftId": "xplat-swiftui-datepicker" },
9
+ "bindings": [
10
+ ["id", 0],
11
+ ["className", 1],
12
+ ["style", 2],
13
+ ["data", 3],
14
+ ["onSwiftUIEvent", 4]
15
+ ]
16
+ });
17
+ registerElement("xplatiosdatepicker", SwiftUI);
18
+ registerSwiftUI("xplat-swiftui-datepicker", (owner) => new UIDataDriver(new XplatDatePickerProvider(), owner));
19
+ var SwiftUIDatePicker = defineUniversalComponent("nativescript", function SwiftUIDatePicker(props) {
20
+ const controlled = props.selection !== void 0;
21
+ const [internalSelection, setInternalSelection] = useState(props.defaultSelection, _h$0);
22
+ const selection = controlled ? props.selection : internalSelection;
23
+ return universalValue(__octaneUniversalPlan0, [
24
+ props.id,
25
+ props.className,
26
+ props.style,
27
+ {
28
+ title: props.title,
29
+ selection: selection?.getTime(),
30
+ range: {
31
+ start: props.minimumDate?.getTime(),
32
+ end: props.maximumDate?.getTime()
33
+ },
34
+ displayedComponents: props.displayedComponents ?? ["date"],
35
+ pickerStyle: props.pickerStyle ?? "automatic",
36
+ disabled: props.disabled ?? false
37
+ },
38
+ (event) => {
39
+ const millis = event.data?.date;
40
+ if (typeof millis !== "number") return;
41
+ const next = new Date(millis);
42
+ if (!controlled) setInternalSelection(next);
43
+ props.onSelectionChange?.(next);
44
+ }
45
+ ]);
46
+ }, { "module": "@nativescript-community/octane" });
47
+ //#endregion
48
+ export { SwiftUIDatePicker };
@@ -0,0 +1,2 @@
1
+ import { MaterialDatePicker } from "../MaterialDatePicker.android.tsrx.js";
2
+ export { MaterialDatePicker };
@@ -0,0 +1,2 @@
1
+ import { SwiftUIDatePicker } from "../SwiftUIDatePicker.ios.tsrx.js";
2
+ export { SwiftUIDatePicker };
@@ -0,0 +1,8 @@
1
+ {
2
+ "version": 1,
3
+ "buildId": "cf47df22-f603-4b83-aabd-2f78d166e858",
4
+ "mode": "production",
5
+ "capabilities": {
6
+ "independentHydration": false
7
+ }
8
+ }
package/package.json CHANGED
@@ -1,18 +1,74 @@
1
1
  {
2
2
  "name": "@octane-xplat/date-picker",
3
- "version": "0.0.1",
3
+ "version": "0.10.0",
4
4
  "description": "Platform-specific date and time selection controls for Octane xplat",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "https://github.com/octane-xplat/octane-xplat.git",
7
+ "url": "https://github.com/octane-xplat/octane-xplat",
8
8
  "directory": "packages/date-picker"
9
9
  },
10
- "homepage": "https://github.com/octane-xplat/octane-xplat/tree/main/packages/date-picker",
11
- "main": "./index.js",
12
- "types": "./index.d.ts",
13
10
  "files": [
14
- "README.md",
15
- "index.js",
16
- "index.d.ts"
17
- ]
18
- }
11
+ "dist",
12
+ "types",
13
+ "src",
14
+ "platforms"
15
+ ],
16
+ "type": "module",
17
+ "exports": {
18
+ "./ios": {
19
+ "types": "./types/ios/index.d.ts",
20
+ "native": "./dist/native/ios/index.js"
21
+ },
22
+ "./android": {
23
+ "types": "./types/android/index.d.ts",
24
+ "native": "./dist/native/android/index.js"
25
+ },
26
+ "./macos": {
27
+ "types": "./types/macos/index.d.ts",
28
+ "macos": "./src/macos/index.ts"
29
+ }
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "dependencies": {
35
+ "@nativescript/jetpack-compose": "1.0.0-beta.0",
36
+ "@nativescript/swift-ui": "4.0.2"
37
+ },
38
+ "devDependencies": {
39
+ "@nativescript-community/octane": "0.2.4",
40
+ "@nativescript/core": "9.1.2",
41
+ "@octanejs/vite-plugin": "0.1.60",
42
+ "octane": "0.5.0",
43
+ "tsrx-typegen": "0.2.2",
44
+ "typescript": "5.9.3",
45
+ "vite": "8.3.0"
46
+ },
47
+ "peerDependencies": {
48
+ "@nativescript-community/octane": ">=0.2.1 <1",
49
+ "@nativescript/core": ">=9.1.0 <10",
50
+ "octane": ">=0.1.51 <1"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "@nativescript-community/octane": {
54
+ "optional": true
55
+ },
56
+ "@nativescript/core": {
57
+ "optional": true
58
+ },
59
+ "octane": {
60
+ "optional": true
61
+ }
62
+ },
63
+ "nativescript": {
64
+ "platforms": {
65
+ "android": "9.1.0",
66
+ "ios": "9.1.0"
67
+ }
68
+ },
69
+ "scripts": {
70
+ "build": "vite build --mode native && tsc -p tsconfig.types.json",
71
+ "test:packed": "node tests/packed-consumer.mjs",
72
+ "pack:check": "tsrx-typegen --pack-check"
73
+ }
74
+ }
@@ -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,335 @@
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.runtime.Composable
25
+ import androidx.compose.runtime.CompositionLocalProvider
26
+ import androidx.compose.runtime.LaunchedEffect
27
+ import androidx.compose.runtime.getValue
28
+ import androidx.compose.runtime.mutableStateOf
29
+ import androidx.compose.runtime.remember
30
+ import androidx.compose.runtime.setValue
31
+ import androidx.compose.ui.Modifier
32
+ import androidx.compose.ui.graphics.Color
33
+ import androidx.compose.ui.platform.ComposeView
34
+ import androidx.compose.ui.platform.LocalConfiguration
35
+ import org.json.JSONObject
36
+ import java.util.Calendar
37
+ import java.util.Date
38
+
39
+ private fun Any?.asBoolean(): Boolean = when (this) {
40
+ is Boolean -> this
41
+ is Number -> toInt() != 0
42
+ else -> false
43
+ }
44
+
45
+ private fun Any?.asLong(): Long? = when (this) {
46
+ is Number -> toLong()
47
+ else -> null
48
+ }
49
+
50
+ private fun Any?.asColor(): Color? = when (this) {
51
+ is String -> runCatching { Color(android.graphics.Color.parseColor(this)) }.getOrNull()
52
+ else -> null
53
+ }
54
+
55
+ private data class SelectableRange(
56
+ val start: Long? = null,
57
+ val end: Long? = null,
58
+ )
59
+
60
+ private data class DatePickerUiState(
61
+ val initialDate: Long? = null,
62
+ val variant: String = "picker",
63
+ val displayedComponents: String = "date",
64
+ val showVariantToggle: Boolean = true,
65
+ val is24Hour: Boolean = true,
66
+ val color: Color? = null,
67
+ val elementColors: Map<*, *> = emptyMap<Any?, Any?>(),
68
+ val selectableDates: SelectableRange = SelectableRange(),
69
+ )
70
+
71
+ private fun toUtcDayMillis(localMillis: Long): Long {
72
+ val cal = Calendar.getInstance()
73
+ cal.timeInMillis = localMillis
74
+ val utcCal = Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))
75
+ utcCal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0)
76
+ utcCal.set(Calendar.MILLISECOND, 0)
77
+ return utcCal.timeInMillis
78
+ }
79
+
80
+ // Material3 stores selectedDateMillis as UTC-midnight epoch; emitting it raw
81
+ // makes `new Date(ms)` resolve to the previous day in negative-UTC zones.
82
+ // Convert back to local-midnight millis so the JS-side Date lands on the
83
+ // calendar day the user picked.
84
+ private fun toLocalDayMillis(utcMillis: Long): Long {
85
+ val utcCal = Calendar.getInstance(java.util.TimeZone.getTimeZone("UTC"))
86
+ utcCal.timeInMillis = utcMillis
87
+ val localCal = Calendar.getInstance()
88
+ localCal.clear()
89
+ localCal.set(utcCal.get(Calendar.YEAR), utcCal.get(Calendar.MONTH), utcCal.get(Calendar.DAY_OF_MONTH))
90
+ return localCal.timeInMillis
91
+ }
92
+
93
+ @OptIn(ExperimentalMaterial3Api::class)
94
+ @Composable
95
+ private fun rememberSelectableDates(range: SelectableRange): SelectableDates {
96
+ val start = range.start
97
+ val end = range.end
98
+ return remember(start, end) {
99
+ if (start != null || end != null) {
100
+ val startUtcDayMillis = start?.let { toUtcDayMillis(it) }
101
+ val endUtcDayMillis = end?.let { toUtcDayMillis(it) }
102
+ val startYear = start?.let {
103
+ val cal = Calendar.getInstance()
104
+ cal.timeInMillis = it
105
+ cal.get(Calendar.YEAR)
106
+ }
107
+ val endYear = end?.let {
108
+ val cal = Calendar.getInstance()
109
+ cal.timeInMillis = it
110
+ cal.get(Calendar.YEAR)
111
+ }
112
+ object : SelectableDates {
113
+ override fun isSelectableDate(utcTimeMillis: Long): Boolean {
114
+ if (startUtcDayMillis != null && utcTimeMillis < startUtcDayMillis) return false
115
+ if (endUtcDayMillis != null && utcTimeMillis > endUtcDayMillis) return false
116
+ return true
117
+ }
118
+
119
+ override fun isSelectableYear(year: Int): Boolean {
120
+ if (startYear != null && year < startYear) return false
121
+ if (endYear != null && year > endYear) return false
122
+ return true
123
+ }
124
+ }
125
+ } else {
126
+ DatePickerDefaults.AllDates
127
+ }
128
+ }
129
+ }
130
+
131
+ // `selectableDates` only greys out unselectable cells; the calendar's actual
132
+ // extent (months list, year grid, range content description) comes from
133
+ // `yearRange`. Derive it from min/max so the picker reflects the constraints
134
+ // instead of always spanning the 1900..2100 default.
135
+ // https://github.com/expo/expo/issues/47206
136
+ @OptIn(ExperimentalMaterial3Api::class)
137
+ @Composable
138
+ private fun rememberDatePickerYearRange(range: SelectableRange, initialDateMillis: Long): IntRange {
139
+ val start = range.start
140
+ val end = range.end
141
+ return remember(start, end, initialDateMillis) {
142
+ val defaults = DatePickerDefaults.YearRange
143
+ val yearOf = { millis: Long ->
144
+ Calendar.getInstance().apply { timeInMillis = millis }.get(Calendar.YEAR)
145
+ }
146
+ val startYear = start?.let(yearOf) ?: defaults.first
147
+ val endYear = end?.let(yearOf) ?: defaults.last
148
+ val initialYear = yearOf(initialDateMillis)
149
+ minOf(startYear, initialYear)..maxOf(endYear, initialYear)
150
+ }
151
+ }
152
+
153
+ private fun Map<*, *>.color(name: String): Color? = this[name].asColor()
154
+
155
+ @OptIn(ExperimentalMaterial3Api::class)
156
+ @Composable
157
+ private fun buildDatePickerColors(
158
+ elementColors: Map<*, *>,
159
+ colorProp: Color?,
160
+ ): androidx.compose.material3.DatePickerColors {
161
+ val defaults = DatePickerDefaults.colors()
162
+ return defaults.copy(
163
+ containerColor = elementColors.color("containerColor") ?: defaults.containerColor,
164
+ titleContentColor = elementColors.color("titleContentColor") ?: colorProp ?: defaults.titleContentColor,
165
+ headlineContentColor = elementColors.color("headlineContentColor") ?: colorProp ?: defaults.headlineContentColor,
166
+ weekdayContentColor = elementColors.color("weekdayContentColor") ?: defaults.weekdayContentColor,
167
+ subheadContentColor = elementColors.color("subheadContentColor") ?: defaults.subheadContentColor,
168
+ navigationContentColor = elementColors.color("navigationContentColor") ?: defaults.navigationContentColor,
169
+ yearContentColor = elementColors.color("yearContentColor") ?: defaults.yearContentColor,
170
+ disabledYearContentColor = elementColors.color("disabledYearContentColor") ?: defaults.disabledYearContentColor,
171
+ currentYearContentColor = elementColors.color("currentYearContentColor") ?: defaults.currentYearContentColor,
172
+ selectedYearContentColor = elementColors.color("selectedYearContentColor") ?: defaults.selectedYearContentColor,
173
+ disabledSelectedYearContentColor = elementColors.color("disabledSelectedYearContentColor") ?: defaults.disabledSelectedYearContentColor,
174
+ selectedYearContainerColor = elementColors.color("selectedYearContainerColor") ?: defaults.selectedYearContainerColor,
175
+ disabledSelectedYearContainerColor = elementColors.color("disabledSelectedYearContainerColor") ?: defaults.disabledSelectedYearContainerColor,
176
+ dayContentColor = elementColors.color("dayContentColor") ?: defaults.dayContentColor,
177
+ disabledDayContentColor = elementColors.color("disabledDayContentColor") ?: defaults.disabledDayContentColor,
178
+ selectedDayContentColor = elementColors.color("selectedDayContentColor") ?: defaults.selectedDayContentColor,
179
+ disabledSelectedDayContentColor = elementColors.color("disabledSelectedDayContentColor") ?: defaults.disabledSelectedDayContentColor,
180
+ selectedDayContainerColor = elementColors.color("selectedDayContainerColor") ?: colorProp ?: defaults.selectedDayContainerColor,
181
+ disabledSelectedDayContainerColor = elementColors.color("disabledSelectedDayContainerColor") ?: defaults.disabledSelectedDayContainerColor,
182
+ todayContentColor = elementColors.color("todayContentColor") ?: defaults.todayContentColor,
183
+ todayDateBorderColor = elementColors.color("todayDateBorderColor") ?: colorProp ?: defaults.todayDateBorderColor,
184
+ dayInSelectionRangeContentColor = elementColors.color("dayInSelectionRangeContentColor") ?: defaults.dayInSelectionRangeContentColor,
185
+ dayInSelectionRangeContainerColor = elementColors.color("dayInSelectionRangeContainerColor") ?: defaults.dayInSelectionRangeContainerColor,
186
+ dividerColor = elementColors.color("dividerColor") ?: defaults.dividerColor,
187
+ )
188
+ }
189
+
190
+ @OptIn(ExperimentalMaterial3Api::class)
191
+ @Composable
192
+ private fun buildTimePickerColors(
193
+ elementColors: Map<*, *>,
194
+ colorProp: Color?,
195
+ ): androidx.compose.material3.TimePickerColors {
196
+ val defaults = TimePickerDefaults.colors()
197
+ return defaults.copy(
198
+ containerColor = elementColors.color("containerColor") ?: defaults.containerColor,
199
+ clockDialColor = elementColors.color("clockDialColor") ?: colorProp?.copy(alpha = 0.3f) ?: defaults.clockDialColor,
200
+ clockDialSelectedContentColor = elementColors.color("clockDialSelectedContentColor") ?: defaults.clockDialSelectedContentColor,
201
+ clockDialUnselectedContentColor = elementColors.color("clockDialUnselectedContentColor") ?: defaults.clockDialUnselectedContentColor,
202
+ selectorColor = elementColors.color("selectorColor") ?: colorProp ?: defaults.selectorColor,
203
+ periodSelectorBorderColor = elementColors.color("periodSelectorBorderColor") ?: defaults.periodSelectorBorderColor,
204
+ periodSelectorSelectedContainerColor = elementColors.color("periodSelectorSelectedContainerColor") ?: defaults.periodSelectorSelectedContainerColor,
205
+ periodSelectorUnselectedContainerColor = elementColors.color("periodSelectorUnselectedContainerColor") ?: defaults.periodSelectorUnselectedContainerColor,
206
+ periodSelectorSelectedContentColor = elementColors.color("periodSelectorSelectedContentColor") ?: defaults.periodSelectorSelectedContentColor,
207
+ periodSelectorUnselectedContentColor = elementColors.color("periodSelectorUnselectedContentColor") ?: defaults.periodSelectorUnselectedContentColor,
208
+ timeSelectorSelectedContainerColor = elementColors.color("timeSelectorSelectedContainerColor") ?: colorProp ?: defaults.timeSelectorSelectedContainerColor,
209
+ timeSelectorUnselectedContainerColor = elementColors.color("timeSelectorUnselectedContainerColor") ?: defaults.timeSelectorUnselectedContainerColor,
210
+ timeSelectorSelectedContentColor = elementColors.color("timeSelectorSelectedContentColor") ?: defaults.timeSelectorSelectedContentColor,
211
+ timeSelectorUnselectedContentColor = elementColors.color("timeSelectorUnselectedContentColor") ?: defaults.timeSelectorUnselectedContentColor,
212
+ )
213
+ }
214
+
215
+ @OptIn(ExperimentalMaterial3Api::class)
216
+ @Composable
217
+ private fun ExpoDatePicker(modifier: Modifier = Modifier, state: DatePickerUiState, onDateSelected: (Long?) -> Unit) {
218
+ val locale = LocalConfiguration.current.locales[0]
219
+ val displayMode = if (state.variant == "input") DisplayMode.Input else DisplayMode.Picker
220
+ val fallbackDate = remember { Date().time }
221
+ val initialDate = state.initialDate ?: fallbackDate
222
+ val selectableDates = rememberSelectableDates(state.selectableDates)
223
+ val yearRange = rememberDatePickerYearRange(state.selectableDates, initialDate)
224
+
225
+ val pickerState = remember(displayMode, initialDate, selectableDates, yearRange) {
226
+ DatePickerState(
227
+ initialDisplayMode = displayMode,
228
+ locale = locale,
229
+ initialSelectedDateMillis = state.initialDate,
230
+ initialDisplayedMonthMillis = initialDate,
231
+ yearRange = yearRange,
232
+ selectableDates = selectableDates,
233
+ )
234
+ }
235
+
236
+ LaunchedEffect(pickerState.selectedDateMillis) {
237
+ onDateSelected(pickerState.selectedDateMillis)
238
+ }
239
+
240
+ val colors = buildDatePickerColors(state.elementColors, state.color)
241
+
242
+ // Material3's year-selector chevron tints from the ambient
243
+ // LocalContentColor (which defaults to black), not
244
+ // `navigationContentColor`; bind the local so the chevron honors the
245
+ // navigation color.
246
+ CompositionLocalProvider(LocalContentColor provides colors.navigationContentColor) {
247
+ DatePicker(
248
+ modifier = modifier,
249
+ state = pickerState,
250
+ showModeToggle = state.showVariantToggle,
251
+ colors = colors,
252
+ )
253
+ }
254
+ }
255
+
256
+ @OptIn(ExperimentalMaterial3Api::class)
257
+ @Composable
258
+ private fun ExpoTimePicker(modifier: Modifier = Modifier, state: DatePickerUiState, onDateSelected: (Long?) -> Unit) {
259
+ val initialDate = state.initialDate
260
+
261
+ val pickerState = remember(initialDate, state.is24Hour) {
262
+ val cal = Calendar.getInstance()
263
+ cal.isLenient = false
264
+ if (initialDate != null) {
265
+ cal.timeInMillis = initialDate
266
+ }
267
+ TimePickerState(
268
+ initialHour = cal.get(Calendar.HOUR_OF_DAY),
269
+ initialMinute = cal.get(Calendar.MINUTE),
270
+ is24Hour = state.is24Hour,
271
+ )
272
+ }
273
+
274
+ LaunchedEffect(pickerState.hour, pickerState.minute) {
275
+ val cal = Calendar.getInstance()
276
+ cal.isLenient = false
277
+ if (initialDate != null) {
278
+ cal.timeInMillis = initialDate
279
+ }
280
+ cal.set(Calendar.HOUR_OF_DAY, pickerState.hour)
281
+ cal.set(Calendar.MINUTE, pickerState.minute)
282
+ onDateSelected(cal.time.time)
283
+ }
284
+
285
+ TimePicker(
286
+ modifier = modifier,
287
+ state = pickerState,
288
+ layoutType = TimePickerLayoutType.Vertical,
289
+ colors = buildTimePickerColors(state.elementColors, state.color),
290
+ )
291
+ }
292
+
293
+ class XplatDatePickerProvider {
294
+ private var state by mutableStateOf(DatePickerUiState())
295
+ var onEvent: ((Any) -> Unit)? = null
296
+
297
+ fun generateComposeView(view: ComposeView) {
298
+ view.setContent {
299
+ MaterialTheme {
300
+ if (state.displayedComponents == "hourAndMinute") {
301
+ ExpoTimePicker(state = state) { millis ->
302
+ onEvent?.invoke(JSONObject().put("date", millis ?: JSONObject.NULL))
303
+ }
304
+ } else {
305
+ ExpoDatePicker(state = state) { millis ->
306
+ onEvent?.invoke(
307
+ JSONObject().put(
308
+ "date",
309
+ millis?.let { toLocalDayMillis(it) } ?: JSONObject.NULL,
310
+ ),
311
+ )
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+
318
+ fun updateData(input: Map<Any, Any>) {
319
+ val data = input["data"] as? Map<*, *> ?: input
320
+ val range = data["selectableDates"] as? Map<*, *>
321
+ state = DatePickerUiState(
322
+ initialDate = data["initialDate"].asLong(),
323
+ variant = data["variant"] as? String ?: "picker",
324
+ displayedComponents = data["displayedComponents"] as? String ?: "date",
325
+ showVariantToggle = data["showVariantToggle"]?.asBoolean() ?: true,
326
+ is24Hour = data["is24Hour"]?.asBoolean() ?: true,
327
+ color = data["color"].asColor(),
328
+ elementColors = data["elementColors"] as? Map<*, *> ?: emptyMap<Any?, Any?>(),
329
+ selectableDates = SelectableRange(
330
+ start = range?.get("start").asLong(),
331
+ end = range?.get("end").asLong(),
332
+ ),
333
+ )
334
+ }
335
+ }
@@ -0,0 +1,144 @@
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
+ if let title = data["title"] as? String {
120
+ model.title = title.isEmpty ? nil : title
121
+ }
122
+ if let selection = data["selection"] as? NSNumber {
123
+ model.selection = Date(timeIntervalSince1970: selection.doubleValue / 1000)
124
+ }
125
+ if let range = data["range"] as? [String: Any] {
126
+ model.rangeStart = Self.millisToDate(range["start"])
127
+ model.rangeEnd = Self.millisToDate(range["end"])
128
+ }
129
+ if let components = data["displayedComponents"] as? [String] {
130
+ var mapped: DatePicker.Components = []
131
+ if components.contains("date") { mapped.insert(.date) }
132
+ if components.contains("hourAndMinute") { mapped.insert(.hourAndMinute) }
133
+ if !mapped.isEmpty { model.displayedComponents = mapped }
134
+ }
135
+ if let pickerStyle = data["pickerStyle"] as? String {
136
+ model.pickerStyle = pickerStyle
137
+ }
138
+ if let disabled = data["disabled"] as? NSNumber {
139
+ model.disabled = disabled.boolValue
140
+ }
141
+ }
142
+
143
+ public var onEvent: ((NSDictionary) -> Void)?
144
+ }
@@ -0,0 +1,56 @@
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 === 'time' ? 'time' : 'date',
19
+ style: props.pickerStyle,
20
+ selectionMillis: (props.selection ?? props.defaultSelection)?.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
+ handle.current = attach(view, options(), (millis: number) => {
31
+ props.onSelectionChange?.(new Date(millis))
32
+ })
33
+
34
+ return () => {
35
+ handle.current?.detach()
36
+ handle.current = null
37
+ }
38
+ }, [props.components, props.pickerStyle])
39
+
40
+ // Push controlled selection/range updates into the mounted picker; the
41
+ // bridge update is echo-suppressed by comparing against dateValue.
42
+ useEffect(() => {
43
+ handle.current?.update(options())
44
+ })
45
+
46
+ <flexboxlayout
47
+ ref={(v: any) => { anchor.current = v }}
48
+ id={props.id}
49
+ className={props.className}
50
+ style={{
51
+ minWidth: 240,
52
+ minHeight: props.pickerStyle === 'graphical' ? 300 : 26,
53
+ ...props.style,
54
+ }}
55
+ />;
56
+ }
@@ -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
+ }
@@ -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,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
package/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export {}
package/index.js DELETED
@@ -1,2 +0,0 @@
1
- // Reservation stub; implementation will arrive in a later release.
2
- module.exports = {}